diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..157acb0 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,10 @@ + +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org). + +## [v0.0.1](https://github.com/puppetlabs/puppet-lint-check_unsafe_interpolations/tree/v0.0.1) - 2022-12-01 + +[Full Changelog](https://github.com/puppetlabs/puppet-lint-check_unsafe_interpolations/compare/6fdffece89c70b016174b766d57ecf22064b20d2...v0.0.1) diff --git a/vendor/bundle/ruby/2.7.0/bin/build b/vendor/bundle/ruby/2.7.0/bin/build new file mode 100755 index 0000000..f5ad125 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/bin/build @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +# +# This file was generated by RubyGems. +# +# The application 'rspec-json_expectations' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +version = ">= 0.a" + +str = ARGV.first +if str + str = str.b[/\A_(.*)_\z/, 1] + if str and Gem::Version.correct?(str) + version = str + ARGV.shift + end +end + +if Gem.respond_to?(:activate_bin_path) +load Gem.activate_bin_path('rspec-json_expectations', 'build', version) +else +gem "rspec-json_expectations", version +load Gem.bin_path("rspec-json_expectations", "build", version) +end diff --git a/vendor/bundle/ruby/2.7.0/bin/byebug b/vendor/bundle/ruby/2.7.0/bin/byebug new file mode 100755 index 0000000..ae022df --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/bin/byebug @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +# +# This file was generated by RubyGems. +# +# The application 'byebug' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +version = ">= 0.a" + +str = ARGV.first +if str + str = str.b[/\A_(.*)_\z/, 1] + if str and Gem::Version.correct?(str) + version = str + ARGV.shift + end +end + +if Gem.respond_to?(:activate_bin_path) +load Gem.activate_bin_path('byebug', 'byebug', version) +else +gem "byebug", version +load Gem.bin_path("byebug", "byebug", version) +end diff --git a/vendor/bundle/ruby/2.7.0/bin/coderay b/vendor/bundle/ruby/2.7.0/bin/coderay new file mode 100755 index 0000000..f662022 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/bin/coderay @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +# +# This file was generated by RubyGems. +# +# The application 'coderay' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +version = ">= 0.a" + +str = ARGV.first +if str + str = str.b[/\A_(.*)_\z/, 1] + if str and Gem::Version.correct?(str) + version = str + ARGV.shift + end +end + +if Gem.respond_to?(:activate_bin_path) +load Gem.activate_bin_path('coderay', 'coderay', version) +else +gem "coderay", version +load Gem.bin_path("coderay", "coderay", version) +end diff --git a/vendor/bundle/ruby/2.7.0/bin/git-generate-changelog b/vendor/bundle/ruby/2.7.0/bin/git-generate-changelog new file mode 100755 index 0000000..06450c2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/bin/git-generate-changelog @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +# +# This file was generated by RubyGems. +# +# The application 'github_changelog_generator' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +version = ">= 0.a" + +str = ARGV.first +if str + str = str.b[/\A_(.*)_\z/, 1] + if str and Gem::Version.correct?(str) + version = str + ARGV.shift + end +end + +if Gem.respond_to?(:activate_bin_path) +load Gem.activate_bin_path('github_changelog_generator', 'git-generate-changelog', version) +else +gem "github_changelog_generator", version +load Gem.bin_path("github_changelog_generator", "git-generate-changelog", version) +end diff --git a/vendor/bundle/ruby/2.7.0/bin/github_changelog_generator b/vendor/bundle/ruby/2.7.0/bin/github_changelog_generator new file mode 100755 index 0000000..689156b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/bin/github_changelog_generator @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +# +# This file was generated by RubyGems. +# +# The application 'github_changelog_generator' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +version = ">= 0.a" + +str = ARGV.first +if str + str = str.b[/\A_(.*)_\z/, 1] + if str and Gem::Version.correct?(str) + version = str + ARGV.shift + end +end + +if Gem.respond_to?(:activate_bin_path) +load Gem.activate_bin_path('github_changelog_generator', 'github_changelog_generator', version) +else +gem "github_changelog_generator", version +load Gem.bin_path("github_changelog_generator", "github_changelog_generator", version) +end diff --git a/vendor/bundle/ruby/2.7.0/bin/htmldiff b/vendor/bundle/ruby/2.7.0/bin/htmldiff new file mode 100755 index 0000000..cf715e6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/bin/htmldiff @@ -0,0 +1,29 @@ +#!/bin/sh +'exec' "ruby" '-x' "$0" "$@" +#!/opt/hostedtoolcache/Ruby/2.7.7/x64/bin/ruby +# +# This file was generated by RubyGems. +# +# The application 'diff-lcs' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +version = ">= 0.a" + +str = ARGV.first +if str + str = str.b[/\A_(.*)_\z/, 1] + if str and Gem::Version.correct?(str) + version = str + ARGV.shift + end +end + +if Gem.respond_to?(:activate_bin_path) +load Gem.activate_bin_path('diff-lcs', 'htmldiff', version) +else +gem "diff-lcs", version +load Gem.bin_path("diff-lcs", "htmldiff", version) +end diff --git a/vendor/bundle/ruby/2.7.0/bin/ldiff b/vendor/bundle/ruby/2.7.0/bin/ldiff new file mode 100755 index 0000000..c5f7caa --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/bin/ldiff @@ -0,0 +1,29 @@ +#!/bin/sh +'exec' "ruby" '-x' "$0" "$@" +#!/opt/hostedtoolcache/Ruby/2.7.7/x64/bin/ruby +# +# This file was generated by RubyGems. +# +# The application 'diff-lcs' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +version = ">= 0.a" + +str = ARGV.first +if str + str = str.b[/\A_(.*)_\z/, 1] + if str and Gem::Version.correct?(str) + version = str + ARGV.shift + end +end + +if Gem.respond_to?(:activate_bin_path) +load Gem.activate_bin_path('diff-lcs', 'ldiff', version) +else +gem "diff-lcs", version +load Gem.bin_path("diff-lcs", "ldiff", version) +end diff --git a/vendor/bundle/ruby/2.7.0/bin/pry b/vendor/bundle/ruby/2.7.0/bin/pry new file mode 100755 index 0000000..d9566e4 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/bin/pry @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +# +# This file was generated by RubyGems. +# +# The application 'pry' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +version = ">= 0.a" + +str = ARGV.first +if str + str = str.b[/\A_(.*)_\z/, 1] + if str and Gem::Version.correct?(str) + version = str + ARGV.shift + end +end + +if Gem.respond_to?(:activate_bin_path) +load Gem.activate_bin_path('pry', 'pry', version) +else +gem "pry", version +load Gem.bin_path("pry", "pry", version) +end diff --git a/vendor/bundle/ruby/2.7.0/bin/puppet-lint b/vendor/bundle/ruby/2.7.0/bin/puppet-lint new file mode 100755 index 0000000..9f5c37a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/bin/puppet-lint @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +# +# This file was generated by RubyGems. +# +# The application 'puppet-lint' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +version = ">= 0.a" + +str = ARGV.first +if str + str = str.b[/\A_(.*)_\z/, 1] + if str and Gem::Version.correct?(str) + version = str + ARGV.shift + end +end + +if Gem.respond_to?(:activate_bin_path) +load Gem.activate_bin_path('puppet-lint', 'puppet-lint', version) +else +gem "puppet-lint", version +load Gem.bin_path("puppet-lint", "puppet-lint", version) +end diff --git a/vendor/bundle/ruby/2.7.0/bin/rake b/vendor/bundle/ruby/2.7.0/bin/rake new file mode 100755 index 0000000..f24ffd8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/bin/rake @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +# +# This file was generated by RubyGems. +# +# The application 'rake' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +version = ">= 0.a" + +str = ARGV.first +if str + str = str.b[/\A_(.*)_\z/, 1] + if str and Gem::Version.correct?(str) + version = str + ARGV.shift + end +end + +if Gem.respond_to?(:activate_bin_path) +load Gem.activate_bin_path('rake', 'rake', version) +else +gem "rake", version +load Gem.bin_path("rake", "rake", version) +end diff --git a/vendor/bundle/ruby/2.7.0/bin/rspec b/vendor/bundle/ruby/2.7.0/bin/rspec new file mode 100755 index 0000000..b1d4adf --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/bin/rspec @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +# +# This file was generated by RubyGems. +# +# The application 'rspec-core' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +version = ">= 0.a" + +str = ARGV.first +if str + str = str.b[/\A_(.*)_\z/, 1] + if str and Gem::Version.correct?(str) + version = str + ARGV.shift + end +end + +if Gem.respond_to?(:activate_bin_path) +load Gem.activate_bin_path('rspec-core', 'rspec', version) +else +gem "rspec-core", version +load Gem.bin_path("rspec-core", "rspec", version) +end diff --git a/vendor/bundle/ruby/2.7.0/bin/rubocop b/vendor/bundle/ruby/2.7.0/bin/rubocop new file mode 100755 index 0000000..0964268 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/bin/rubocop @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +# +# This file was generated by RubyGems. +# +# The application 'rubocop' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +version = ">= 0.a" + +str = ARGV.first +if str + str = str.b[/\A_(.*)_\z/, 1] + if str and Gem::Version.correct?(str) + version = str + ARGV.shift + end +end + +if Gem.respond_to?(:activate_bin_path) +load Gem.activate_bin_path('rubocop', 'rubocop', version) +else +gem "rubocop", version +load Gem.bin_path("rubocop", "rubocop", version) +end diff --git a/vendor/bundle/ruby/2.7.0/bin/ruby-parse b/vendor/bundle/ruby/2.7.0/bin/ruby-parse new file mode 100755 index 0000000..80903e6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/bin/ruby-parse @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +# +# This file was generated by RubyGems. +# +# The application 'parser' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +version = ">= 0.a" + +str = ARGV.first +if str + str = str.b[/\A_(.*)_\z/, 1] + if str and Gem::Version.correct?(str) + version = str + ARGV.shift + end +end + +if Gem.respond_to?(:activate_bin_path) +load Gem.activate_bin_path('parser', 'ruby-parse', version) +else +gem "parser", version +load Gem.bin_path("parser", "ruby-parse", version) +end diff --git a/vendor/bundle/ruby/2.7.0/bin/ruby-rewrite b/vendor/bundle/ruby/2.7.0/bin/ruby-rewrite new file mode 100755 index 0000000..096397c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/bin/ruby-rewrite @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +# +# This file was generated by RubyGems. +# +# The application 'parser' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +version = ">= 0.a" + +str = ARGV.first +if str + str = str.b[/\A_(.*)_\z/, 1] + if str and Gem::Version.correct?(str) + version = str + ARGV.shift + end +end + +if Gem.respond_to?(:activate_bin_path) +load Gem.activate_bin_path('parser', 'ruby-rewrite', version) +else +gem "parser", version +load Gem.bin_path("parser", "ruby-rewrite", version) +end diff --git a/vendor/bundle/ruby/2.7.0/bin/with-rspec-2 b/vendor/bundle/ruby/2.7.0/bin/with-rspec-2 new file mode 100755 index 0000000..e9ee7f2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/bin/with-rspec-2 @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +# +# This file was generated by RubyGems. +# +# The application 'rspec-json_expectations' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +version = ">= 0.a" + +str = ARGV.first +if str + str = str.b[/\A_(.*)_\z/, 1] + if str and Gem::Version.correct?(str) + version = str + ARGV.shift + end +end + +if Gem.respond_to?(:activate_bin_path) +load Gem.activate_bin_path('rspec-json_expectations', 'with-rspec-2', version) +else +gem "rspec-json_expectations", version +load Gem.bin_path("rspec-json_expectations", "with-rspec-2", version) +end diff --git a/vendor/bundle/ruby/2.7.0/bin/with-rspec-3 b/vendor/bundle/ruby/2.7.0/bin/with-rspec-3 new file mode 100755 index 0000000..8183dd6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/bin/with-rspec-3 @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +# +# This file was generated by RubyGems. +# +# The application 'rspec-json_expectations' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +version = ">= 0.a" + +str = ARGV.first +if str + str = str.b[/\A_(.*)_\z/, 1] + if str and Gem::Version.correct?(str) + version = str + ARGV.shift + end +end + +if Gem.respond_to?(:activate_bin_path) +load Gem.activate_bin_path('rspec-json_expectations', 'with-rspec-3', version) +else +gem "rspec-json_expectations", version +load Gem.bin_path("rspec-json_expectations", "with-rspec-3", version) +end diff --git a/vendor/bundle/ruby/2.7.0/cache/activesupport-7.0.4.gem b/vendor/bundle/ruby/2.7.0/cache/activesupport-7.0.4.gem new file mode 100644 index 0000000..3c823a0 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/activesupport-7.0.4.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/addressable-2.8.1.gem b/vendor/bundle/ruby/2.7.0/cache/addressable-2.8.1.gem new file mode 100644 index 0000000..17e4257 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/addressable-2.8.1.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/ast-2.4.2.gem b/vendor/bundle/ruby/2.7.0/cache/ast-2.4.2.gem new file mode 100644 index 0000000..abe1643 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/ast-2.4.2.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/async-1.30.3.gem b/vendor/bundle/ruby/2.7.0/cache/async-1.30.3.gem new file mode 100644 index 0000000..fe8c889 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/async-1.30.3.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/async-http-0.59.3.gem b/vendor/bundle/ruby/2.7.0/cache/async-http-0.59.3.gem new file mode 100644 index 0000000..6117b69 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/async-http-0.59.3.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/async-http-faraday-0.11.0.gem b/vendor/bundle/ruby/2.7.0/cache/async-http-faraday-0.11.0.gem new file mode 100644 index 0000000..032083a Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/async-http-faraday-0.11.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/async-io-1.34.0.gem b/vendor/bundle/ruby/2.7.0/cache/async-io-1.34.0.gem new file mode 100644 index 0000000..739deea Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/async-io-1.34.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/async-pool-0.3.12.gem b/vendor/bundle/ruby/2.7.0/cache/async-pool-0.3.12.gem new file mode 100644 index 0000000..d331d80 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/async-pool-0.3.12.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/binding_of_caller-1.0.0.gem b/vendor/bundle/ruby/2.7.0/cache/binding_of_caller-1.0.0.gem new file mode 100644 index 0000000..81be9b1 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/binding_of_caller-1.0.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/byebug-11.1.3.gem b/vendor/bundle/ruby/2.7.0/cache/byebug-11.1.3.gem new file mode 100644 index 0000000..4321f4b Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/byebug-11.1.3.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/coderay-1.1.3.gem b/vendor/bundle/ruby/2.7.0/cache/coderay-1.1.3.gem new file mode 100644 index 0000000..3475820 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/coderay-1.1.3.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/concurrent-ruby-1.1.10.gem b/vendor/bundle/ruby/2.7.0/cache/concurrent-ruby-1.1.10.gem new file mode 100644 index 0000000..c796701 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/concurrent-ruby-1.1.10.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/console-1.16.2.gem b/vendor/bundle/ruby/2.7.0/cache/console-1.16.2.gem new file mode 100644 index 0000000..984b7a8 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/console-1.16.2.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/debug_inspector-1.1.0.gem b/vendor/bundle/ruby/2.7.0/cache/debug_inspector-1.1.0.gem new file mode 100644 index 0000000..c16993e Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/debug_inspector-1.1.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/diff-lcs-1.5.0.gem b/vendor/bundle/ruby/2.7.0/cache/diff-lcs-1.5.0.gem new file mode 100644 index 0000000..3a25852 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/diff-lcs-1.5.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/faraday-2.7.1.gem b/vendor/bundle/ruby/2.7.0/cache/faraday-2.7.1.gem new file mode 100644 index 0000000..bafbdd9 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/faraday-2.7.1.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/faraday-http-cache-2.4.1.gem b/vendor/bundle/ruby/2.7.0/cache/faraday-http-cache-2.4.1.gem new file mode 100644 index 0000000..ac55317 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/faraday-http-cache-2.4.1.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/faraday-net_http-3.0.2.gem b/vendor/bundle/ruby/2.7.0/cache/faraday-net_http-3.0.2.gem new file mode 100644 index 0000000..f6b5175 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/faraday-net_http-3.0.2.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/faraday-retry-2.0.0.gem b/vendor/bundle/ruby/2.7.0/cache/faraday-retry-2.0.0.gem new file mode 100644 index 0000000..427363a Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/faraday-retry-2.0.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/fiber-local-1.0.0.gem b/vendor/bundle/ruby/2.7.0/cache/fiber-local-1.0.0.gem new file mode 100644 index 0000000..6832342 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/fiber-local-1.0.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/github_changelog_generator-1.16.4.gem b/vendor/bundle/ruby/2.7.0/cache/github_changelog_generator-1.16.4.gem new file mode 100644 index 0000000..fe36b7b Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/github_changelog_generator-1.16.4.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/i18n-1.12.0.gem b/vendor/bundle/ruby/2.7.0/cache/i18n-1.12.0.gem new file mode 100644 index 0000000..c64c068 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/i18n-1.12.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/json-2.6.2.gem b/vendor/bundle/ruby/2.7.0/cache/json-2.6.2.gem new file mode 100644 index 0000000..dfa9204 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/json-2.6.2.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/method_source-1.0.0.gem b/vendor/bundle/ruby/2.7.0/cache/method_source-1.0.0.gem new file mode 100644 index 0000000..2e035c3 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/method_source-1.0.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/minitest-5.16.3.gem b/vendor/bundle/ruby/2.7.0/cache/minitest-5.16.3.gem new file mode 100644 index 0000000..ebdb92e Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/minitest-5.16.3.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/multi_json-1.15.0.gem b/vendor/bundle/ruby/2.7.0/cache/multi_json-1.15.0.gem new file mode 100644 index 0000000..8348d7b Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/multi_json-1.15.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/nio4r-2.5.8.gem b/vendor/bundle/ruby/2.7.0/cache/nio4r-2.5.8.gem new file mode 100644 index 0000000..f116b6f Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/nio4r-2.5.8.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/octokit-4.25.1.gem b/vendor/bundle/ruby/2.7.0/cache/octokit-4.25.1.gem new file mode 100644 index 0000000..6f25e59 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/octokit-4.25.1.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/parallel-1.22.1.gem b/vendor/bundle/ruby/2.7.0/cache/parallel-1.22.1.gem new file mode 100644 index 0000000..5208c79 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/parallel-1.22.1.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/parser-3.1.3.0.gem b/vendor/bundle/ruby/2.7.0/cache/parser-3.1.3.0.gem new file mode 100644 index 0000000..26ea6f0 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/parser-3.1.3.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/protocol-hpack-1.4.2.gem b/vendor/bundle/ruby/2.7.0/cache/protocol-hpack-1.4.2.gem new file mode 100644 index 0000000..b659b09 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/protocol-hpack-1.4.2.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/protocol-http-0.23.12.gem b/vendor/bundle/ruby/2.7.0/cache/protocol-http-0.23.12.gem new file mode 100644 index 0000000..275d6f6 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/protocol-http-0.23.12.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/protocol-http1-0.14.6.gem b/vendor/bundle/ruby/2.7.0/cache/protocol-http1-0.14.6.gem new file mode 100644 index 0000000..3618b84 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/protocol-http1-0.14.6.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/protocol-http2-0.14.2.gem b/vendor/bundle/ruby/2.7.0/cache/protocol-http2-0.14.2.gem new file mode 100644 index 0000000..348ef17 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/protocol-http2-0.14.2.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/pry-0.14.1.gem b/vendor/bundle/ruby/2.7.0/cache/pry-0.14.1.gem new file mode 100644 index 0000000..46b1b66 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/pry-0.14.1.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/pry-byebug-3.10.1.gem b/vendor/bundle/ruby/2.7.0/cache/pry-byebug-3.10.1.gem new file mode 100644 index 0000000..f0a1440 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/pry-byebug-3.10.1.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/pry-stack_explorer-0.6.1.gem b/vendor/bundle/ruby/2.7.0/cache/pry-stack_explorer-0.6.1.gem new file mode 100644 index 0000000..23820b4 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/pry-stack_explorer-0.6.1.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/public_suffix-5.0.0.gem b/vendor/bundle/ruby/2.7.0/cache/public_suffix-5.0.0.gem new file mode 100644 index 0000000..6b6ed52 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/public_suffix-5.0.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/puppet-lint-3.0.1.gem b/vendor/bundle/ruby/2.7.0/cache/puppet-lint-3.0.1.gem new file mode 100644 index 0000000..f6462dc Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/puppet-lint-3.0.1.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/rainbow-3.1.1.gem b/vendor/bundle/ruby/2.7.0/cache/rainbow-3.1.1.gem new file mode 100644 index 0000000..863181a Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/rainbow-3.1.1.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/rake-10.5.0.gem b/vendor/bundle/ruby/2.7.0/cache/rake-10.5.0.gem new file mode 100644 index 0000000..0fe2757 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/rake-10.5.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/regexp_parser-2.6.1.gem b/vendor/bundle/ruby/2.7.0/cache/regexp_parser-2.6.1.gem new file mode 100644 index 0000000..efec49c Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/regexp_parser-2.6.1.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/rexml-3.2.5.gem b/vendor/bundle/ruby/2.7.0/cache/rexml-3.2.5.gem new file mode 100644 index 0000000..5680fec Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/rexml-3.2.5.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/rspec-3.12.0.gem b/vendor/bundle/ruby/2.7.0/cache/rspec-3.12.0.gem new file mode 100644 index 0000000..a28e4c3 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/rspec-3.12.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/rspec-collection_matchers-1.2.0.gem b/vendor/bundle/ruby/2.7.0/cache/rspec-collection_matchers-1.2.0.gem new file mode 100644 index 0000000..14ae008 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/rspec-collection_matchers-1.2.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/rspec-core-3.12.0.gem b/vendor/bundle/ruby/2.7.0/cache/rspec-core-3.12.0.gem new file mode 100644 index 0000000..f774c2c Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/rspec-core-3.12.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/rspec-expectations-3.12.0.gem b/vendor/bundle/ruby/2.7.0/cache/rspec-expectations-3.12.0.gem new file mode 100644 index 0000000..aec6079 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/rspec-expectations-3.12.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/rspec-its-1.3.0.gem b/vendor/bundle/ruby/2.7.0/cache/rspec-its-1.3.0.gem new file mode 100644 index 0000000..bcff4af Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/rspec-its-1.3.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/rspec-json_expectations-1.4.0.gem b/vendor/bundle/ruby/2.7.0/cache/rspec-json_expectations-1.4.0.gem new file mode 100644 index 0000000..a9fa6c8 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/rspec-json_expectations-1.4.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/rspec-mocks-3.12.0.gem b/vendor/bundle/ruby/2.7.0/cache/rspec-mocks-3.12.0.gem new file mode 100644 index 0000000..04ec3f8 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/rspec-mocks-3.12.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/rspec-support-3.12.0.gem b/vendor/bundle/ruby/2.7.0/cache/rspec-support-3.12.0.gem new file mode 100644 index 0000000..1902af6 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/rspec-support-3.12.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/rubocop-1.6.1.gem b/vendor/bundle/ruby/2.7.0/cache/rubocop-1.6.1.gem new file mode 100644 index 0000000..6190963 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/rubocop-1.6.1.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/rubocop-ast-1.24.0.gem b/vendor/bundle/ruby/2.7.0/cache/rubocop-ast-1.24.0.gem new file mode 100644 index 0000000..04230d1 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/rubocop-ast-1.24.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/rubocop-performance-1.9.2.gem b/vendor/bundle/ruby/2.7.0/cache/rubocop-performance-1.9.2.gem new file mode 100644 index 0000000..a60571b Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/rubocop-performance-1.9.2.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/rubocop-rspec-2.0.1.gem b/vendor/bundle/ruby/2.7.0/cache/rubocop-rspec-2.0.1.gem new file mode 100644 index 0000000..97f0519 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/rubocop-rspec-2.0.1.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/ruby-progressbar-1.11.0.gem b/vendor/bundle/ruby/2.7.0/cache/ruby-progressbar-1.11.0.gem new file mode 100644 index 0000000..a9d84e5 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/ruby-progressbar-1.11.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/ruby2_keywords-0.0.5.gem b/vendor/bundle/ruby/2.7.0/cache/ruby2_keywords-0.0.5.gem new file mode 100644 index 0000000..d311c5d Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/ruby2_keywords-0.0.5.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/sawyer-0.9.2.gem b/vendor/bundle/ruby/2.7.0/cache/sawyer-0.9.2.gem new file mode 100644 index 0000000..5213eb8 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/sawyer-0.9.2.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/timers-4.3.5.gem b/vendor/bundle/ruby/2.7.0/cache/timers-4.3.5.gem new file mode 100644 index 0000000..4214cd5 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/timers-4.3.5.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/traces-0.8.0.gem b/vendor/bundle/ruby/2.7.0/cache/traces-0.8.0.gem new file mode 100644 index 0000000..9cef2ec Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/traces-0.8.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/tzinfo-2.0.5.gem b/vendor/bundle/ruby/2.7.0/cache/tzinfo-2.0.5.gem new file mode 100644 index 0000000..1b28f07 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/tzinfo-2.0.5.gem differ diff --git a/vendor/bundle/ruby/2.7.0/cache/unicode-display_width-1.8.0.gem b/vendor/bundle/ruby/2.7.0/cache/unicode-display_width-1.8.0.gem new file mode 100644 index 0000000..75166d4 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/cache/unicode-display_width-1.8.0.gem differ diff --git a/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/byebug-11.1.3/byebug/byebug.so b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/byebug-11.1.3/byebug/byebug.so new file mode 100755 index 0000000..86e4bde Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/byebug-11.1.3/byebug/byebug.so differ diff --git a/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/byebug-11.1.3/gem.build_complete b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/byebug-11.1.3/gem.build_complete new file mode 100644 index 0000000..e69de29 diff --git a/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/byebug-11.1.3/gem_make.out b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/byebug-11.1.3/gem_make.out new file mode 100644 index 0000000..b875fe6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/byebug-11.1.3/gem_make.out @@ -0,0 +1,19 @@ +current directory: /home/runner/work/puppet-lint-check_unsafe_interpolations/puppet-lint-check_unsafe_interpolations/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug +/opt/hostedtoolcache/Ruby/2.7.7/x64/bin/ruby -I /opt/hostedtoolcache/Ruby/2.7.7/x64/lib/ruby/2.7.0 -r ./siteconf20221126-1610-1qj3jx5.rb extconf.rb +creating Makefile + +current directory: /home/runner/work/puppet-lint-check_unsafe_interpolations/puppet-lint-check_unsafe_interpolations/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug +make "DESTDIR=" clean + +current directory: /home/runner/work/puppet-lint-check_unsafe_interpolations/puppet-lint-check_unsafe_interpolations/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug +make "DESTDIR=" +compiling breakpoint.c +compiling byebug.c +compiling context.c +compiling locker.c +compiling threads.c +linking shared-object byebug/byebug.so + +current directory: /home/runner/work/puppet-lint-check_unsafe_interpolations/puppet-lint-check_unsafe_interpolations/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug +make "DESTDIR=" install +/usr/bin/install -c -m 0755 byebug.so ./.gem.20221126-1610-1v6fje/byebug diff --git a/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/debug_inspector-1.1.0/debug_inspector.so b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/debug_inspector-1.1.0/debug_inspector.so new file mode 100755 index 0000000..b6603ee Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/debug_inspector-1.1.0/debug_inspector.so differ diff --git a/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/debug_inspector-1.1.0/gem.build_complete b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/debug_inspector-1.1.0/gem.build_complete new file mode 100644 index 0000000..e69de29 diff --git a/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/debug_inspector-1.1.0/gem_make.out b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/debug_inspector-1.1.0/gem_make.out new file mode 100644 index 0000000..67bc627 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/debug_inspector-1.1.0/gem_make.out @@ -0,0 +1,15 @@ +current directory: /home/runner/work/puppet-lint-check_unsafe_interpolations/puppet-lint-check_unsafe_interpolations/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/ext/debug_inspector +/opt/hostedtoolcache/Ruby/2.7.7/x64/bin/ruby -I /opt/hostedtoolcache/Ruby/2.7.7/x64/lib/ruby/2.7.0 -r ./siteconf20221126-1610-g4u2az.rb extconf.rb +creating Makefile + +current directory: /home/runner/work/puppet-lint-check_unsafe_interpolations/puppet-lint-check_unsafe_interpolations/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/ext/debug_inspector +make "DESTDIR=" clean + +current directory: /home/runner/work/puppet-lint-check_unsafe_interpolations/puppet-lint-check_unsafe_interpolations/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/ext/debug_inspector +make "DESTDIR=" +compiling debug_inspector.c +linking shared-object debug_inspector.so + +current directory: /home/runner/work/puppet-lint-check_unsafe_interpolations/puppet-lint-check_unsafe_interpolations/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/ext/debug_inspector +make "DESTDIR=" install +/usr/bin/install -c -m 0755 debug_inspector.so ./.gem.20221126-1610-1463i5z diff --git a/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/json-2.6.2/gem.build_complete b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/json-2.6.2/gem.build_complete new file mode 100644 index 0000000..e69de29 diff --git a/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/json-2.6.2/gem_make.out b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/json-2.6.2/gem_make.out new file mode 100644 index 0000000..c71bea9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/json-2.6.2/gem_make.out @@ -0,0 +1,13 @@ +current directory: /home/runner/work/puppet-lint-check_unsafe_interpolations/puppet-lint-check_unsafe_interpolations/vendor/bundle/ruby/2.7.0/gems/json-2.6.2/ext/json +/opt/hostedtoolcache/Ruby/2.7.7/x64/bin/ruby -I /opt/hostedtoolcache/Ruby/2.7.7/x64/lib/ruby/2.7.0 -r ./siteconf20221126-1610-itgs4s.rb extconf.rb +creating Makefile + +current directory: /home/runner/work/puppet-lint-check_unsafe_interpolations/puppet-lint-check_unsafe_interpolations/vendor/bundle/ruby/2.7.0/gems/json-2.6.2/ext/json +make "DESTDIR=" clean + +current directory: /home/runner/work/puppet-lint-check_unsafe_interpolations/puppet-lint-check_unsafe_interpolations/vendor/bundle/ruby/2.7.0/gems/json-2.6.2/ext/json +make "DESTDIR=" +make: Nothing to be done for 'all'. + +current directory: /home/runner/work/puppet-lint-check_unsafe_interpolations/puppet-lint-check_unsafe_interpolations/vendor/bundle/ruby/2.7.0/gems/json-2.6.2/ext/json +make "DESTDIR=" install diff --git a/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/json-2.6.2/json/ext/generator.so b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/json-2.6.2/json/ext/generator.so new file mode 100755 index 0000000..f968669 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/json-2.6.2/json/ext/generator.so differ diff --git a/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/json-2.6.2/json/ext/parser.so b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/json-2.6.2/json/ext/parser.so new file mode 100755 index 0000000..95391e3 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/json-2.6.2/json/ext/parser.so differ diff --git a/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/json-2.6.2/mkmf.log b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/json-2.6.2/mkmf.log new file mode 100644 index 0000000..d0b4aee --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/json-2.6.2/mkmf.log @@ -0,0 +1,120 @@ +have_func: checking for rb_enc_raise() in ruby.h... -------------------- yes + +"gcc -o conftest -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/ruby/backward -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0 -I. -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include -DENABLE_PATH_CHECK=0 -g -O2 -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -L. -L/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,-rpath,/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -L/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -lruby -lm -lc" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: int main(int argc, char **argv) +4: { +5: return !!argv[argc]; +6: } +/* end */ + +"gcc -o conftest -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/ruby/backward -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0 -I. -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include -DENABLE_PATH_CHECK=0 -g -O2 -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -L. -L/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,-rpath,/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -L/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -lruby -lm -lc" +conftest.c: In function ‘t’: +conftest.c:16:57: error: ‘rb_enc_raise’ undeclared (first use in this function); did you mean ‘rb_exc_raise’? + 16 | int t(void) { void ((*volatile p)()); p = (void ((*)()))rb_enc_raise; return !p; } + | ^~~~~~~~~~~~ + | rb_exc_raise +conftest.c:16:57: note: each undeclared identifier is reported only once for each function it appears in +checked program was: +/* begin */ + 1: #include "ruby.h" + 2: + 3: #include + 4: + 5: /*top*/ + 6: extern int t(void); + 7: int main(int argc, char **argv) + 8: { + 9: if (argc > 1000000) { +10: int (* volatile tp)(void)=(int (*)(void))&t; +11: printf("%d", (*tp)()); +12: } +13: +14: return !!argv[argc]; +15: } +16: int t(void) { void ((*volatile p)()); p = (void ((*)()))rb_enc_raise; return !p; } +/* end */ + +"gcc -o conftest -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/ruby/backward -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0 -I. -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include -DENABLE_PATH_CHECK=0 -g -O2 -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -L. -L/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,-rpath,/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -L/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -lruby -lm -lc" +checked program was: +/* begin */ + 1: #include "ruby.h" + 2: + 3: #include + 4: + 5: /*top*/ + 6: extern int t(void); + 7: int main(int argc, char **argv) + 8: { + 9: if (argc > 1000000) { +10: int (* volatile tp)(void)=(int (*)(void))&t; +11: printf("%d", (*tp)()); +12: } +13: +14: return !!argv[argc]; +15: } +16: extern void rb_enc_raise(); +17: int t(void) { rb_enc_raise(); return 0; } +/* end */ + +-------------------- + +have_func: checking for rb_enc_interned_str() in ruby.h... -------------------- no + +"gcc -o conftest -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/ruby/backward -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0 -I. -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include -DENABLE_PATH_CHECK=0 -g -O2 -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -L. -L/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,-rpath,/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -L/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -lruby -lm -lc" +conftest.c: In function ‘t’: +conftest.c:16:57: error: ‘rb_enc_interned_str’ undeclared (first use in this function); did you mean ‘rb_sym_interned_p’? + 16 | int t(void) { void ((*volatile p)()); p = (void ((*)()))rb_enc_interned_str; return !p; } + | ^~~~~~~~~~~~~~~~~~~ + | rb_sym_interned_p +conftest.c:16:57: note: each undeclared identifier is reported only once for each function it appears in +checked program was: +/* begin */ + 1: #include "ruby.h" + 2: + 3: #include + 4: + 5: /*top*/ + 6: extern int t(void); + 7: int main(int argc, char **argv) + 8: { + 9: if (argc > 1000000) { +10: int (* volatile tp)(void)=(int (*)(void))&t; +11: printf("%d", (*tp)()); +12: } +13: +14: return !!argv[argc]; +15: } +16: int t(void) { void ((*volatile p)()); p = (void ((*)()))rb_enc_interned_str; return !p; } +/* end */ + +"gcc -o conftest -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/ruby/backward -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0 -I. -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include -DENABLE_PATH_CHECK=0 -g -O2 -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -L. -L/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,-rpath,/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -L/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -lruby -lm -lc" +/usr/bin/ld: /tmp/cc67GYkH.o: in function `t': +/home/runner/work/puppet-lint-check_unsafe_interpolations/puppet-lint-check_unsafe_interpolations/vendor/bundle/ruby/2.7.0/gems/json-2.6.2/ext/json/ext/parser/conftest.c:17: undefined reference to `rb_enc_interned_str' +collect2: error: ld returned 1 exit status +checked program was: +/* begin */ + 1: #include "ruby.h" + 2: + 3: #include + 4: + 5: /*top*/ + 6: extern int t(void); + 7: int main(int argc, char **argv) + 8: { + 9: if (argc > 1000000) { +10: int (* volatile tp)(void)=(int (*)(void))&t; +11: printf("%d", (*tp)()); +12: } +13: +14: return !!argv[argc]; +15: } +16: extern void rb_enc_interned_str(); +17: int t(void) { rb_enc_interned_str(); return 0; } +/* end */ + +-------------------- + diff --git a/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/nio4r-2.5.8/gem.build_complete b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/nio4r-2.5.8/gem.build_complete new file mode 100644 index 0000000..e69de29 diff --git a/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/nio4r-2.5.8/gem_make.out b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/nio4r-2.5.8/gem_make.out new file mode 100644 index 0000000..9f44c04 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/nio4r-2.5.8/gem_make.out @@ -0,0 +1,61 @@ +current directory: /home/runner/work/puppet-lint-check_unsafe_interpolations/puppet-lint-check_unsafe_interpolations/vendor/bundle/ruby/2.7.0/gems/nio4r-2.5.8/ext/nio4r +/opt/hostedtoolcache/Ruby/2.7.7/x64/bin/ruby -I /opt/hostedtoolcache/Ruby/2.7.7/x64/lib/ruby/2.7.0 -r ./siteconf20221126-1610-pz8dhk.rb extconf.rb +checking for unistd.h... yes +checking for linux/aio_abi.h... yes +checking for linux/io_uring.h... yes +checking for sys/select.h... yes +checking for port_event_t in poll.h... no +checking for sys/epoll.h... yes +checking for sys/event.h... no +checking for port_event_t in port.h... no +checking for sys/resource.h... yes +creating Makefile + +current directory: /home/runner/work/puppet-lint-check_unsafe_interpolations/puppet-lint-check_unsafe_interpolations/vendor/bundle/ruby/2.7.0/gems/nio4r-2.5.8/ext/nio4r +make "DESTDIR=" clean + +current directory: /home/runner/work/puppet-lint-check_unsafe_interpolations/puppet-lint-check_unsafe_interpolations/vendor/bundle/ruby/2.7.0/gems/nio4r-2.5.8/ext/nio4r +make "DESTDIR=" +compiling bytebuffer.c +compiling monitor.c +compiling nio4r_ext.c +In file included from nio4r_ext.c:6: +../libev/ev.c:2136:31: warning: ‘ev_default_loop_ptr’ initialized and declared ‘extern’ + 2136 | EV_API_DECL struct ev_loop *ev_default_loop_ptr = 0; /* needs to be initialised to make it a definition despite extern */ + | ^~~~~~~~~~~~~~~~~~~ +../libev/ev.c: In function ‘evpipe_write’: +../libev/ev.c:2798:11: warning: ignoring return value of ‘write’, declared with attribute warn_unused_result [-Wunused-result] + 2798 | write (evpipe [1], &counter, sizeof (uint64_t)); + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +../libev/ev.c:2810:11: warning: ignoring return value of ‘write’, declared with attribute warn_unused_result [-Wunused-result] + 2810 | write (evpipe [1], &(evpipe [1]), 1); + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +../libev/ev.c: In function ‘pipecb’: +../libev/ev.c:2831:11: warning: ignoring return value of ‘read’, declared with attribute warn_unused_result [-Wunused-result] + 2831 | read (evpipe [1], &counter, sizeof (uint64_t)); + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +../libev/ev.c:2845:11: warning: ignoring return value of ‘read’, declared with attribute warn_unused_result [-Wunused-result] + 2845 | read (evpipe [0], &dummy, sizeof (dummy)); + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +compiling selector.c +selector.c: In function ‘NIO_Selector_synchronize’: +selector.c:301:26: warning: passing argument 1 of ‘rb_ensure’ from incompatible pointer type [-Wincompatible-pointer-types] + 301 | return rb_ensure(func, (VALUE)args, NIO_Selector_unlock, self); + | ^~~~ + | | + | VALUE (*)(VALUE *) {aka long unsigned int (*)(long unsigned int *)} +In file included from /opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/ruby.h:33, + from nio4r.h:10, + from selector.c:6: +/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/ruby/ruby.h:1992:17: note: expected ‘VALUE (*)(VALUE)’ {aka ‘long unsigned int (*)(long unsigned int)’} but argument is of type ‘VALUE (*)(VALUE *)’ {aka ‘long unsigned int (*)(long unsigned int *)’} + 1992 | VALUE rb_ensure(VALUE(*)(VALUE),VALUE,VALUE(*)(VALUE),VALUE); + | ^~~~~~~~~~~~~~~ +selector.c: In function ‘NIO_Selector_wakeup’: +selector.c:499:5: warning: ignoring return value of ‘write’, declared with attribute warn_unused_result [-Wunused-result] + 499 | write(selector->wakeup_writer, "\0", 1); + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +linking shared-object nio4r_ext.so + +current directory: /home/runner/work/puppet-lint-check_unsafe_interpolations/puppet-lint-check_unsafe_interpolations/vendor/bundle/ruby/2.7.0/gems/nio4r-2.5.8/ext/nio4r +make "DESTDIR=" install +/usr/bin/install -c -m 0755 nio4r_ext.so ./.gem.20221126-1610-1h36rwb diff --git a/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/nio4r-2.5.8/mkmf.log b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/nio4r-2.5.8/mkmf.log new file mode 100644 index 0000000..0a7acfb --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/nio4r-2.5.8/mkmf.log @@ -0,0 +1,138 @@ +have_header: checking for unistd.h... -------------------- yes + +"gcc -o conftest -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/ruby/backward -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0 -I. -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include -DENABLE_PATH_CHECK=0 -g -O2 -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -L. -L/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,-rpath,/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -L/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -lruby -lm -lc" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: int main(int argc, char **argv) +4: { +5: return !!argv[argc]; +6: } +/* end */ + +"gcc -E -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/ruby/backward -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0 -I. -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include -DENABLE_PATH_CHECK=0 -g -O2 -fPIC conftest.c -o conftest.i" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +-------------------- + +have_header: checking for linux/aio_abi.h... -------------------- yes + +"gcc -E -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/ruby/backward -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0 -I. -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include -DENABLE_PATH_CHECK=0 -g -O2 -fPIC conftest.c -o conftest.i" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +-------------------- + +have_header: checking for linux/io_uring.h... -------------------- yes + +"gcc -E -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/ruby/backward -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0 -I. -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include -DENABLE_PATH_CHECK=0 -g -O2 -fPIC conftest.c -o conftest.i" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +-------------------- + +have_header: checking for sys/select.h... -------------------- yes + +"gcc -E -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/ruby/backward -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0 -I. -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include -DENABLE_PATH_CHECK=0 -g -O2 -fPIC conftest.c -o conftest.i" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +-------------------- + +have_type: checking for port_event_t in poll.h... -------------------- no + +"gcc -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/ruby/backward -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0 -I. -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include -DENABLE_PATH_CHECK=0 -g -O2 -fPIC -c conftest.c" +conftest.c:6:9: error: unknown type name ‘port_event_t’ + 6 | typedef port_event_t conftest_type; + | ^~~~~~~~~~~~ +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +4: +5: /*top*/ +6: typedef port_event_t conftest_type; +7: int conftestval[sizeof(conftest_type)?1:-1]; +/* end */ + +-------------------- + +have_header: checking for sys/epoll.h... -------------------- yes + +"gcc -E -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/ruby/backward -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0 -I. -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include -DENABLE_PATH_CHECK=0 -g -O2 -fPIC conftest.c -o conftest.i" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +-------------------- + +have_header: checking for sys/event.h... -------------------- no + +"gcc -E -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/ruby/backward -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0 -I. -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include -DENABLE_PATH_CHECK=0 -g -O2 -fPIC conftest.c -o conftest.i" +conftest.c:3:10: fatal error: sys/event.h: No such file or directory + 3 | #include + | ^~~~~~~~~~~~~ +compilation terminated. +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +-------------------- + +have_type: checking for port_event_t in port.h... -------------------- no + +"gcc -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/ruby/backward -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0 -I. -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include -DENABLE_PATH_CHECK=0 -g -O2 -fPIC -c conftest.c" +conftest.c:3:10: fatal error: port.h: No such file or directory + 3 | #include + | ^~~~~~~~ +compilation terminated. +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +4: +5: /*top*/ +6: typedef port_event_t conftest_type; +7: int conftestval[sizeof(conftest_type)?1:-1]; +/* end */ + +-------------------- + +have_header: checking for sys/resource.h... -------------------- yes + +"gcc -E -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/ruby/backward -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0 -I. -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include -DENABLE_PATH_CHECK=0 -g -O2 -fPIC conftest.c -o conftest.i" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +-------------------- + diff --git a/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/nio4r-2.5.8/nio4r_ext.so b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/nio4r-2.5.8/nio4r_ext.so new file mode 100755 index 0000000..8ac7984 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/extensions/x86_64-linux/2.7.0/nio4r-2.5.8/nio4r_ext.so differ diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/CHANGELOG.md b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/CHANGELOG.md new file mode 100644 index 0000000..370a639 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/CHANGELOG.md @@ -0,0 +1,455 @@ +## Rails 7.0.4 (September 09, 2022) ## + +* Redis cache store is now compatible with redis-rb 5.0. + + *Jean Boussier* + +* Fix `NoMethodError` on custom `ActiveSupport::Deprecation` behavior. + + `ActiveSupport::Deprecation.behavior=` was supposed to accept any object + that responds to `call`, but in fact its internal implementation assumed that + this object could respond to `arity`, so it was restricted to only `Proc` objects. + + This change removes this `arity` restriction of custom behaviors. + + *Ryo Nakamura* + + +## Rails 7.0.3.1 (July 12, 2022) ## + +* No changes. + + +## Rails 7.0.3 (May 09, 2022) ## + +* No changes. + + +## Rails 7.0.2.4 (April 26, 2022) ## + +* Fix and add protections for XSS in `ActionView::Helpers` and `ERB::Util`. + + Add the method `ERB::Util.xml_name_escape` to escape dangerous characters + in names of tags and names of attributes, following the specification of XML. + + *Álvaro Martín Fraguas* + +## Rails 7.0.2.3 (March 08, 2022) ## + +* No changes. + + +## Rails 7.0.2.2 (February 11, 2022) ## + +* Fix Reloader method signature to work with the new Executor signature + + +## Rails 7.0.2.1 (February 11, 2022) ## + +* No changes. + + +## Rails 7.0.2 (February 08, 2022) ## + +* Fix `ActiveSupport::EncryptedConfiguration` to be compatible with Psych 4 + + *Stephen Sugden* + +* Improve `File.atomic_write` error handling. + + *Daniel Pepper* + + +## Rails 7.0.1 (January 06, 2022) ## + +* Fix `Class#descendants` and `DescendantsTracker#descendants` compatibility with Ruby 3.1. + + [The native `Class#descendants` was reverted prior to Ruby 3.1 release](https://bugs.ruby-lang.org/issues/14394#note-33), + but `Class#subclasses` was kept, breaking the feature detection. + + *Jean Boussier* + + +## Rails 7.0.0 (December 15, 2021) ## + +* Fix `ActiveSupport::Duration.build` to support negative values. + + The algorithm to collect the `parts` of the `ActiveSupport::Duration` + ignored the sign of the `value` and accumulated incorrect part values. This + impacted `ActiveSupport::Duration#sum` (which is dependent on `parts`) but + not `ActiveSupport::Duration#eql?` (which is dependent on `value`). + + *Caleb Buxton*, *Braden Staudacher* + + +## Rails 7.0.0.rc3 (December 14, 2021) ## + +* No changes. + + +## Rails 7.0.0.rc2 (December 14, 2021) ## + +* No changes. + +## Rails 7.0.0.rc1 (December 06, 2021) ## + +* Deprecate passing a format to `#to_s` in favor of `#to_formatted_s` in `Array`, `Range`, `Date`, `DateTime`, `Time`, + `BigDecimal`, `Float` and, `Integer`. + + *Rafael Mendonça França* + +* Document `ActiveSupport::Testing::Deprecation`. + + *Sam Bostock & Sam Jordan* + +* Add `Pathname#existence`. + + ```ruby + Pathname.new("file").existence&.read + ``` + + *Timo Schilling* + +* Remove deprecate `ActiveSupport::Multibyte::Unicode.default_normalization_form`. + + *Rafael Mendonça França* + +* Remove deprecated support to use `Range#include?` to check the inclusion of a value in + a date time range is deprecated. + + *Rafael Mendonça França* + +* Remove deprecated `URI.parser`. + + *Rafael Mendonça França* + +* Remove deprecated `config.active_support.use_sha1_digests`. + + *Rafael Mendonça França* + +* Invoking `Object#with_options` without a `&block` argument returns the + `ActiveSupport::OptionMerger` instance. + + *Sean Doyle* + +* `Rails.application.executor` hooks can now be called around every test + + This helps to better simulate request or job local state being reset around tests and prevents state + leaking from one test to another. + + However it requires the executor hooks executed in the test environment to be re-entrant. + + To enable this, set `config.active_support.executor_around_test_case = true` (this is the default in Rails 7). + + *Jean Boussier* + +* `ActiveSupport::DescendantsTracker` now mostly delegate to `Class#descendants` on Ruby 3.1 + + Ruby now provides a fast `Class#descendants` making `ActiveSupport::DescendantsTracker` mostly useless. + + As a result the following methods are deprecated: + + - `ActiveSupport::DescendantsTracker.direct_descendants` + - `ActiveSupport::DescendantsTracker#direct_descendants` + + *Jean Boussier* + +* Fix the `Digest::UUID.uuid_from_hash` behavior for namespace IDs that are different from the ones defined on `Digest::UUID`. + + The new behavior will be enabled by setting the + `config.active_support.use_rfc4122_namespaced_uuids` option to `true` + and is the default for new apps. + + The old behavior is the default for upgraded apps and will output a + deprecation warning every time a value that is different than one of + the constants defined on the `Digest::UUID` extension is used as the + namespace ID. + + *Alex Robbin*, *Erich Soares Machado*, *Eugene Kenny* + +* `ActiveSupport::Inflector::Inflections#clear(:acronyms)` is now supported, + and `inflector.clear` / `inflector.clear(:all)` also clears acronyms. + + *Alex Ghiculescu*, *Oliver Peate* + + +## Rails 7.0.0.alpha2 (September 15, 2021) ## + +* No changes. + + +## Rails 7.0.0.alpha1 (September 15, 2021) ## + +* `ActiveSupport::Dependencies` no longer installs a `const_missing` hook. Before this, you could push to the autoload paths and have constants autoloaded. This feature, known as the `classic` autoloader, has been removed. + + *Xavier Noria* + +* Private internal classes of `ActiveSupport::Dependencies` have been deleted, like `ActiveSupport::Dependencies::Reference`, `ActiveSupport::Dependencies::Blamable`, and others. + + *Xavier Noria* + +* The private API of `ActiveSupport::Dependencies` has been deleted. That includes methods like `hook!`, `unhook!`, `depend_on`, `require_or_load`, `mechanism`, and many others. + + *Xavier Noria* + +* Improves the performance of `ActiveSupport::NumberHelper` formatters by avoiding the use of exceptions as flow control. + + *Mike Dalessio* + +* Removed rescue block from `ActiveSupport::Cache::RedisCacheStore#handle_exception` + + Previously, if you provided a `error_handler` to `redis_cache_store`, any errors thrown by + the error handler would be rescued and logged only. Removed the `rescue` clause from `handle_exception` + to allow these to be thrown. + + *Nicholas A. Stuart* + +* Allow entirely opting out of deprecation warnings. + + Previously if you did `app.config.active_support.deprecation = :silence`, some work would + still be done on each call to `ActiveSupport::Deprecation.warn`. In very hot paths, this could + cause performance issues. + + Now, you can make `ActiveSupport::Deprecation.warn` a no-op: + + ```ruby + config.active_support.report_deprecations = false + ``` + + This is the default in production for new apps. It is the equivalent to: + + ```ruby + config.active_support.deprecation = :silence + config.active_support.disallowed_deprecation = :silence + ``` + + but will take a more optimised code path. + + *Alex Ghiculescu* + +* Faster tests by parallelizing only when overhead is justified by the number + of them. + + Running tests in parallel adds overhead in terms of database + setup and fixture loading. Now, Rails will only parallelize test executions when + there are enough tests to make it worth it. + + This threshold is 50 by default, and is configurable via config setting in + your test.rb: + + ```ruby + config.active_support.test_parallelization_threshold = 100 + ``` + + It's also configurable at the test case level: + + ```ruby + class ActiveSupport::TestCase + parallelize threshold: 100 + end + ``` + + *Jorge Manrubia* + +* OpenSSL constants are now used for Digest computations. + + *Dirkjan Bussink* + +* `TimeZone.iso8601` now accepts valid ordinal values similar to Ruby's `Date._iso8601` method. + A valid ordinal value will be converted to an instance of `TimeWithZone` using the `:year` + and `:yday` fragments returned from `Date._iso8601`. + + ```ruby + twz = ActiveSupport::TimeZone["Eastern Time (US & Canada)"].iso8601("21087") + twz.to_a[0, 6] == [0, 0, 0, 28, 03, 2021] + ``` + + *Steve Laing* + +* `Time#change` and methods that call it (e.g. `Time#advance`) will now + return a `Time` with the timezone argument provided, if the caller was + initialized with a timezone argument. + + Fixes [#42467](https://github.com/rails/rails/issues/42467). + + *Alex Ghiculescu* + +* Allow serializing any module or class to JSON by name. + + *Tyler Rick*, *Zachary Scott* + +* Raise `ActiveSupport::EncryptedFile::MissingKeyError` when the + `RAILS_MASTER_KEY` environment variable is blank (e.g. `""`). + + *Sunny Ripert* + +* The `from:` option is added to `ActiveSupport::TestCase#assert_no_changes`. + + It permits asserting on the initial value that is expected not to change. + + ```ruby + assert_no_changes -> { Status.all_good? }, from: true do + post :create, params: { status: { ok: true } } + end + ``` + + *George Claghorn* + +* Deprecate `ActiveSupport::SafeBuffer`'s incorrect implicit conversion of objects into string. + + Except for a few methods like `String#%`, objects must implement `#to_str` + to be implicitly converted to a String in string operations. In some + circumstances `ActiveSupport::SafeBuffer` was incorrectly calling the + explicit conversion method (`#to_s`) on them. This behavior is now + deprecated. + + *Jean Boussier* + +* Allow nested access to keys on `Rails.application.credentials`. + + Previously only top level keys in `credentials.yml.enc` could be accessed with method calls. Now any key can. + + For example, given these secrets: + + ```yml + aws: + access_key_id: 123 + secret_access_key: 345 + ``` + + `Rails.application.credentials.aws.access_key_id` will now return the same thing as + `Rails.application.credentials.aws[:access_key_id]`. + + *Alex Ghiculescu* + +* Added a faster and more compact `ActiveSupport::Cache` serialization format. + + It can be enabled with `config.active_support.cache_format_version = 7.0` or + `config.load_defaults 7.0`. Regardless of the configuration Active Support + 7.0 can read cache entries serialized by Active Support 6.1 which allows to + upgrade without invalidating the cache. However Rails 6.1 can't read the + new format, so all readers must be upgraded before the new format is enabled. + + *Jean Boussier* + +* Add `Enumerable#sole`, per `ActiveRecord::FinderMethods#sole`. Returns the + sole item of the enumerable, raising if no items are found, or if more than + one is. + + *Asherah Connor* + +* Freeze `ActiveSupport::Duration#parts` and remove writer methods. + + Durations are meant to be value objects and should not be mutated. + + *Andrew White* + +* Fix `ActiveSupport::TimeZone#utc_to_local` with fractional seconds. + + When `utc_to_local_returns_utc_offset_times` is false and the time + instance had fractional seconds the new UTC time instance was out by + a factor of 1,000,000 as the `Time.utc` constructor takes a usec + value and not a fractional second value. + + *Andrew White* + +* Add `expires_at` argument to `ActiveSupport::Cache` `write` and `fetch` to set a cache entry TTL as an absolute time. + + ```ruby + Rails.cache.write(key, value, expires_at: Time.now.at_end_of_hour) + ``` + + *Jean Boussier* + +* Deprecate `ActiveSupport::TimeWithZone.name` so that from Rails 7.1 it will use the default implementation. + + *Andrew White* + +* Deprecates Rails custom `Enumerable#sum` and `Array#sum` in favor of Ruby's native implementation which + is considerably faster. + + Ruby requires an initializer for non-numeric type as per examples below: + + ```ruby + %w[foo bar].sum('') + # instead of %w[foo bar].sum + + [[1, 2], [3, 4, 5]].sum([]) + # instead of [[1, 2], [3, 4, 5]].sum + ``` + + *Alberto Mota* + +* Tests parallelization is now disabled when running individual files to prevent the setup overhead. + + It can still be enforced if the environment variable `PARALLEL_WORKERS` is present and set to a value greater than 1. + + *Ricardo Díaz* + +* Fix proxying keyword arguments in `ActiveSupport::CurrentAttributes`. + + *Marcin Kołodziej* + +* Add `Enumerable#maximum` and `Enumerable#minimum` to easily calculate the maximum or minimum from extracted + elements of an enumerable. + + ```ruby + payments = [Payment.new(5), Payment.new(15), Payment.new(10)] + + payments.minimum(:price) # => 5 + payments.maximum(:price) # => 15 + ``` + + This also allows passing enumerables to `fresh_when` and `stale?` in Action Controller. + See PR [#41404](https://github.com/rails/rails/pull/41404) for an example. + + *Ayrton De Craene* + +* `ActiveSupport::Cache::MemCacheStore` now accepts an explicit `nil` for its `addresses` argument. + + ```ruby + config.cache_store = :mem_cache_store, nil + + # is now equivalent to + + config.cache_store = :mem_cache_store + + # and is also equivalent to + + config.cache_store = :mem_cache_store, ENV["MEMCACHE_SERVERS"] || "localhost:11211" + + # which is the fallback behavior of Dalli + ``` + + This helps those migrating from `:dalli_store`, where an explicit `nil` was permitted. + + *Michael Overmeyer* + +* Add `Enumerable#in_order_of` to put an Enumerable in a certain order by a key. + + *DHH* + +* `ActiveSupport::Inflector.camelize` behaves expected when provided a symbol `:upper` or `:lower` argument. Matches + `String#camelize` behavior. + + *Alex Ghiculescu* + +* Raises an `ArgumentError` when the first argument of `ActiveSupport::Notification.subscribe` is + invalid. + + *Vipul A M* + +* `HashWithIndifferentAccess#deep_transform_keys` now returns a `HashWithIndifferentAccess` instead of a `Hash`. + + *Nathaniel Woodthorpe* + +* Consume dalli’s `cache_nils` configuration as `ActiveSupport::Cache`'s `skip_nil` when using `MemCacheStore`. + + *Ritikesh G* + +* Add `RedisCacheStore#stats` method similar to `MemCacheStore#stats`. Calls `redis#info` internally. + + *Ritikesh G* + + +Please check [6-1-stable](https://github.com/rails/rails/blob/6-1-stable/activesupport/CHANGELOG.md) for previous changes. diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/MIT-LICENSE b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/MIT-LICENSE new file mode 100644 index 0000000..0a0ce38 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/MIT-LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2005-2022 David Heinemeier Hansson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/README.rdoc b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/README.rdoc new file mode 100644 index 0000000..c2df6d7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/README.rdoc @@ -0,0 +1,40 @@ += Active Support -- Utility classes and Ruby extensions from Rails + +Active Support is a collection of utility classes and standard library +extensions that were found useful for the Rails framework. These additions +reside in this package so they can be loaded as needed in Ruby projects +outside of Rails. + +You can read more about the extensions in the {Active Support Core Extensions}[https://edgeguides.rubyonrails.org/active_support_core_extensions.html] guide. + +== Download and installation + +The latest version of Active Support can be installed with RubyGems: + + $ gem install activesupport + +Source code can be downloaded as part of the Rails project on GitHub: + +* https://github.com/rails/rails/tree/main/activesupport + + +== License + +Active Support is released under the MIT license: + +* https://opensource.org/licenses/MIT + + +== Support + +API documentation is at: + +* https://api.rubyonrails.org + +Bug reports for the Ruby on Rails project can be filed here: + +* https://github.com/rails/rails/issues + +Feature requests should be discussed on the rails-core mailing list here: + +* https://discuss.rubyonrails.org/c/rubyonrails-core diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support.rb new file mode 100644 index 0000000..e04796b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support.rb @@ -0,0 +1,124 @@ +# frozen_string_literal: true + +#-- +# Copyright (c) 2005-2022 David Heinemeier Hansson +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +require "securerandom" +require "active_support/dependencies/autoload" +require "active_support/version" +require "active_support/logger" +require "active_support/lazy_load_hooks" +require "active_support/core_ext/date_and_time/compatibility" + +module ActiveSupport + extend ActiveSupport::Autoload + + autoload :Concern + autoload :CodeGenerator + autoload :ActionableError + autoload :ConfigurationFile + autoload :CurrentAttributes + autoload :Dependencies + autoload :DescendantsTracker + autoload :ExecutionContext + autoload :ExecutionWrapper + autoload :Executor + autoload :ErrorReporter + autoload :FileUpdateChecker + autoload :EventedFileUpdateChecker + autoload :ForkTracker + autoload :LogSubscriber + autoload :IsolatedExecutionState + autoload :Notifications + autoload :Reloader + autoload :PerThreadRegistry + autoload :SecureCompareRotator + + eager_autoload do + autoload :BacktraceCleaner + autoload :ProxyObject + autoload :Benchmarkable + autoload :Cache + autoload :Callbacks + autoload :Configurable + autoload :Deprecation + autoload :Digest + autoload :Gzip + autoload :Inflector + autoload :JSON + autoload :KeyGenerator + autoload :MessageEncryptor + autoload :MessageVerifier + autoload :Multibyte + autoload :NumberHelper + autoload :OptionMerger + autoload :OrderedHash + autoload :OrderedOptions + autoload :StringInquirer + autoload :EnvironmentInquirer + autoload :TaggedLogging + autoload :XmlMini + autoload :ArrayInquirer + end + + autoload :Rescuable + autoload :SafeBuffer, "active_support/core_ext/string/output_safety" + autoload :TestCase + + def self.eager_load! + super + + NumberHelper.eager_load! + end + + cattr_accessor :test_order # :nodoc: + cattr_accessor :test_parallelization_threshold, default: 50 # :nodoc: + + singleton_class.attr_accessor :error_reporter # :nodoc: + + def self.cache_format_version + Cache.format_version + end + + def self.cache_format_version=(value) + Cache.format_version = value + end + + def self.to_time_preserves_timezone + DateAndTime::Compatibility.preserve_timezone + end + + def self.to_time_preserves_timezone=(value) + DateAndTime::Compatibility.preserve_timezone = value + end + + def self.utc_to_local_returns_utc_offset_times + DateAndTime::Compatibility.utc_to_local_returns_utc_offset_times + end + + def self.utc_to_local_returns_utc_offset_times=(value) + DateAndTime::Compatibility.utc_to_local_returns_utc_offset_times = value + end +end + +autoload :I18n, "active_support/i18n" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/actionable_error.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/actionable_error.rb new file mode 100644 index 0000000..2b8b2ff --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/actionable_error.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module ActiveSupport + # Actionable errors lets you define actions to resolve an error. + # + # To make an error actionable, include the ActiveSupport::ActionableError + # module and invoke the +action+ class macro to define the action. An action + # needs a name and a block to execute. + module ActionableError + extend Concern + + class NonActionable < StandardError; end + + included do + class_attribute :_actions, default: {} + end + + def self.actions(error) # :nodoc: + case error + when ActionableError, -> it { Class === it && it < ActionableError } + error._actions + else + {} + end + end + + def self.dispatch(error, name) # :nodoc: + actions(error).fetch(name).call + rescue KeyError + raise NonActionable, "Cannot find action \"#{name}\"" + end + + module ClassMethods + # Defines an action that can resolve the error. + # + # class PendingMigrationError < MigrationError + # include ActiveSupport::ActionableError + # + # action "Run pending migrations" do + # ActiveRecord::Tasks::DatabaseTasks.migrate + # end + # end + def action(name, &block) + _actions[name] = block + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/all.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/all.rb new file mode 100644 index 0000000..4adf446 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/all.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +require "active_support" +require "active_support/time" +require "active_support/core_ext" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/array_inquirer.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/array_inquirer.rb new file mode 100644 index 0000000..ecd2389 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/array_inquirer.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module ActiveSupport + # Wrapping an array in an +ArrayInquirer+ gives a friendlier way to check + # its string-like contents: + # + # variants = ActiveSupport::ArrayInquirer.new([:phone, :tablet]) + # + # variants.phone? # => true + # variants.tablet? # => true + # variants.desktop? # => false + class ArrayInquirer < Array + # Passes each element of +candidates+ collection to ArrayInquirer collection. + # The method returns true if any element from the ArrayInquirer collection + # is equal to the stringified or symbolized form of any element in the +candidates+ collection. + # + # If +candidates+ collection is not given, method returns true. + # + # variants = ActiveSupport::ArrayInquirer.new([:phone, :tablet]) + # + # variants.any? # => true + # variants.any?(:phone, :tablet) # => true + # variants.any?('phone', 'desktop') # => true + # variants.any?(:desktop, :watch) # => false + def any?(*candidates) + if candidates.none? + super + else + candidates.any? do |candidate| + include?(candidate.to_sym) || include?(candidate.to_s) + end + end + end + + private + def respond_to_missing?(name, include_private = false) + name.end_with?("?") || super + end + + def method_missing(name, *args) + if name.end_with?("?") + any?(name[0..-2]) + else + super + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/backtrace_cleaner.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/backtrace_cleaner.rb new file mode 100644 index 0000000..d570f21 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/backtrace_cleaner.rb @@ -0,0 +1,131 @@ +# frozen_string_literal: true + +module ActiveSupport + # Backtraces often include many lines that are not relevant for the context + # under review. This makes it hard to find the signal amongst the backtrace + # noise, and adds debugging time. With a BacktraceCleaner, filters and + # silencers are used to remove the noisy lines, so that only the most relevant + # lines remain. + # + # Filters are used to modify lines of data, while silencers are used to remove + # lines entirely. The typical filter use case is to remove lengthy path + # information from the start of each line, and view file paths relevant to the + # app directory instead of the file system root. The typical silencer use case + # is to exclude the output of a noisy library from the backtrace, so that you + # can focus on the rest. + # + # bc = ActiveSupport::BacktraceCleaner.new + # bc.add_filter { |line| line.gsub(Rails.root.to_s, '') } # strip the Rails.root prefix + # bc.add_silencer { |line| /puma|rubygems/.match?(line) } # skip any lines from puma or rubygems + # bc.clean(exception.backtrace) # perform the cleanup + # + # To reconfigure an existing BacktraceCleaner (like the default one in Rails) + # and show as much data as possible, you can always call + # BacktraceCleaner#remove_silencers!, which will restore the + # backtrace to a pristine state. If you need to reconfigure an existing + # BacktraceCleaner so that it does not filter or modify the paths of any lines + # of the backtrace, you can call BacktraceCleaner#remove_filters! + # These two methods will give you a completely untouched backtrace. + # + # Inspired by the Quiet Backtrace gem by thoughtbot. + class BacktraceCleaner + def initialize + @filters, @silencers = [], [] + add_gem_filter + add_gem_silencer + add_stdlib_silencer + end + + # Returns the backtrace after all filters and silencers have been run + # against it. Filters run first, then silencers. + def clean(backtrace, kind = :silent) + filtered = filter_backtrace(backtrace) + + case kind + when :silent + silence(filtered) + when :noise + noise(filtered) + else + filtered + end + end + alias :filter :clean + + # Adds a filter from the block provided. Each line in the backtrace will be + # mapped against this filter. + # + # # Will turn "/my/rails/root/app/models/person.rb" into "/app/models/person.rb" + # backtrace_cleaner.add_filter { |line| line.gsub(Rails.root, '') } + def add_filter(&block) + @filters << block + end + + # Adds a silencer from the block provided. If the silencer returns +true+ + # for a given line, it will be excluded from the clean backtrace. + # + # # Will reject all lines that include the word "puma", like "/gems/puma/server.rb" or "/app/my_puma_server/rb" + # backtrace_cleaner.add_silencer { |line| /puma/.match?(line) } + def add_silencer(&block) + @silencers << block + end + + # Removes all silencers, but leaves in the filters. Useful if your + # context of debugging suddenly expands as you suspect a bug in one of + # the libraries you use. + def remove_silencers! + @silencers = [] + end + + # Removes all filters, but leaves in the silencers. Useful if you suddenly + # need to see entire filepaths in the backtrace that you had already + # filtered out. + def remove_filters! + @filters = [] + end + + private + FORMATTED_GEMS_PATTERN = /\A[^\/]+ \([\w.]+\) / + + def add_gem_filter + gems_paths = (Gem.path | [Gem.default_dir]).map { |p| Regexp.escape(p) } + return if gems_paths.empty? + + gems_regexp = %r{\A(#{gems_paths.join('|')})/(bundler/)?gems/([^/]+)-([\w.]+)/(.*)} + gems_result = '\3 (\4) \5' + add_filter { |line| line.sub(gems_regexp, gems_result) } + end + + def add_gem_silencer + add_silencer { |line| FORMATTED_GEMS_PATTERN.match?(line) } + end + + def add_stdlib_silencer + add_silencer { |line| line.start_with?(RbConfig::CONFIG["rubylibdir"]) } + end + + def filter_backtrace(backtrace) + @filters.each do |f| + backtrace = backtrace.map { |line| f.call(line) } + end + + backtrace + end + + def silence(backtrace) + @silencers.each do |s| + backtrace = backtrace.reject { |line| s.call(line) } + end + + backtrace + end + + def noise(backtrace) + backtrace.select do |line| + @silencers.any? do |s| + s.call(line) + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/benchmarkable.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/benchmarkable.rb new file mode 100644 index 0000000..4060784 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/benchmarkable.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require "active_support/core_ext/benchmark" +require "active_support/core_ext/hash/keys" + +module ActiveSupport + module Benchmarkable + # Allows you to measure the execution time of a block in a template and + # records the result to the log. Wrap this block around expensive operations + # or possible bottlenecks to get a time reading for the operation. For + # example, let's say you thought your file processing method was taking too + # long; you could wrap it in a benchmark block. + # + # <% benchmark 'Process data files' do %> + # <%= expensive_files_operation %> + # <% end %> + # + # That would add something like "Process data files (345.2ms)" to the log, + # which you can then use to compare timings when optimizing your code. + # + # You may give an optional logger level (:debug, :info, + # :warn, :error) as the :level option. The + # default logger level value is :info. + # + # <% benchmark 'Low-level files', level: :debug do %> + # <%= lowlevel_files_operation %> + # <% end %> + # + # Finally, you can pass true as the third argument to silence all log + # activity (other than the timing information) from inside the block. This + # is great for boiling down a noisy block to just a single statement that + # produces one log line: + # + # <% benchmark 'Process data files', level: :info, silence: true do %> + # <%= expensive_and_chatty_files_operation %> + # <% end %> + def benchmark(message = "Benchmarking", options = {}, &block) + if logger + options.assert_valid_keys(:level, :silence) + options[:level] ||= :info + + result = nil + ms = Benchmark.ms { result = options[:silence] ? logger.silence(&block) : yield } + logger.public_send(options[:level], "%s (%.1fms)" % [ message, ms ]) + result + else + yield + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/builder.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/builder.rb new file mode 100644 index 0000000..3fa7e6b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/builder.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +begin + require "builder" +rescue LoadError => e + $stderr.puts "You don't have builder installed in your application. Please add it to your Gemfile and run bundle install" + raise e +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache.rb new file mode 100644 index 0000000..a059c1e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache.rb @@ -0,0 +1,1030 @@ +# frozen_string_literal: true + +require "zlib" +require "active_support/core_ext/array/extract_options" +require "active_support/core_ext/array/wrap" +require "active_support/core_ext/enumerable" +require "active_support/core_ext/module/attribute_accessors" +require "active_support/core_ext/numeric/bytes" +require "active_support/core_ext/numeric/time" +require "active_support/core_ext/object/to_param" +require "active_support/core_ext/object/try" +require "active_support/core_ext/string/inflections" + +module ActiveSupport + # See ActiveSupport::Cache::Store for documentation. + module Cache + autoload :FileStore, "active_support/cache/file_store" + autoload :MemoryStore, "active_support/cache/memory_store" + autoload :MemCacheStore, "active_support/cache/mem_cache_store" + autoload :NullStore, "active_support/cache/null_store" + autoload :RedisCacheStore, "active_support/cache/redis_cache_store" + + # These options mean something to all cache implementations. Individual cache + # implementations may support additional options. + UNIVERSAL_OPTIONS = [:namespace, :compress, :compress_threshold, :expires_in, :expire_in, :expired_in, :race_condition_ttl, :coder, :skip_nil] + + DEFAULT_COMPRESS_LIMIT = 1.kilobyte + + # Mapping of canonical option names to aliases that a store will recognize. + OPTION_ALIASES = { + expires_in: [:expire_in, :expired_in] + }.freeze + + module Strategy + autoload :LocalCache, "active_support/cache/strategy/local_cache" + end + + @format_version = 6.1 + + class << self + attr_accessor :format_version + + # Creates a new Store object according to the given options. + # + # If no arguments are passed to this method, then a new + # ActiveSupport::Cache::MemoryStore object will be returned. + # + # If you pass a Symbol as the first argument, then a corresponding cache + # store class under the ActiveSupport::Cache namespace will be created. + # For example: + # + # ActiveSupport::Cache.lookup_store(:memory_store) + # # => returns a new ActiveSupport::Cache::MemoryStore object + # + # ActiveSupport::Cache.lookup_store(:mem_cache_store) + # # => returns a new ActiveSupport::Cache::MemCacheStore object + # + # Any additional arguments will be passed to the corresponding cache store + # class's constructor: + # + # ActiveSupport::Cache.lookup_store(:file_store, '/tmp/cache') + # # => same as: ActiveSupport::Cache::FileStore.new('/tmp/cache') + # + # If the first argument is not a Symbol, then it will simply be returned: + # + # ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new) + # # => returns MyOwnCacheStore.new + def lookup_store(store = nil, *parameters) + case store + when Symbol + options = parameters.extract_options! + # clean this up once Ruby 2.7 support is dropped + # see https://github.com/rails/rails/pull/41522#discussion_r581186602 + if options.empty? + retrieve_store_class(store).new(*parameters) + else + retrieve_store_class(store).new(*parameters, **options) + end + when Array + lookup_store(*store) + when nil + ActiveSupport::Cache::MemoryStore.new + else + store + end + end + + # Expands out the +key+ argument into a key that can be used for the + # cache store. Optionally accepts a namespace, and all keys will be + # scoped within that namespace. + # + # If the +key+ argument provided is an array, or responds to +to_a+, then + # each of elements in the array will be turned into parameters/keys and + # concatenated into a single key. For example: + # + # ActiveSupport::Cache.expand_cache_key([:foo, :bar]) # => "foo/bar" + # ActiveSupport::Cache.expand_cache_key([:foo, :bar], "namespace") # => "namespace/foo/bar" + # + # The +key+ argument can also respond to +cache_key+ or +to_param+. + def expand_cache_key(key, namespace = nil) + expanded_cache_key = namespace ? +"#{namespace}/" : +"" + + if prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"] + expanded_cache_key << "#{prefix}/" + end + + expanded_cache_key << retrieve_cache_key(key) + expanded_cache_key + end + + private + def retrieve_cache_key(key) + case + when key.respond_to?(:cache_key_with_version) then key.cache_key_with_version + when key.respond_to?(:cache_key) then key.cache_key + when key.is_a?(Array) then key.map { |element| retrieve_cache_key(element) }.to_param + when key.respond_to?(:to_a) then retrieve_cache_key(key.to_a) + else key.to_param + end.to_s + end + + # Obtains the specified cache store class, given the name of the +store+. + # Raises an error when the store class cannot be found. + def retrieve_store_class(store) + # require_relative cannot be used here because the class might be + # provided by another gem, like redis-activesupport for example. + require "active_support/cache/#{store}" + rescue LoadError => e + raise "Could not find cache store adapter for #{store} (#{e})" + else + ActiveSupport::Cache.const_get(store.to_s.camelize) + end + end + + # An abstract cache store class. There are multiple cache store + # implementations, each having its own additional features. See the classes + # under the ActiveSupport::Cache module, e.g. + # ActiveSupport::Cache::MemCacheStore. MemCacheStore is currently the most + # popular cache store for large production websites. + # + # Some implementations may not support all methods beyond the basic cache + # methods of #fetch, #write, #read, #exist?, and #delete. + # + # ActiveSupport::Cache::Store can store any Ruby object that is supported by + # its +coder+'s +dump+ and +load+ methods. + # + # cache = ActiveSupport::Cache::MemoryStore.new + # + # cache.read('city') # => nil + # cache.write('city', "Duckburgh") + # cache.read('city') # => "Duckburgh" + # + # cache.write('not serializable', Proc.new {}) # => TypeError + # + # Keys are always translated into Strings and are case sensitive. When an + # object is specified as a key and has a +cache_key+ method defined, this + # method will be called to define the key. Otherwise, the +to_param+ + # method will be called. Hashes and Arrays can also be used as keys. The + # elements will be delimited by slashes, and the elements within a Hash + # will be sorted by key so they are consistent. + # + # cache.read('city') == cache.read(:city) # => true + # + # Nil values can be cached. + # + # If your cache is on a shared infrastructure, you can define a namespace + # for your cache entries. If a namespace is defined, it will be prefixed on + # to every key. The namespace can be either a static value or a Proc. If it + # is a Proc, it will be invoked when each key is evaluated so that you can + # use application logic to invalidate keys. + # + # cache.namespace = -> { @last_mod_time } # Set the namespace to a variable + # @last_mod_time = Time.now # Invalidate the entire cache by changing namespace + # + class Store + cattr_accessor :logger, instance_writer: true + + attr_reader :silence, :options + alias :silence? :silence + + class << self + private + def retrieve_pool_options(options) + {}.tap do |pool_options| + pool_options[:size] = options.delete(:pool_size) if options[:pool_size] + pool_options[:timeout] = options.delete(:pool_timeout) if options[:pool_timeout] + end + end + + def ensure_connection_pool_added! + require "connection_pool" + rescue LoadError => e + $stderr.puts "You don't have connection_pool installed in your application. Please add it to your Gemfile and run bundle install" + raise e + end + end + + # Creates a new cache. + # + # ==== Options + # + # * +:namespace+ - Sets the namespace for the cache. This option is + # especially useful if your application shares a cache with other + # applications. + # * +:coder+ - Replaces the default cache entry serialization mechanism + # with a custom one. The +coder+ must respond to +dump+ and +load+. + # Using a custom coder disables automatic compression. + # + # Any other specified options are treated as default options for the + # relevant cache operations, such as #read, #write, and #fetch. + def initialize(options = nil) + @options = options ? normalize_options(options) : {} + @options[:compress] = true unless @options.key?(:compress) + @options[:compress_threshold] = DEFAULT_COMPRESS_LIMIT unless @options.key?(:compress_threshold) + + @coder = @options.delete(:coder) { default_coder } || NullCoder + @coder_supports_compression = @coder.respond_to?(:dump_compressed) + end + + # Silences the logger. + def silence! + @silence = true + self + end + + # Silences the logger within a block. + def mute + previous_silence, @silence = defined?(@silence) && @silence, true + yield + ensure + @silence = previous_silence + end + + # Fetches data from the cache, using the given key. If there is data in + # the cache with the given key, then that data is returned. + # + # If there is no such data in the cache (a cache miss), then +nil+ will be + # returned. However, if a block has been passed, that block will be passed + # the key and executed in the event of a cache miss. The return value of the + # block will be written to the cache under the given cache key, and that + # return value will be returned. + # + # cache.write('today', 'Monday') + # cache.fetch('today') # => "Monday" + # + # cache.fetch('city') # => nil + # cache.fetch('city') do + # 'Duckburgh' + # end + # cache.fetch('city') # => "Duckburgh" + # + # ==== Options + # + # Internally, +fetch+ calls #read_entry, and calls #write_entry on a cache + # miss. Thus, +fetch+ supports the same options as #read and #write. + # Additionally, +fetch+ supports the following options: + # + # * force: true - Forces a cache "miss," meaning we treat the + # cache value as missing even if it's present. Passing a block is + # required when +force+ is true so this always results in a cache write. + # + # cache.write('today', 'Monday') + # cache.fetch('today', force: true) { 'Tuesday' } # => 'Tuesday' + # cache.fetch('today', force: true) # => ArgumentError + # + # The +:force+ option is useful when you're calling some other method to + # ask whether you should force a cache write. Otherwise, it's clearer to + # just call +write+. + # + # * skip_nil: true - Prevents caching a nil result: + # + # cache.fetch('foo') { nil } + # cache.fetch('bar', skip_nil: true) { nil } + # cache.exist?('foo') # => true + # cache.exist?('bar') # => false + # + # * +:race_condition_ttl+ - Specifies the number of seconds during which + # an expired value can be reused while a new value is being generated. + # This can be used to prevent race conditions when cache entries expire, + # by preventing multiple processes from simultaneously regenerating the + # same entry (also known as the dog pile effect). + # + # When a process encounters a cache entry that has expired less than + # +:race_condition_ttl+ seconds ago, it will bump the expiration time by + # +:race_condition_ttl+ seconds before generating a new value. During + # this extended time window, while the process generates a new value, + # other processes will continue to use the old value. After the first + # process writes the new value, other processes will then use it. + # + # If the first process errors out while generating a new value, another + # process can try to generate a new value after the extended time window + # has elapsed. + # + # # Set all values to expire after one minute. + # cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1.minute) + # + # cache.write('foo', 'original value') + # val_1 = nil + # val_2 = nil + # sleep 60 + # + # Thread.new do + # val_1 = cache.fetch('foo', race_condition_ttl: 10.seconds) do + # sleep 1 + # 'new value 1' + # end + # end + # + # Thread.new do + # val_2 = cache.fetch('foo', race_condition_ttl: 10.seconds) do + # 'new value 2' + # end + # end + # + # cache.fetch('foo') # => "original value" + # sleep 10 # First thread extended the life of cache by another 10 seconds + # cache.fetch('foo') # => "new value 1" + # val_1 # => "new value 1" + # val_2 # => "original value" + # + def fetch(name, options = nil, &block) + if block_given? + options = merged_options(options) + key = normalize_key(name, options) + + entry = nil + instrument(:read, name, options) do |payload| + cached_entry = read_entry(key, **options, event: payload) unless options[:force] + entry = handle_expired_entry(cached_entry, key, options) + entry = nil if entry && entry.mismatched?(normalize_version(name, options)) + payload[:super_operation] = :fetch if payload + payload[:hit] = !!entry if payload + end + + if entry + get_entry_value(entry, name, options) + else + save_block_result_to_cache(name, options, &block) + end + elsif options && options[:force] + raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block." + else + read(name, options) + end + end + + # Reads data from the cache, using the given key. If there is data in + # the cache with the given key, then that data is returned. Otherwise, + # +nil+ is returned. + # + # Note, if data was written with the :expires_in or + # :version options, both of these conditions are applied before + # the data is returned. + # + # ==== Options + # + # * +:version+ - Specifies a version for the cache entry. If the cached + # version does not match the requested version, the read will be treated + # as a cache miss. This feature is used to support recyclable cache keys. + # + # Other options will be handled by the specific cache store implementation. + def read(name, options = nil) + options = merged_options(options) + key = normalize_key(name, options) + version = normalize_version(name, options) + + instrument(:read, name, options) do |payload| + entry = read_entry(key, **options, event: payload) + + if entry + if entry.expired? + delete_entry(key, **options) + payload[:hit] = false if payload + nil + elsif entry.mismatched?(version) + payload[:hit] = false if payload + nil + else + payload[:hit] = true if payload + entry.value + end + else + payload[:hit] = false if payload + nil + end + end + end + + # Reads multiple values at once from the cache. Options can be passed + # in the last argument. + # + # Some cache implementation may optimize this method. + # + # Returns a hash mapping the names provided to the values found. + def read_multi(*names) + options = names.extract_options! + options = merged_options(options) + + instrument :read_multi, names, options do |payload| + read_multi_entries(names, **options, event: payload).tap do |results| + payload[:hits] = results.keys + end + end + end + + # Cache Storage API to write multiple values at once. + def write_multi(hash, options = nil) + options = merged_options(options) + + instrument :write_multi, hash, options do |payload| + entries = hash.each_with_object({}) do |(name, value), memo| + memo[normalize_key(name, options)] = Entry.new(value, **options.merge(version: normalize_version(name, options))) + end + + write_multi_entries entries, **options + end + end + + # Fetches data from the cache, using the given keys. If there is data in + # the cache with the given keys, then that data is returned. Otherwise, + # the supplied block is called for each key for which there was no data, + # and the result will be written to the cache and returned. + # Therefore, you need to pass a block that returns the data to be written + # to the cache. If you do not want to write the cache when the cache is + # not found, use #read_multi. + # + # Returns a hash with the data for each of the names. For example: + # + # cache.write("bim", "bam") + # cache.fetch_multi("bim", "unknown_key") do |key| + # "Fallback value for key: #{key}" + # end + # # => { "bim" => "bam", + # # "unknown_key" => "Fallback value for key: unknown_key" } + # + # Options are passed to the underlying cache implementation. For example: + # + # cache.fetch_multi("fizz", expires_in: 5.seconds) do |key| + # "buzz" + # end + # # => {"fizz"=>"buzz"} + # cache.read("fizz") + # # => "buzz" + # sleep(6) + # cache.read("fizz") + # # => nil + def fetch_multi(*names) + raise ArgumentError, "Missing block: `Cache#fetch_multi` requires a block." unless block_given? + + options = names.extract_options! + options = merged_options(options) + + instrument :read_multi, names, options do |payload| + reads = read_multi_entries(names, **options) + writes = {} + ordered = names.index_with do |name| + reads.fetch(name) { writes[name] = yield(name) } + end + + payload[:hits] = reads.keys + payload[:super_operation] = :fetch_multi + + write_multi(writes, options) + + ordered + end + end + + # Writes the value to the cache with the key. The value must be supported + # by the +coder+'s +dump+ and +load+ methods. + # + # By default, cache entries larger than 1kB are compressed. Compression + # allows more data to be stored in the same memory footprint, leading to + # fewer cache evictions and higher hit rates. + # + # ==== Options + # + # * compress: false - Disables compression of the cache entry. + # + # * +:compress_threshold+ - The compression threshold, specified in bytes. + # \Cache entries larger than this threshold will be compressed. Defaults + # to +1.kilobyte+. + # + # * +:expires_in+ - Sets a relative expiration time for the cache entry, + # specified in seconds. +:expire_in+ and +:expired_in+ are aliases for + # +:expires_in+. + # + # cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes) + # cache.write(key, value, expires_in: 1.minute) # Set a lower value for one entry + # + # * +:expires_at+ - Sets an absolute expiration time for the cache entry. + # + # cache = ActiveSupport::Cache::MemoryStore.new + # cache.write(key, value, expires_at: Time.now.at_end_of_hour) + # + # * +:version+ - Specifies a version for the cache entry. When reading + # from the cache, if the cached version does not match the requested + # version, the read will be treated as a cache miss. This feature is + # used to support recyclable cache keys. + # + # Other options will be handled by the specific cache store implementation. + def write(name, value, options = nil) + options = merged_options(options) + + instrument(:write, name, options) do + entry = Entry.new(value, **options.merge(version: normalize_version(name, options))) + write_entry(normalize_key(name, options), entry, **options) + end + end + + # Deletes an entry in the cache. Returns +true+ if an entry is deleted. + # + # Options are passed to the underlying cache implementation. + def delete(name, options = nil) + options = merged_options(options) + + instrument(:delete, name) do + delete_entry(normalize_key(name, options), **options) + end + end + + # Deletes multiple entries in the cache. + # + # Options are passed to the underlying cache implementation. + def delete_multi(names, options = nil) + options = merged_options(options) + names.map! { |key| normalize_key(key, options) } + + instrument :delete_multi, names do + delete_multi_entries(names, **options) + end + end + + # Returns +true+ if the cache contains an entry for the given key. + # + # Options are passed to the underlying cache implementation. + def exist?(name, options = nil) + options = merged_options(options) + + instrument(:exist?, name) do |payload| + entry = read_entry(normalize_key(name, options), **options, event: payload) + (entry && !entry.expired? && !entry.mismatched?(normalize_version(name, options))) || false + end + end + + def new_entry(value, options = nil) # :nodoc: + Entry.new(value, **merged_options(options)) + end + + # Deletes all entries with keys matching the pattern. + # + # Options are passed to the underlying cache implementation. + # + # Some implementations may not support this method. + def delete_matched(matcher, options = nil) + raise NotImplementedError.new("#{self.class.name} does not support delete_matched") + end + + # Increments an integer value in the cache. + # + # Options are passed to the underlying cache implementation. + # + # Some implementations may not support this method. + def increment(name, amount = 1, options = nil) + raise NotImplementedError.new("#{self.class.name} does not support increment") + end + + # Decrements an integer value in the cache. + # + # Options are passed to the underlying cache implementation. + # + # Some implementations may not support this method. + def decrement(name, amount = 1, options = nil) + raise NotImplementedError.new("#{self.class.name} does not support decrement") + end + + # Cleans up the cache by removing expired entries. + # + # Options are passed to the underlying cache implementation. + # + # Some implementations may not support this method. + def cleanup(options = nil) + raise NotImplementedError.new("#{self.class.name} does not support cleanup") + end + + # Clears the entire cache. Be careful with this method since it could + # affect other processes if shared cache is being used. + # + # The options hash is passed to the underlying cache implementation. + # + # Some implementations may not support this method. + def clear(options = nil) + raise NotImplementedError.new("#{self.class.name} does not support clear") + end + + private + def default_coder + Coders[Cache.format_version] + end + + # Adds the namespace defined in the options to a pattern designed to + # match keys. Implementations that support delete_matched should call + # this method to translate a pattern that matches names into one that + # matches namespaced keys. + def key_matcher(pattern, options) # :doc: + prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace] + if prefix + source = pattern.source + if source.start_with?("^") + source = source[1, source.length] + else + source = ".*#{source[0, source.length]}" + end + Regexp.new("^#{Regexp.escape(prefix)}:#{source}", pattern.options) + else + pattern + end + end + + # Reads an entry from the cache implementation. Subclasses must implement + # this method. + def read_entry(key, **options) + raise NotImplementedError.new + end + + # Writes an entry to the cache implementation. Subclasses must implement + # this method. + def write_entry(key, entry, **options) + raise NotImplementedError.new + end + + def serialize_entry(entry, **options) + options = merged_options(options) + if @coder_supports_compression && options[:compress] + @coder.dump_compressed(entry, options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT) + else + @coder.dump(entry) + end + end + + def deserialize_entry(payload) + payload.nil? ? nil : @coder.load(payload) + end + + # Reads multiple entries from the cache implementation. Subclasses MAY + # implement this method. + def read_multi_entries(names, **options) + names.each_with_object({}) do |name, results| + key = normalize_key(name, options) + entry = read_entry(key, **options) + + next unless entry + + version = normalize_version(name, options) + + if entry.expired? + delete_entry(key, **options) + elsif !entry.mismatched?(version) + results[name] = entry.value + end + end + end + + # Writes multiple entries to the cache implementation. Subclasses MAY + # implement this method. + def write_multi_entries(hash, **options) + hash.each do |key, entry| + write_entry key, entry, **options + end + end + + # Deletes an entry from the cache implementation. Subclasses must + # implement this method. + def delete_entry(key, **options) + raise NotImplementedError.new + end + + # Deletes multiples entries in the cache implementation. Subclasses MAY + # implement this method. + def delete_multi_entries(entries, **options) + entries.count { |key| delete_entry(key, **options) } + end + + # Merges the default options with ones specific to a method call. + def merged_options(call_options) + if call_options + call_options = normalize_options(call_options) + if options.empty? + call_options + else + options.merge(call_options) + end + else + options + end + end + + # Normalize aliased options to their canonical form + def normalize_options(options) + options = options.dup + OPTION_ALIASES.each do |canonical_name, aliases| + alias_key = aliases.detect { |key| options.key?(key) } + options[canonical_name] ||= options[alias_key] if alias_key + options.except!(*aliases) + end + + options + end + + # Expands and namespaces the cache key. May be overridden by + # cache stores to do additional normalization. + def normalize_key(key, options = nil) + namespace_key expanded_key(key), options + end + + # Prefix the key with a namespace string: + # + # namespace_key 'foo', namespace: 'cache' + # # => 'cache:foo' + # + # With a namespace block: + # + # namespace_key 'foo', namespace: -> { 'cache' } + # # => 'cache:foo' + def namespace_key(key, options = nil) + options = merged_options(options) + namespace = options[:namespace] + + if namespace.respond_to?(:call) + namespace = namespace.call + end + + if key && key.encoding != Encoding::UTF_8 + key = key.dup.force_encoding(Encoding::UTF_8) + end + + if namespace + "#{namespace}:#{key}" + else + key + end + end + + # Expands key to be a consistent string value. Invokes +cache_key+ if + # object responds to +cache_key+. Otherwise, +to_param+ method will be + # called. If the key is a Hash, then keys will be sorted alphabetically. + def expanded_key(key) + return key.cache_key.to_s if key.respond_to?(:cache_key) + + case key + when Array + if key.size > 1 + key.collect { |element| expanded_key(element) } + else + expanded_key(key.first) + end + when Hash + key.collect { |k, v| "#{k}=#{v}" }.sort! + else + key + end.to_param + end + + def normalize_version(key, options = nil) + (options && options[:version].try(:to_param)) || expanded_version(key) + end + + def expanded_version(key) + case + when key.respond_to?(:cache_version) then key.cache_version.to_param + when key.is_a?(Array) then key.map { |element| expanded_version(element) }.tap(&:compact!).to_param + when key.respond_to?(:to_a) then expanded_version(key.to_a) + end + end + + def instrument(operation, key, options = nil) + if logger && logger.debug? && !silence? + logger.debug "Cache #{operation}: #{normalize_key(key, options)}#{options.blank? ? "" : " (#{options.inspect})"}" + end + + payload = { key: key, store: self.class.name } + payload.merge!(options) if options.is_a?(Hash) + ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) { yield(payload) } + end + + def handle_expired_entry(entry, key, options) + if entry && entry.expired? + race_ttl = options[:race_condition_ttl].to_i + if (race_ttl > 0) && (Time.now.to_f - entry.expires_at <= race_ttl) + # When an entry has a positive :race_condition_ttl defined, put the stale entry back into the cache + # for a brief period while the entry is being recalculated. + entry.expires_at = Time.now.to_f + race_ttl + write_entry(key, entry, expires_in: race_ttl * 2) + else + delete_entry(key, **options) + end + entry = nil + end + entry + end + + def get_entry_value(entry, name, options) + instrument(:fetch_hit, name, options) { } + entry.value + end + + def save_block_result_to_cache(name, options) + result = instrument(:generate, name, options) do + yield(name) + end + + write(name, result, options) unless result.nil? && options[:skip_nil] + result + end + end + + module NullCoder # :nodoc: + extend self + + def dump(entry) + entry + end + + def dump_compressed(entry, threshold) + entry.compressed(threshold) + end + + def load(payload) + payload + end + end + + module Coders # :nodoc: + MARK_61 = "\x04\b".b.freeze # The one set by Marshal. + MARK_70_UNCOMPRESSED = "\x00".b.freeze + MARK_70_COMPRESSED = "\x01".b.freeze + + class << self + def [](version) + case version + when 6.1 + Rails61Coder + when 7.0 + Rails70Coder + else + raise ArgumentError, "Unknown ActiveSupport::Cache.format_version: #{Cache.format_version.inspect}" + end + end + end + + module Loader + extend self + + def load(payload) + if !payload.is_a?(String) + ActiveSupport::Cache::Store.logger&.warn %{Payload wasn't a string, was #{payload.class.name} - couldn't unmarshal, so returning nil."} + + return nil + elsif payload.start_with?(MARK_70_UNCOMPRESSED) + members = Marshal.load(payload.byteslice(1..-1)) + elsif payload.start_with?(MARK_70_COMPRESSED) + members = Marshal.load(Zlib::Inflate.inflate(payload.byteslice(1..-1))) + elsif payload.start_with?(MARK_61) + return Marshal.load(payload) + else + ActiveSupport::Cache::Store.logger&.warn %{Invalid cache prefix: #{payload.byteslice(0).inspect}, expected "\\x00" or "\\x01"} + + return nil + end + Entry.unpack(members) + end + end + + module Rails61Coder + include Loader + extend self + + def dump(entry) + Marshal.dump(entry) + end + + def dump_compressed(entry, threshold) + Marshal.dump(entry.compressed(threshold)) + end + end + + module Rails70Coder + include Loader + extend self + + def dump(entry) + MARK_70_UNCOMPRESSED + Marshal.dump(entry.pack) + end + + def dump_compressed(entry, threshold) + payload = Marshal.dump(entry.pack) + if payload.bytesize >= threshold + compressed_payload = Zlib::Deflate.deflate(payload) + if compressed_payload.bytesize < payload.bytesize + return MARK_70_COMPRESSED + compressed_payload + end + end + + MARK_70_UNCOMPRESSED + payload + end + end + end + + # This class is used to represent cache entries. Cache entries have a value, an optional + # expiration time, and an optional version. The expiration time is used to support the :race_condition_ttl option + # on the cache. The version is used to support the :version option on the cache for rejecting + # mismatches. + # + # Since cache entries in most instances will be serialized, the internals of this class are highly optimized + # using short instance variable names that are lazily defined. + class Entry # :nodoc: + class << self + def unpack(members) + new(members[0], expires_at: members[1], version: members[2]) + end + end + + attr_reader :version + + # Creates a new cache entry for the specified value. Options supported are + # +:compressed+, +:version+, +:expires_at+ and +:expires_in+. + def initialize(value, compressed: false, version: nil, expires_in: nil, expires_at: nil, **) + @value = value + @version = version + @created_at = 0.0 + @expires_in = expires_at&.to_f || expires_in && (expires_in.to_f + Time.now.to_f) + @compressed = true if compressed + end + + def value + compressed? ? uncompress(@value) : @value + end + + def mismatched?(version) + @version && version && @version != version + end + + # Checks if the entry is expired. The +expires_in+ parameter can override + # the value set when the entry was created. + def expired? + @expires_in && @created_at + @expires_in <= Time.now.to_f + end + + def expires_at + @expires_in ? @created_at + @expires_in : nil + end + + def expires_at=(value) + if value + @expires_in = value.to_f - @created_at + else + @expires_in = nil + end + end + + # Returns the size of the cached value. This could be less than + # value.bytesize if the data is compressed. + def bytesize + case value + when NilClass + 0 + when String + @value.bytesize + else + @s ||= Marshal.dump(@value).bytesize + end + end + + def compressed? # :nodoc: + defined?(@compressed) + end + + def compressed(compress_threshold) + return self if compressed? + + case @value + when nil, true, false, Numeric + uncompressed_size = 0 + when String + uncompressed_size = @value.bytesize + else + serialized = Marshal.dump(@value) + uncompressed_size = serialized.bytesize + end + + if uncompressed_size >= compress_threshold + serialized ||= Marshal.dump(@value) + compressed = Zlib::Deflate.deflate(serialized) + + if compressed.bytesize < uncompressed_size + return Entry.new(compressed, compressed: true, expires_at: expires_at, version: version) + end + end + self + end + + def local? + false + end + + # Duplicates the value in a class. This is used by cache implementations that don't natively + # serialize entries to protect against accidental cache modifications. + def dup_value! + if @value && !compressed? && !(@value.is_a?(Numeric) || @value == true || @value == false) + if @value.is_a?(String) + @value = @value.dup + else + @value = Marshal.load(Marshal.dump(@value)) + end + end + end + + def pack + members = [value, expires_at, version] + members.pop while !members.empty? && members.last.nil? + members + end + + private + def uncompress(value) + Marshal.load(Zlib::Inflate.inflate(value)) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache/file_store.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache/file_store.rb new file mode 100644 index 0000000..604ed82 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache/file_store.rb @@ -0,0 +1,202 @@ +# frozen_string_literal: true + +require "active_support/core_ext/file/atomic" +require "active_support/core_ext/string/conversions" +require "uri/common" + +module ActiveSupport + module Cache + # A cache store implementation which stores everything on the filesystem. + # + # FileStore implements the Strategy::LocalCache strategy which implements + # an in-memory cache inside of a block. + class FileStore < Store + attr_reader :cache_path + + DIR_FORMATTER = "%03X" + FILENAME_MAX_SIZE = 226 # max filename size on file system is 255, minus room for timestamp, pid, and random characters appended by Tempfile (used by atomic write) + FILEPATH_MAX_SIZE = 900 # max is 1024, plus some room + GITKEEP_FILES = [".gitkeep", ".keep"].freeze + + def initialize(cache_path, **options) + super(options) + @cache_path = cache_path.to_s + end + + # Advertise cache versioning support. + def self.supports_cache_versioning? + true + end + + # Deletes all items from the cache. In this case it deletes all the entries in the specified + # file store directory except for .keep or .gitkeep. Be careful which directory is specified in your + # config file when using +FileStore+ because everything in that directory will be deleted. + def clear(options = nil) + root_dirs = (Dir.children(cache_path) - GITKEEP_FILES) + FileUtils.rm_r(root_dirs.collect { |f| File.join(cache_path, f) }) + rescue Errno::ENOENT, Errno::ENOTEMPTY + end + + # Preemptively iterates through all stored keys and removes the ones which have expired. + def cleanup(options = nil) + options = merged_options(options) + search_dir(cache_path) do |fname| + entry = read_entry(fname, **options) + delete_entry(fname, **options) if entry && entry.expired? + end + end + + # Increments an already existing integer value that is stored in the cache. + # If the key is not found nothing is done. + def increment(name, amount = 1, options = nil) + modify_value(name, amount, options) + end + + # Decrements an already existing integer value that is stored in the cache. + # If the key is not found nothing is done. + def decrement(name, amount = 1, options = nil) + modify_value(name, -amount, options) + end + + def delete_matched(matcher, options = nil) + options = merged_options(options) + instrument(:delete_matched, matcher.inspect) do + matcher = key_matcher(matcher, options) + search_dir(cache_path) do |path| + key = file_path_key(path) + delete_entry(path, **options) if key.match(matcher) + end + end + end + + private + def read_entry(key, **options) + if payload = read_serialized_entry(key, **options) + entry = deserialize_entry(payload) + entry if entry.is_a?(Cache::Entry) + end + end + + def read_serialized_entry(key, **) + File.binread(key) if File.exist?(key) + rescue => error + logger.error("FileStoreError (#{error}): #{error.message}") if logger + nil + end + + def write_entry(key, entry, **options) + write_serialized_entry(key, serialize_entry(entry, **options), **options) + end + + def write_serialized_entry(key, payload, **options) + return false if options[:unless_exist] && File.exist?(key) + ensure_cache_path(File.dirname(key)) + File.atomic_write(key, cache_path) { |f| f.write(payload) } + true + end + + def delete_entry(key, **options) + if File.exist?(key) + begin + File.delete(key) + delete_empty_directories(File.dirname(key)) + true + rescue + # Just in case the error was caused by another process deleting the file first. + raise if File.exist?(key) + false + end + end + end + + # Lock a file for a block so only one process can modify it at a time. + def lock_file(file_name, &block) + if File.exist?(file_name) + File.open(file_name, "r+") do |f| + f.flock File::LOCK_EX + yield + ensure + f.flock File::LOCK_UN + end + else + yield + end + end + + # Translate a key into a file path. + def normalize_key(key, options) + key = super + fname = URI.encode_www_form_component(key) + + if fname.size > FILEPATH_MAX_SIZE + fname = ActiveSupport::Digest.hexdigest(key) + end + + hash = Zlib.adler32(fname) + hash, dir_1 = hash.divmod(0x1000) + dir_2 = hash.modulo(0x1000) + + # Make sure file name doesn't exceed file system limits. + if fname.length < FILENAME_MAX_SIZE + fname_paths = fname + else + fname_paths = [] + begin + fname_paths << fname[0, FILENAME_MAX_SIZE] + fname = fname[FILENAME_MAX_SIZE..-1] + end until fname.blank? + end + + File.join(cache_path, DIR_FORMATTER % dir_1, DIR_FORMATTER % dir_2, fname_paths) + end + + # Translate a file path into a key. + def file_path_key(path) + fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last + URI.decode_www_form_component(fname, Encoding::UTF_8) + end + + # Delete empty directories in the cache. + def delete_empty_directories(dir) + return if File.realpath(dir) == File.realpath(cache_path) + if Dir.children(dir).empty? + Dir.delete(dir) rescue nil + delete_empty_directories(File.dirname(dir)) + end + end + + # Make sure a file path's directories exist. + def ensure_cache_path(path) + FileUtils.makedirs(path) unless File.exist?(path) + end + + def search_dir(dir, &callback) + return if !File.exist?(dir) + Dir.each_child(dir) do |d| + name = File.join(dir, d) + if File.directory?(name) + search_dir(name, &callback) + else + callback.call name + end + end + end + + # Modifies the amount of an already existing integer value that is stored in the cache. + # If the key is not found nothing is done. + def modify_value(name, amount, options) + file_name = normalize_key(name, options) + + lock_file(file_name) do + options = merged_options(options) + + if num = read(name, options) + num = num.to_i + amount + write(name, num, options) + num + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache/mem_cache_store.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache/mem_cache_store.rb new file mode 100644 index 0000000..c41d724 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache/mem_cache_store.rb @@ -0,0 +1,324 @@ +# frozen_string_literal: true + +begin + require "dalli" +rescue LoadError => e + $stderr.puts "You don't have dalli installed in your application. Please add it to your Gemfile and run bundle install" + raise e +end + +require "delegate" +require "active_support/core_ext/enumerable" +require "active_support/core_ext/array/extract_options" + +module ActiveSupport + module Cache + # A cache store implementation which stores data in Memcached: + # https://memcached.org + # + # This is currently the most popular cache store for production websites. + # + # Special features: + # - Clustering and load balancing. One can specify multiple memcached servers, + # and MemCacheStore will load balance between all available servers. If a + # server goes down, then MemCacheStore will ignore it until it comes back up. + # + # MemCacheStore implements the Strategy::LocalCache strategy which implements + # an in-memory cache inside of a block. + class MemCacheStore < Store + # Advertise cache versioning support. + def self.supports_cache_versioning? + true + end + + prepend Strategy::LocalCache + + module DupLocalCache + class DupLocalStore < DelegateClass(Strategy::LocalCache::LocalStore) + def write_entry(_key, entry) + if entry.is_a?(Entry) + entry.dup_value! + end + super + end + + def fetch_entry(key) + entry = super do + new_entry = yield + if entry.is_a?(Entry) + new_entry.dup_value! + end + new_entry + end + entry = entry.dup + + if entry.is_a?(Entry) + entry.dup_value! + end + + entry + end + end + + private + def local_cache + if ActiveSupport::Cache.format_version == 6.1 + if local_cache = super + DupLocalStore.new(local_cache) + end + else + super + end + end + end + prepend DupLocalCache + + ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n + + # Creates a new Dalli::Client instance with specified addresses and options. + # If no addresses are provided, we give nil to Dalli::Client, so it uses its fallbacks: + # - ENV["MEMCACHE_SERVERS"] (if defined) + # - "127.0.0.1:11211" (otherwise) + # + # ActiveSupport::Cache::MemCacheStore.build_mem_cache + # # => # + # ActiveSupport::Cache::MemCacheStore.build_mem_cache('localhost:10290') + # # => # + def self.build_mem_cache(*addresses) # :nodoc: + addresses = addresses.flatten + options = addresses.extract_options! + addresses = nil if addresses.compact.empty? + pool_options = retrieve_pool_options(options) + + if pool_options.empty? + Dalli::Client.new(addresses, options) + else + ensure_connection_pool_added! + ConnectionPool.new(pool_options) { Dalli::Client.new(addresses, options.merge(threadsafe: false)) } + end + end + + # Creates a new MemCacheStore object, with the given memcached server + # addresses. Each address is either a host name, or a host-with-port string + # in the form of "host_name:port". For example: + # + # ActiveSupport::Cache::MemCacheStore.new("localhost", "server-downstairs.localnetwork:8229") + # + # If no addresses are provided, but ENV['MEMCACHE_SERVERS'] is defined, it will be used instead. Otherwise, + # MemCacheStore will connect to localhost:11211 (the default memcached port). + def initialize(*addresses) + addresses = addresses.flatten + options = addresses.extract_options! + if options.key?(:cache_nils) + options[:skip_nil] = !options.delete(:cache_nils) + end + super(options) + + unless [String, Dalli::Client, NilClass].include?(addresses.first.class) + raise ArgumentError, "First argument must be an empty array, an array of hosts or a Dalli::Client instance." + end + if addresses.first.is_a?(Dalli::Client) + @data = addresses.first + else + mem_cache_options = options.dup + # The value "compress: false" prevents duplicate compression within Dalli. + mem_cache_options[:compress] = false + (UNIVERSAL_OPTIONS - %i(compress)).each { |name| mem_cache_options.delete(name) } + @data = self.class.build_mem_cache(*(addresses + [mem_cache_options])) + end + end + + ## + # :method: write + # :call-seq: write(name, value, options = nil) + # + # Behaves the same as ActiveSupport::Cache::Store#write, but supports + # additional options specific to memcached. + # + # ==== Additional Options + # + # * raw: true - Sends the value directly to the server as raw + # bytes. The value must be a string or number. You can use memcached + # direct operations like +increment+ and +decrement+ only on raw values. + # + # * unless_exist: true - Prevents overwriting an existing cache + # entry. + + # Increment a cached value. This method uses the memcached incr atomic + # operator and can only be used on values written with the +:raw+ option. + # Calling it on a value not stored with +:raw+ will initialize that value + # to zero. + def increment(name, amount = 1, options = nil) + options = merged_options(options) + instrument(:increment, name, amount: amount) do + rescue_error_with nil do + @data.with { |c| c.incr(normalize_key(name, options), amount, options[:expires_in]) } + end + end + end + + # Decrement a cached value. This method uses the memcached decr atomic + # operator and can only be used on values written with the +:raw+ option. + # Calling it on a value not stored with +:raw+ will initialize that value + # to zero. + def decrement(name, amount = 1, options = nil) + options = merged_options(options) + instrument(:decrement, name, amount: amount) do + rescue_error_with nil do + @data.with { |c| c.decr(normalize_key(name, options), amount, options[:expires_in]) } + end + end + end + + # Clear the entire cache on all memcached servers. This method should + # be used with care when shared cache is being used. + def clear(options = nil) + rescue_error_with(nil) { @data.with { |c| c.flush_all } } + end + + # Get the statistics from the memcached servers. + def stats + @data.with { |c| c.stats } + end + + private + module Coders # :nodoc: + class << self + def [](version) + case version + when 6.1 + Rails61Coder + when 7.0 + Rails70Coder + else + raise ArgumentError, "Unknown ActiveSupport::Cache.format_version #{Cache.format_version.inspect}" + end + end + end + + module Loader + def load(payload) + if payload.is_a?(Entry) + payload + else + Cache::Coders::Loader.load(payload) + end + end + end + + module Rails61Coder + include Loader + extend self + + def dump(entry) + entry + end + + def dump_compressed(entry, threshold) + entry.compressed(threshold) + end + end + + module Rails70Coder + include Cache::Coders::Rails70Coder + include Loader + extend self + end + end + + def default_coder + Coders[Cache.format_version] + end + + # Read an entry from the cache. + def read_entry(key, **options) + deserialize_entry(read_serialized_entry(key, **options), **options) + end + + def read_serialized_entry(key, **options) + rescue_error_with(nil) do + @data.with { |c| c.get(key, options) } + end + end + + # Write an entry to the cache. + def write_entry(key, entry, **options) + write_serialized_entry(key, serialize_entry(entry, **options), **options) + end + + def write_serialized_entry(key, payload, **options) + method = options[:unless_exist] ? :add : :set + expires_in = options[:expires_in].to_i + if options[:race_condition_ttl] && expires_in > 0 && !options[:raw] + # Set the memcache expire a few minutes in the future to support race condition ttls on read + expires_in += 5.minutes + end + rescue_error_with false do + # Don't pass compress option to Dalli since we are already dealing with compression. + options.delete(:compress) + @data.with { |c| c.send(method, key, payload, expires_in, **options) } + end + end + + # Reads multiple entries from the cache implementation. + def read_multi_entries(names, **options) + keys_to_names = names.index_by { |name| normalize_key(name, options) } + + raw_values = @data.with { |c| c.get_multi(keys_to_names.keys) } + values = {} + + raw_values.each do |key, value| + entry = deserialize_entry(value, raw: options[:raw]) + + unless entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options)) + values[keys_to_names[key]] = entry.value + end + end + + values + end + + # Delete an entry from the cache. + def delete_entry(key, **options) + rescue_error_with(false) { @data.with { |c| c.delete(key) } } + end + + def serialize_entry(entry, raw: false, **options) + if raw + entry.value.to_s + else + super(entry, raw: raw, **options) + end + end + + # Memcache keys are binaries. So we need to force their encoding to binary + # before applying the regular expression to ensure we are escaping all + # characters properly. + def normalize_key(key, options) + key = super + if key + key = key.dup.force_encoding(Encoding::ASCII_8BIT) + key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" } + key = "#{key[0, 212]}:hash:#{ActiveSupport::Digest.hexdigest(key)}" if key.size > 250 + end + key + end + + def deserialize_entry(payload, raw: false, **) + if payload && raw + Entry.new(payload) + else + super(payload) + end + end + + def rescue_error_with(fallback) + yield + rescue Dalli::DalliError => error + ActiveSupport.error_reporter&.report(error, handled: true, severity: :warning) + logger.error("DalliError (#{error}): #{error.message}") if logger + fallback + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache/memory_store.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache/memory_store.rb new file mode 100644 index 0000000..f03edef --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache/memory_store.rb @@ -0,0 +1,203 @@ +# frozen_string_literal: true + +require "monitor" + +module ActiveSupport + module Cache + # A cache store implementation which stores everything into memory in the + # same process. If you're running multiple Ruby on Rails server processes + # (which is the case if you're using Phusion Passenger or puma clustered mode), + # then this means that Rails server process instances won't be able + # to share cache data with each other and this may not be the most + # appropriate cache in that scenario. + # + # This cache has a bounded size specified by the +:size+ options to the + # initializer (default is 32Mb). When the cache exceeds the allotted size, + # a cleanup will occur which tries to prune the cache down to three quarters + # of the maximum size by removing the least recently used entries. + # + # Unlike other Cache store implementations, MemoryStore does not compress + # values by default. MemoryStore does not benefit from compression as much + # as other Store implementations, as it does not send data over a network. + # However, when compression is enabled, it still pays the full cost of + # compression in terms of cpu use. + # + # MemoryStore is thread-safe. + class MemoryStore < Store + module DupCoder # :nodoc: + extend self + + def dump(entry) + entry.dup_value! unless entry.compressed? + entry + end + + def dump_compressed(entry, threshold) + entry = entry.compressed(threshold) + entry.dup_value! unless entry.compressed? + entry + end + + def load(entry) + entry = entry.dup + entry.dup_value! + entry + end + end + + def initialize(options = nil) + options ||= {} + # Disable compression by default. + options[:compress] ||= false + super(options) + @data = {} + @max_size = options[:size] || 32.megabytes + @max_prune_time = options[:max_prune_time] || 2 + @cache_size = 0 + @monitor = Monitor.new + @pruning = false + end + + # Advertise cache versioning support. + def self.supports_cache_versioning? + true + end + + # Delete all data stored in a given cache store. + def clear(options = nil) + synchronize do + @data.clear + @cache_size = 0 + end + end + + # Preemptively iterates through all stored keys and removes the ones which have expired. + def cleanup(options = nil) + options = merged_options(options) + instrument(:cleanup, size: @data.size) do + keys = synchronize { @data.keys } + keys.each do |key| + entry = @data[key] + delete_entry(key, **options) if entry && entry.expired? + end + end + end + + # To ensure entries fit within the specified memory prune the cache by removing the least + # recently accessed entries. + def prune(target_size, max_time = nil) + return if pruning? + @pruning = true + begin + start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + cleanup + instrument(:prune, target_size, from: @cache_size) do + keys = synchronize { @data.keys } + keys.each do |key| + delete_entry(key, **options) + return if @cache_size <= target_size || (max_time && Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time > max_time) + end + end + ensure + @pruning = false + end + end + + # Returns true if the cache is currently being pruned. + def pruning? + @pruning + end + + # Increment an integer value in the cache. + def increment(name, amount = 1, options = nil) + modify_value(name, amount, options) + end + + # Decrement an integer value in the cache. + def decrement(name, amount = 1, options = nil) + modify_value(name, -amount, options) + end + + # Deletes cache entries if the cache key matches a given pattern. + def delete_matched(matcher, options = nil) + options = merged_options(options) + instrument(:delete_matched, matcher.inspect) do + matcher = key_matcher(matcher, options) + keys = synchronize { @data.keys } + keys.each do |key| + delete_entry(key, **options) if key.match(matcher) + end + end + end + + def inspect # :nodoc: + "#<#{self.class.name} entries=#{@data.size}, size=#{@cache_size}, options=#{@options.inspect}>" + end + + # Synchronize calls to the cache. This should be called wherever the underlying cache implementation + # is not thread safe. + def synchronize(&block) # :nodoc: + @monitor.synchronize(&block) + end + + private + PER_ENTRY_OVERHEAD = 240 + + def default_coder + DupCoder + end + + def cached_size(key, payload) + key.to_s.bytesize + payload.bytesize + PER_ENTRY_OVERHEAD + end + + def read_entry(key, **options) + entry = nil + synchronize do + payload = @data.delete(key) + if payload + @data[key] = payload + entry = deserialize_entry(payload) + end + end + entry + end + + def write_entry(key, entry, **options) + payload = serialize_entry(entry, **options) + synchronize do + return false if options[:unless_exist] && @data.key?(key) + + old_payload = @data[key] + if old_payload + @cache_size -= (old_payload.bytesize - payload.bytesize) + else + @cache_size += cached_size(key, payload) + end + @data[key] = payload + prune(@max_size * 0.75, @max_prune_time) if @cache_size > @max_size + true + end + end + + def delete_entry(key, **options) + synchronize do + payload = @data.delete(key) + @cache_size -= cached_size(key, payload) if payload + !!payload + end + end + + def modify_value(name, amount, options) + options = merged_options(options) + synchronize do + if num = read(name, options) + num = num.to_i + amount + write(name, num, options) + num + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache/null_store.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache/null_store.rb new file mode 100644 index 0000000..e840b26 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache/null_store.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +module ActiveSupport + module Cache + # A cache store implementation which doesn't actually store anything. Useful in + # development and test environments where you don't want caching turned on but + # need to go through the caching interface. + # + # This cache does implement the local cache strategy, so values will actually + # be cached inside blocks that utilize this strategy. See + # ActiveSupport::Cache::Strategy::LocalCache for more details. + class NullStore < Store + prepend Strategy::LocalCache + + # Advertise cache versioning support. + def self.supports_cache_versioning? + true + end + + def clear(options = nil) + end + + def cleanup(options = nil) + end + + def increment(name, amount = 1, options = nil) + end + + def decrement(name, amount = 1, options = nil) + end + + def delete_matched(matcher, options = nil) + end + + private + def read_entry(key, **s) + deserialize_entry(read_serialized_entry(key)) + end + + def read_serialized_entry(_key, **) + end + + def write_entry(key, entry, **) + write_serialized_entry(key, serialize_entry(entry)) + end + + def write_serialized_entry(_key, _payload, **) + true + end + + def delete_entry(key, **options) + false + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache/redis_cache_store.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache/redis_cache_store.rb new file mode 100644 index 0000000..4a5a28a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache/redis_cache_store.rb @@ -0,0 +1,474 @@ +# frozen_string_literal: true + +begin + gem "redis", ">= 4.0.1" + require "redis" + require "redis/distributed" +rescue LoadError + warn "The Redis cache store requires the redis gem, version 4.0.1 or later. Please add it to your Gemfile: `gem \"redis\", \">= 4.0.1\"`" + raise +end + +# Prefer the hiredis driver but don't require it. +begin + if ::Redis::VERSION < "5" + require "redis/connection/hiredis" + else + require "hiredis-client" + end +rescue LoadError +end + +require "active_support/digest" + +module ActiveSupport + module Cache + module ConnectionPoolLike + def with + yield self + end + end + + ::Redis.include(ConnectionPoolLike) + ::Redis::Distributed.include(ConnectionPoolLike) + + # Redis cache store. + # + # Deployment note: Take care to use a *dedicated Redis cache* rather + # than pointing this at your existing Redis server. It won't cope well + # with mixed usage patterns and it won't expire cache entries by default. + # + # Redis cache server setup guide: https://redis.io/topics/lru-cache + # + # * Supports vanilla Redis, hiredis, and Redis::Distributed. + # * Supports Memcached-like sharding across Redises with Redis::Distributed. + # * Fault tolerant. If the Redis server is unavailable, no exceptions are + # raised. Cache fetches are all misses and writes are dropped. + # * Local cache. Hot in-memory primary cache within block/middleware scope. + # * +read_multi+ and +write_multi+ support for Redis mget/mset. Use Redis::Distributed + # 4.0.1+ for distributed mget support. + # * +delete_matched+ support for Redis KEYS globs. + class RedisCacheStore < Store + # Keys are truncated with the ActiveSupport digest if they exceed 1kB + MAX_KEY_BYTESIZE = 1024 + + DEFAULT_REDIS_OPTIONS = { + connect_timeout: 20, + read_timeout: 1, + write_timeout: 1, + reconnect_attempts: 0, + } + + DEFAULT_ERROR_HANDLER = -> (method:, returning:, exception:) do + if logger + logger.error { "RedisCacheStore: #{method} failed, returned #{returning.inspect}: #{exception.class}: #{exception.message}" } + end + end + + # The maximum number of entries to receive per SCAN call. + SCAN_BATCH_SIZE = 1000 + private_constant :SCAN_BATCH_SIZE + + # Advertise cache versioning support. + def self.supports_cache_versioning? + true + end + + prepend Strategy::LocalCache + + class << self + # Factory method to create a new Redis instance. + # + # Handles four options: :redis block, :redis instance, single :url + # string, and multiple :url strings. + # + # Option Class Result + # :redis Proc -> options[:redis].call + # :redis Object -> options[:redis] + # :url String -> Redis.new(url: …) + # :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …]) + # + def build_redis(redis: nil, url: nil, **redis_options) # :nodoc: + urls = Array(url) + + if redis.is_a?(Proc) + redis.call + elsif redis + redis + elsif urls.size > 1 + build_redis_distributed_client(urls: urls, **redis_options) + elsif urls.empty? + build_redis_client(**redis_options) + else + build_redis_client(url: urls.first, **redis_options) + end + end + + private + def build_redis_distributed_client(urls:, **redis_options) + ::Redis::Distributed.new([], DEFAULT_REDIS_OPTIONS.merge(redis_options)).tap do |dist| + urls.each { |u| dist.add_node url: u } + end + end + + def build_redis_client(**redis_options) + ::Redis.new(DEFAULT_REDIS_OPTIONS.merge(redis_options)) + end + end + + attr_reader :redis_options + attr_reader :max_key_bytesize + + # Creates a new Redis cache store. + # + # Handles four options: :redis block, :redis instance, single :url + # string, and multiple :url strings. + # + # Option Class Result + # :redis Proc -> options[:redis].call + # :redis Object -> options[:redis] + # :url String -> Redis.new(url: …) + # :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …]) + # + # No namespace is set by default. Provide one if the Redis cache + # server is shared with other apps: namespace: 'myapp-cache'. + # + # Compression is enabled by default with a 1kB threshold, so cached + # values larger than 1kB are automatically compressed. Disable by + # passing compress: false or change the threshold by passing + # compress_threshold: 4.kilobytes. + # + # No expiry is set on cache entries by default. Redis is expected to + # be configured with an eviction policy that automatically deletes + # least-recently or -frequently used keys when it reaches max memory. + # See https://redis.io/topics/lru-cache for cache server setup. + # + # Race condition TTL is not set by default. This can be used to avoid + # "thundering herd" cache writes when hot cache entries are expired. + # See ActiveSupport::Cache::Store#fetch for more. + def initialize(namespace: nil, compress: true, compress_threshold: 1.kilobyte, coder: default_coder, expires_in: nil, race_condition_ttl: nil, error_handler: DEFAULT_ERROR_HANDLER, **redis_options) + @redis_options = redis_options + + @max_key_bytesize = MAX_KEY_BYTESIZE + @error_handler = error_handler + + super namespace: namespace, + compress: compress, compress_threshold: compress_threshold, + expires_in: expires_in, race_condition_ttl: race_condition_ttl, + coder: coder + end + + def redis + @redis ||= begin + pool_options = self.class.send(:retrieve_pool_options, redis_options) + + if pool_options.any? + self.class.send(:ensure_connection_pool_added!) + ::ConnectionPool.new(pool_options) { self.class.build_redis(**redis_options) } + else + self.class.build_redis(**redis_options) + end + end + end + + def inspect + instance = @redis || @redis_options + "#<#{self.class} options=#{options.inspect} redis=#{instance.inspect}>" + end + + # Cache Store API implementation. + # + # Read multiple values at once. Returns a hash of requested keys -> + # fetched values. + def read_multi(*names) + if mget_capable? + instrument(:read_multi, names, options) do |payload| + read_multi_mget(*names).tap do |results| + payload[:hits] = results.keys + end + end + else + super + end + end + + # Cache Store API implementation. + # + # Supports Redis KEYS glob patterns: + # + # h?llo matches hello, hallo and hxllo + # h*llo matches hllo and heeeello + # h[ae]llo matches hello and hallo, but not hillo + # h[^e]llo matches hallo, hbllo, ... but not hello + # h[a-b]llo matches hallo and hbllo + # + # Use \ to escape special characters if you want to match them verbatim. + # + # See https://redis.io/commands/KEYS for more. + # + # Failsafe: Raises errors. + def delete_matched(matcher, options = nil) + instrument :delete_matched, matcher do + unless String === matcher + raise ArgumentError, "Only Redis glob strings are supported: #{matcher.inspect}" + end + redis.with do |c| + pattern = namespace_key(matcher, options) + cursor = "0" + # Fetch keys in batches using SCAN to avoid blocking the Redis server. + nodes = c.respond_to?(:nodes) ? c.nodes : [c] + + nodes.each do |node| + begin + cursor, keys = node.scan(cursor, match: pattern, count: SCAN_BATCH_SIZE) + node.del(*keys) unless keys.empty? + end until cursor == "0" + end + end + end + end + + # Cache Store API implementation. + # + # Increment a cached value. This method uses the Redis incr atomic + # operator and can only be used on values written with the +:raw+ option. + # Calling it on a value not stored with +:raw+ will initialize that value + # to zero. + # + # Failsafe: Raises errors. + def increment(name, amount = 1, options = nil) + instrument :increment, name, amount: amount do + failsafe :increment do + options = merged_options(options) + key = normalize_key(name, options) + + redis.with do |c| + c.incrby(key, amount).tap do + write_key_expiry(c, key, options) + end + end + end + end + end + + # Cache Store API implementation. + # + # Decrement a cached value. This method uses the Redis decr atomic + # operator and can only be used on values written with the +:raw+ option. + # Calling it on a value not stored with +:raw+ will initialize that value + # to zero. + # + # Failsafe: Raises errors. + def decrement(name, amount = 1, options = nil) + instrument :decrement, name, amount: amount do + failsafe :decrement do + options = merged_options(options) + key = normalize_key(name, options) + + redis.with do |c| + c.decrby(key, amount).tap do + write_key_expiry(c, key, options) + end + end + end + end + end + + # Cache Store API implementation. + # + # Removes expired entries. Handled natively by Redis least-recently-/ + # least-frequently-used expiry, so manual cleanup is not supported. + def cleanup(options = nil) + super + end + + # Clear the entire cache on all Redis servers. Safe to use on + # shared servers if the cache is namespaced. + # + # Failsafe: Raises errors. + def clear(options = nil) + failsafe :clear do + if namespace = merged_options(options)[:namespace] + delete_matched "*", namespace: namespace + else + redis.with { |c| c.flushdb } + end + end + end + + # Get info from redis servers. + def stats + redis.with { |c| c.info } + end + + def mget_capable? # :nodoc: + set_redis_capabilities unless defined? @mget_capable + @mget_capable + end + + def mset_capable? # :nodoc: + set_redis_capabilities unless defined? @mset_capable + @mset_capable + end + + private + def set_redis_capabilities + case redis + when Redis::Distributed + @mget_capable = true + @mset_capable = false + else + @mget_capable = true + @mset_capable = true + end + end + + # Store provider interface: + # Read an entry from the cache. + def read_entry(key, **options) + deserialize_entry(read_serialized_entry(key, **options), **options) + end + + def read_serialized_entry(key, raw: false, **options) + failsafe :read_entry do + redis.with { |c| c.get(key) } + end + end + + def read_multi_entries(names, **options) + if mget_capable? + read_multi_mget(*names, **options) + else + super + end + end + + def read_multi_mget(*names) + options = names.extract_options! + options = merged_options(options) + return {} if names == [] + raw = options&.fetch(:raw, false) + + keys = names.map { |name| normalize_key(name, options) } + + values = failsafe(:read_multi_mget, returning: {}) do + redis.with { |c| c.mget(*keys) } + end + + names.zip(values).each_with_object({}) do |(name, value), results| + if value + entry = deserialize_entry(value, raw: raw) + unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(name, options)) + results[name] = entry.value + end + end + end + end + + # Write an entry to the cache. + # + # Requires Redis 2.6.12+ for extended SET options. + def write_entry(key, entry, raw: false, **options) + write_serialized_entry(key, serialize_entry(entry, raw: raw, **options), raw: raw, **options) + end + + def write_serialized_entry(key, payload, raw: false, unless_exist: false, expires_in: nil, race_condition_ttl: nil, **options) + # If race condition TTL is in use, ensure that cache entries + # stick around a bit longer after they would have expired + # so we can purposefully serve stale entries. + if race_condition_ttl && expires_in && expires_in > 0 && !raw + expires_in += 5.minutes + end + + modifiers = {} + if unless_exist || expires_in + modifiers[:nx] = unless_exist + modifiers[:px] = (1000 * expires_in.to_f).ceil if expires_in + end + + failsafe :write_entry, returning: false do + redis.with { |c| c.set key, payload, **modifiers } + end + end + + def write_key_expiry(client, key, options) + if options[:expires_in] && client.ttl(key).negative? + client.expire key, options[:expires_in].to_i + end + end + + # Delete an entry from the cache. + def delete_entry(key, options) + failsafe :delete_entry, returning: false do + redis.with { |c| c.del key } + end + end + + # Deletes multiple entries in the cache. Returns the number of entries deleted. + def delete_multi_entries(entries, **_options) + redis.with { |c| c.del(entries) } + end + + # Nonstandard store provider API to write multiple values at once. + def write_multi_entries(entries, expires_in: nil, **options) + if entries.any? + if mset_capable? && expires_in.nil? + failsafe :write_multi_entries do + payload = serialize_entries(entries, **options) + redis.with do |c| + c.mapped_mset(payload) + end + end + else + super + end + end + end + + # Truncate keys that exceed 1kB. + def normalize_key(key, options) + truncate_key super&.b + end + + def truncate_key(key) + if key && key.bytesize > max_key_bytesize + suffix = ":hash:#{ActiveSupport::Digest.hexdigest(key)}" + truncate_at = max_key_bytesize - suffix.bytesize + "#{key.byteslice(0, truncate_at)}#{suffix}" + else + key + end + end + + def deserialize_entry(payload, raw: false, **) + if raw && !payload.nil? + Entry.new(payload) + else + super(payload) + end + end + + def serialize_entry(entry, raw: false, **options) + if raw + entry.value.to_s + else + super(entry, raw: raw, **options) + end + end + + def serialize_entries(entries, **options) + entries.transform_values do |entry| + serialize_entry(entry, **options) + end + end + + def failsafe(method, returning: nil) + yield + rescue ::Redis::BaseError => error + ActiveSupport.error_reporter&.report(error, handled: true, severity: :warning) + @error_handler&.call(method: method, exception: error, returning: returning) + returning + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache/strategy/local_cache.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache/strategy/local_cache.rb new file mode 100644 index 0000000..c89cacd --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache/strategy/local_cache.rb @@ -0,0 +1,186 @@ +# frozen_string_literal: true + +require "active_support/core_ext/string/inflections" + +module ActiveSupport + module Cache + module Strategy + # Caches that implement LocalCache will be backed by an in-memory cache for the + # duration of a block. Repeated calls to the cache for the same key will hit the + # in-memory cache for faster access. + module LocalCache + autoload :Middleware, "active_support/cache/strategy/local_cache_middleware" + + # Class for storing and registering the local caches. + module LocalCacheRegistry # :nodoc: + extend self + + def cache_for(local_cache_key) + registry = ActiveSupport::IsolatedExecutionState[:active_support_local_cache_registry] ||= {} + registry[local_cache_key] + end + + def set_cache_for(local_cache_key, value) + registry = ActiveSupport::IsolatedExecutionState[:active_support_local_cache_registry] ||= {} + registry[local_cache_key] = value + end + end + + # Simple memory backed cache. This cache is not thread safe and is intended only + # for serving as a temporary memory cache for a single thread. + class LocalStore + def initialize + @data = {} + end + + def clear(options = nil) + @data.clear + end + + def read_entry(key) + @data[key] + end + + def read_multi_entries(keys) + @data.slice(*keys) + end + + def write_entry(key, entry) + @data[key] = entry + true + end + + def delete_entry(key) + !!@data.delete(key) + end + + def fetch_entry(key) # :nodoc: + @data.fetch(key) { @data[key] = yield } + end + end + + # Use a local cache for the duration of block. + def with_local_cache(&block) + use_temporary_local_cache(LocalStore.new, &block) + end + + # Middleware class can be inserted as a Rack handler to be local cache for the + # duration of request. + def middleware + @middleware ||= Middleware.new( + "ActiveSupport::Cache::Strategy::LocalCache", + local_cache_key) + end + + def clear(**options) # :nodoc: + return super unless cache = local_cache + cache.clear(options) + super + end + + def cleanup(**options) # :nodoc: + return super unless cache = local_cache + cache.clear + super + end + + def delete_matched(matcher, options = nil) # :nodoc: + return super unless cache = local_cache + cache.clear + super + end + + def increment(name, amount = 1, **options) # :nodoc: + return super unless local_cache + value = bypass_local_cache { super } + write_cache_value(name, value, raw: true, **options) + value + end + + def decrement(name, amount = 1, **options) # :nodoc: + return super unless local_cache + value = bypass_local_cache { super } + write_cache_value(name, value, raw: true, **options) + value + end + + private + def read_serialized_entry(key, raw: false, **options) + if cache = local_cache + hit = true + entry = cache.fetch_entry(key) do + hit = false + super + end + options[:event][:store] = cache.class.name if hit && options[:event] + entry + else + super + end + end + + def read_multi_entries(keys, **options) + return super unless local_cache + + local_entries = local_cache.read_multi_entries(keys) + local_entries.transform_values! do |payload| + deserialize_entry(payload).value + end + missed_keys = keys - local_entries.keys + + if missed_keys.any? + local_entries.merge!(super(missed_keys, **options)) + else + local_entries + end + end + + def write_serialized_entry(key, payload, **) + if return_value = super + local_cache.write_entry(key, payload) if local_cache + else + local_cache.delete_entry(key) if local_cache + end + return_value + end + + def delete_entry(key, **) + local_cache.delete_entry(key) if local_cache + super + end + + def write_cache_value(name, value, **options) + name = normalize_key(name, options) + cache = local_cache + if value + cache.write_entry(name, serialize_entry(new_entry(value, **options), **options)) + else + cache.delete_entry(name) + end + end + + def local_cache_key + @local_cache_key ||= "#{self.class.name.underscore}_local_cache_#{object_id}".gsub(/[\/-]/, "_").to_sym + end + + def local_cache + LocalCacheRegistry.cache_for(local_cache_key) + end + + def bypass_local_cache(&block) + use_temporary_local_cache(nil, &block) + end + + def use_temporary_local_cache(temporary_cache) + save_cache = LocalCacheRegistry.cache_for(local_cache_key) + begin + LocalCacheRegistry.set_cache_for(local_cache_key, temporary_cache) + yield + ensure + LocalCacheRegistry.set_cache_for(local_cache_key, save_cache) + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache/strategy/local_cache_middleware.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache/strategy/local_cache_middleware.rb new file mode 100644 index 0000000..62542bd --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/cache/strategy/local_cache_middleware.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require "rack/body_proxy" +require "rack/utils" + +module ActiveSupport + module Cache + module Strategy + module LocalCache + #-- + # This class wraps up local storage for middlewares. Only the middleware method should + # construct them. + class Middleware # :nodoc: + attr_reader :name, :local_cache_key + + def initialize(name, local_cache_key) + @name = name + @local_cache_key = local_cache_key + @app = nil + end + + def new(app) + @app = app + self + end + + def call(env) + LocalCacheRegistry.set_cache_for(local_cache_key, LocalStore.new) + response = @app.call(env) + response[2] = ::Rack::BodyProxy.new(response[2]) do + LocalCacheRegistry.set_cache_for(local_cache_key, nil) + end + cleanup_on_body_close = true + response + rescue Rack::Utils::InvalidParameterError + [400, {}, []] + ensure + LocalCacheRegistry.set_cache_for(local_cache_key, nil) unless + cleanup_on_body_close + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/callbacks.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/callbacks.rb new file mode 100644 index 0000000..4c4bc9f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/callbacks.rb @@ -0,0 +1,961 @@ +# frozen_string_literal: true + +require "active_support/concern" +require "active_support/descendants_tracker" +require "active_support/core_ext/array/extract_options" +require "active_support/core_ext/class/attribute" +require "active_support/core_ext/string/filters" +require "active_support/core_ext/object/blank" +require "thread" + +module ActiveSupport + # Callbacks are code hooks that are run at key points in an object's life cycle. + # The typical use case is to have a base class define a set of callbacks + # relevant to the other functionality it supplies, so that subclasses can + # install callbacks that enhance or modify the base functionality without + # needing to override or redefine methods of the base class. + # + # Mixing in this module allows you to define the events in the object's + # life cycle that will support callbacks (via ClassMethods#define_callbacks), + # set the instance methods, procs, or callback objects to be called (via + # ClassMethods#set_callback), and run the installed callbacks at the + # appropriate times (via +run_callbacks+). + # + # By default callbacks are halted by throwing +:abort+. + # See ClassMethods#define_callbacks for details. + # + # Three kinds of callbacks are supported: before callbacks, run before a + # certain event; after callbacks, run after the event; and around callbacks, + # blocks that surround the event, triggering it when they yield. Callback code + # can be contained in instance methods, procs or lambdas, or callback objects + # that respond to certain predetermined methods. See ClassMethods#set_callback + # for details. + # + # class Record + # include ActiveSupport::Callbacks + # define_callbacks :save + # + # def save + # run_callbacks :save do + # puts "- save" + # end + # end + # end + # + # class PersonRecord < Record + # set_callback :save, :before, :saving_message + # def saving_message + # puts "saving..." + # end + # + # set_callback :save, :after do |object| + # puts "saved" + # end + # end + # + # person = PersonRecord.new + # person.save + # + # Output: + # saving... + # - save + # saved + module Callbacks + extend Concern + + included do + extend ActiveSupport::DescendantsTracker + class_attribute :__callbacks, instance_writer: false, default: {} + end + + CALLBACK_FILTER_TYPES = [:before, :after, :around] + + # Runs the callbacks for the given event. + # + # Calls the before and around callbacks in the order they were set, yields + # the block (if given one), and then runs the after callbacks in reverse + # order. + # + # If the callback chain was halted, returns +false+. Otherwise returns the + # result of the block, +nil+ if no callbacks have been set, or +true+ + # if callbacks have been set but no block is given. + # + # run_callbacks :save do + # save + # end + # + #-- + # + # As this method is used in many places, and often wraps large portions of + # user code, it has an additional design goal of minimizing its impact on + # the visible call stack. An exception from inside a :before or :after + # callback can be as noisy as it likes -- but when control has passed + # smoothly through and into the supplied block, we want as little evidence + # as possible that we were here. + def run_callbacks(kind) + callbacks = __callbacks[kind.to_sym] + + if callbacks.empty? + yield if block_given? + else + env = Filters::Environment.new(self, false, nil) + next_sequence = callbacks.compile + + # Common case: no 'around' callbacks defined + if next_sequence.final? + next_sequence.invoke_before(env) + env.value = !env.halted && (!block_given? || yield) + next_sequence.invoke_after(env) + env.value + else + invoke_sequence = Proc.new do + skipped = nil + + while true + current = next_sequence + current.invoke_before(env) + if current.final? + env.value = !env.halted && (!block_given? || yield) + elsif current.skip?(env) + (skipped ||= []) << current + next_sequence = next_sequence.nested + next + else + next_sequence = next_sequence.nested + begin + target, block, method, *arguments = current.expand_call_template(env, invoke_sequence) + target.send(method, *arguments, &block) + ensure + next_sequence = current + end + end + current.invoke_after(env) + skipped.pop.invoke_after(env) while skipped&.first + break env.value + end + end + + invoke_sequence.call + end + end + end + + private + # A hook invoked every time a before callback is halted. + # This can be overridden in ActiveSupport::Callbacks implementors in order + # to provide better debugging/logging. + def halted_callback_hook(filter, name) + end + + module Conditionals # :nodoc: + class Value + def initialize(&block) + @block = block + end + def call(target, value); @block.call(value); end + end + end + + module Filters + Environment = Struct.new(:target, :halted, :value) + + class Before + def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter, name) + halted_lambda = chain_config[:terminator] + + if user_conditions.any? + halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter, name) + else + halting(callback_sequence, user_callback, halted_lambda, filter, name) + end + end + + def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter, name) + callback_sequence.before do |env| + target = env.target + value = env.value + halted = env.halted + + if !halted && user_conditions.all? { |c| c.call(target, value) } + result_lambda = -> { user_callback.call target, value } + env.halted = halted_lambda.call(target, result_lambda) + if env.halted + target.send :halted_callback_hook, filter, name + end + end + + env + end + end + private_class_method :halting_and_conditional + + def self.halting(callback_sequence, user_callback, halted_lambda, filter, name) + callback_sequence.before do |env| + target = env.target + value = env.value + halted = env.halted + + unless halted + result_lambda = -> { user_callback.call target, value } + env.halted = halted_lambda.call(target, result_lambda) + if env.halted + target.send :halted_callback_hook, filter, name + end + end + + env + end + end + private_class_method :halting + end + + class After + def self.build(callback_sequence, user_callback, user_conditions, chain_config) + if chain_config[:skip_after_callbacks_if_terminated] + if user_conditions.any? + halting_and_conditional(callback_sequence, user_callback, user_conditions) + else + halting(callback_sequence, user_callback) + end + else + if user_conditions.any? + conditional callback_sequence, user_callback, user_conditions + else + simple callback_sequence, user_callback + end + end + end + + def self.halting_and_conditional(callback_sequence, user_callback, user_conditions) + callback_sequence.after do |env| + target = env.target + value = env.value + halted = env.halted + + if !halted && user_conditions.all? { |c| c.call(target, value) } + user_callback.call target, value + end + + env + end + end + private_class_method :halting_and_conditional + + def self.halting(callback_sequence, user_callback) + callback_sequence.after do |env| + unless env.halted + user_callback.call env.target, env.value + end + + env + end + end + private_class_method :halting + + def self.conditional(callback_sequence, user_callback, user_conditions) + callback_sequence.after do |env| + target = env.target + value = env.value + + if user_conditions.all? { |c| c.call(target, value) } + user_callback.call target, value + end + + env + end + end + private_class_method :conditional + + def self.simple(callback_sequence, user_callback) + callback_sequence.after do |env| + user_callback.call env.target, env.value + + env + end + end + private_class_method :simple + end + end + + class Callback # :nodoc:# + def self.build(chain, filter, kind, options) + if filter.is_a?(String) + raise ArgumentError, <<-MSG.squish + Passing string to define a callback is not supported. See the `.set_callback` + documentation to see supported values. + MSG + end + + new chain.name, filter, kind, options, chain.config + end + + attr_accessor :kind, :name + attr_reader :chain_config, :filter + + def initialize(name, filter, kind, options, chain_config) + @chain_config = chain_config + @name = name + @kind = kind + @filter = filter + @if = check_conditionals(options[:if]) + @unless = check_conditionals(options[:unless]) + end + + def merge_conditional_options(chain, if_option:, unless_option:) + options = { + if: @if.dup, + unless: @unless.dup + } + + options[:if].concat Array(unless_option) + options[:unless].concat Array(if_option) + + self.class.build chain, @filter, @kind, options + end + + def matches?(_kind, _filter) + @kind == _kind && filter == _filter + end + + def duplicates?(other) + case @filter + when Symbol + matches?(other.kind, other.filter) + else + false + end + end + + # Wraps code with filter + def apply(callback_sequence) + user_conditions = conditions_lambdas + user_callback = CallTemplate.build(@filter, self) + + case kind + when :before + Filters::Before.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter, name) + when :after + Filters::After.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config) + when :around + callback_sequence.around(user_callback, user_conditions) + end + end + + def current_scopes + Array(chain_config[:scope]).map { |s| public_send(s) } + end + + private + EMPTY_ARRAY = [].freeze + private_constant :EMPTY_ARRAY + + def check_conditionals(conditionals) + return EMPTY_ARRAY if conditionals.blank? + + conditionals = Array(conditionals) + if conditionals.any?(String) + raise ArgumentError, <<-MSG.squish + Passing string to be evaluated in :if and :unless conditional + options is not supported. Pass a symbol for an instance method, + or a lambda, proc or block, instead. + MSG + end + + conditionals.freeze + end + + def conditions_lambdas + @if.map { |c| CallTemplate.build(c, self).make_lambda } + + @unless.map { |c| CallTemplate.build(c, self).inverted_lambda } + end + end + + # A future invocation of user-supplied code (either as a callback, + # or a condition filter). + module CallTemplate # :nodoc: + class MethodCall + def initialize(method) + @method_name = method + end + + # Return the parts needed to make this call, with the given + # input values. + # + # Returns an array of the form: + # + # [target, block, method, *arguments] + # + # This array can be used as such: + # + # target.send(method, *arguments, &block) + # + # The actual invocation is left up to the caller to minimize + # call stack pollution. + def expand(target, value, block) + [target, block, @method_name] + end + + def make_lambda + lambda do |target, value, &block| + target.send(@method_name, &block) + end + end + + def inverted_lambda + lambda do |target, value, &block| + !target.send(@method_name, &block) + end + end + end + + class ObjectCall + def initialize(target, method) + @override_target = target + @method_name = method + end + + def expand(target, value, block) + [@override_target || target, block, @method_name, target] + end + + def make_lambda + lambda do |target, value, &block| + (@override_target || target).send(@method_name, target, &block) + end + end + + def inverted_lambda + lambda do |target, value, &block| + !(@override_target || target).send(@method_name, target, &block) + end + end + end + + class InstanceExec0 + def initialize(block) + @override_block = block + end + + def expand(target, value, block) + [target, @override_block, :instance_exec] + end + + def make_lambda + lambda do |target, value, &block| + target.instance_exec(&@override_block) + end + end + + def inverted_lambda + lambda do |target, value, &block| + !target.instance_exec(&@override_block) + end + end + end + + class InstanceExec1 + def initialize(block) + @override_block = block + end + + def expand(target, value, block) + [target, @override_block, :instance_exec, target] + end + + def make_lambda + lambda do |target, value, &block| + target.instance_exec(target, &@override_block) + end + end + + def inverted_lambda + lambda do |target, value, &block| + !target.instance_exec(target, &@override_block) + end + end + end + + class InstanceExec2 + def initialize(block) + @override_block = block + end + + def expand(target, value, block) + raise ArgumentError unless block + [target, @override_block || block, :instance_exec, target, block] + end + + def make_lambda + lambda do |target, value, &block| + raise ArgumentError unless block + target.instance_exec(target, block, &@override_block) + end + end + + def inverted_lambda + lambda do |target, value, &block| + raise ArgumentError unless block + !target.instance_exec(target, block, &@override_block) + end + end + end + + class ProcCall + def initialize(target) + @override_target = target + end + + def expand(target, value, block) + [@override_target || target, block, :call, target, value] + end + + def make_lambda + lambda do |target, value, &block| + (@override_target || target).call(target, value, &block) + end + end + + def inverted_lambda + lambda do |target, value, &block| + !(@override_target || target).call(target, value, &block) + end + end + end + + # Filters support: + # + # Symbols:: A method to call. + # Procs:: A proc to call with the object. + # Objects:: An object with a before_foo method on it to call. + # + # All of these objects are converted into a CallTemplate and handled + # the same after this point. + def self.build(filter, callback) + case filter + when Symbol + MethodCall.new(filter) + when Conditionals::Value + ProcCall.new(filter) + when ::Proc + if filter.arity > 1 + InstanceExec2.new(filter) + elsif filter.arity > 0 + InstanceExec1.new(filter) + else + InstanceExec0.new(filter) + end + else + ObjectCall.new(filter, callback.current_scopes.join("_").to_sym) + end + end + end + + # Execute before and after filters in a sequence instead of + # chaining them with nested lambda calls, see: + # https://github.com/rails/rails/issues/18011 + class CallbackSequence # :nodoc: + def initialize(nested = nil, call_template = nil, user_conditions = nil) + @nested = nested + @call_template = call_template + @user_conditions = user_conditions + + @before = [] + @after = [] + end + + def before(&before) + @before.unshift(before) + self + end + + def after(&after) + @after.push(after) + self + end + + def around(call_template, user_conditions) + CallbackSequence.new(self, call_template, user_conditions) + end + + def skip?(arg) + arg.halted || !@user_conditions.all? { |c| c.call(arg.target, arg.value) } + end + + attr_reader :nested + + def final? + !@call_template + end + + def expand_call_template(arg, block) + @call_template.expand(arg.target, arg.value, block) + end + + def invoke_before(arg) + @before.each { |b| b.call(arg) } + end + + def invoke_after(arg) + @after.each { |a| a.call(arg) } + end + end + + class CallbackChain # :nodoc: + include Enumerable + + attr_reader :name, :config + + def initialize(name, config) + @name = name + @config = { + scope: [:kind], + terminator: default_terminator + }.merge!(config) + @chain = [] + @callbacks = nil + @mutex = Mutex.new + end + + def each(&block); @chain.each(&block); end + def index(o); @chain.index(o); end + def empty?; @chain.empty?; end + + def insert(index, o) + @callbacks = nil + @chain.insert(index, o) + end + + def delete(o) + @callbacks = nil + @chain.delete(o) + end + + def clear + @callbacks = nil + @chain.clear + self + end + + def initialize_copy(other) + @callbacks = nil + @chain = other.chain.dup + @mutex = Mutex.new + end + + def compile + @callbacks || @mutex.synchronize do + final_sequence = CallbackSequence.new + @callbacks ||= @chain.reverse.inject(final_sequence) do |callback_sequence, callback| + callback.apply callback_sequence + end + end + end + + def append(*callbacks) + callbacks.each { |c| append_one(c) } + end + + def prepend(*callbacks) + callbacks.each { |c| prepend_one(c) } + end + + protected + attr_reader :chain + + private + def append_one(callback) + @callbacks = nil + remove_duplicates(callback) + @chain.push(callback) + end + + def prepend_one(callback) + @callbacks = nil + remove_duplicates(callback) + @chain.unshift(callback) + end + + def remove_duplicates(callback) + @callbacks = nil + @chain.delete_if { |c| callback.duplicates?(c) } + end + + def default_terminator + Proc.new do |target, result_lambda| + terminate = true + catch(:abort) do + result_lambda.call + terminate = false + end + terminate + end + end + end + + module ClassMethods + def normalize_callback_params(filters, block) # :nodoc: + type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before + options = filters.extract_options! + filters.unshift(block) if block + [type, filters, options.dup] + end + + # This is used internally to append, prepend and skip callbacks to the + # CallbackChain. + def __update_callbacks(name) # :nodoc: + ([self] + self.descendants).reverse_each do |target| + chain = target.get_callbacks name + yield target, chain.dup + end + end + + # Install a callback for the given event. + # + # set_callback :save, :before, :before_method + # set_callback :save, :after, :after_method, if: :condition + # set_callback :save, :around, ->(r, block) { stuff; result = block.call; stuff } + # + # The second argument indicates whether the callback is to be run +:before+, + # +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This + # means the first example above can also be written as: + # + # set_callback :save, :before_method + # + # The callback can be specified as a symbol naming an instance method; as a + # proc, lambda, or block; or as an object that responds to a certain method + # determined by the :scope argument to +define_callbacks+. + # + # If a proc, lambda, or block is given, its body is evaluated in the context + # of the current object. It can also optionally accept the current object as + # an argument. + # + # Before and around callbacks are called in the order that they are set; + # after callbacks are called in the reverse order. + # + # Around callbacks can access the return value from the event, if it + # wasn't halted, from the +yield+ call. + # + # ===== Options + # + # * :if - A symbol or an array of symbols, each naming an instance + # method or a proc; the callback will be called only when they all return + # a true value. + # + # If a proc is given, its body is evaluated in the context of the + # current object. It can also optionally accept the current object as + # an argument. + # * :unless - A symbol or an array of symbols, each naming an + # instance method or a proc; the callback will be called only when they + # all return a false value. + # + # If a proc is given, its body is evaluated in the context of the + # current object. It can also optionally accept the current object as + # an argument. + # * :prepend - If +true+, the callback will be prepended to the + # existing chain rather than appended. + def set_callback(name, *filter_list, &block) + type, filters, options = normalize_callback_params(filter_list, block) + + self_chain = get_callbacks name + mapped = filters.map do |filter| + Callback.build(self_chain, filter, type, options) + end + + __update_callbacks(name) do |target, chain| + options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped) + target.set_callbacks name, chain + end + end + + # Skip a previously set callback. Like +set_callback+, :if or + # :unless options may be passed in order to control when the + # callback is skipped. + # + # class Writer < PersonRecord + # attr_accessor :age + # skip_callback :save, :before, :saving_message, if: -> { age > 18 } + # end + # + # When if option returns true, callback is skipped. + # + # writer = Writer.new + # writer.age = 20 + # writer.save + # + # Output: + # - save + # saved + # + # When if option returns false, callback is NOT skipped. + # + # young_writer = Writer.new + # young_writer.age = 17 + # young_writer.save + # + # Output: + # saving... + # - save + # saved + # + # An ArgumentError will be raised if the callback has not + # already been set (unless the :raise option is set to false). + def skip_callback(name, *filter_list, &block) + type, filters, options = normalize_callback_params(filter_list, block) + + options[:raise] = true unless options.key?(:raise) + + __update_callbacks(name) do |target, chain| + filters.each do |filter| + callback = chain.find { |c| c.matches?(type, filter) } + + if !callback && options[:raise] + raise ArgumentError, "#{type.to_s.capitalize} #{name} callback #{filter.inspect} has not been defined" + end + + if callback && (options.key?(:if) || options.key?(:unless)) + new_callback = callback.merge_conditional_options(chain, if_option: options[:if], unless_option: options[:unless]) + chain.insert(chain.index(callback), new_callback) + end + + chain.delete(callback) + end + target.set_callbacks name, chain + end + end + + # Remove all set callbacks for the given event. + def reset_callbacks(name) + callbacks = get_callbacks name + + self.descendants.each do |target| + chain = target.get_callbacks(name).dup + callbacks.each { |c| chain.delete(c) } + target.set_callbacks name, chain + end + + set_callbacks(name, callbacks.dup.clear) + end + + # Define sets of events in the object life cycle that support callbacks. + # + # define_callbacks :validate + # define_callbacks :initialize, :save, :destroy + # + # ===== Options + # + # * :terminator - Determines when a before filter will halt the + # callback chain, preventing following before and around callbacks from + # being called and the event from being triggered. + # This should be a lambda to be executed. + # The current object and the result lambda of the callback will be provided + # to the terminator lambda. + # + # define_callbacks :validate, terminator: ->(target, result_lambda) { result_lambda.call == false } + # + # In this example, if any before validate callbacks returns +false+, + # any successive before and around callback is not executed. + # + # The default terminator halts the chain when a callback throws +:abort+. + # + # * :skip_after_callbacks_if_terminated - Determines if after + # callbacks should be terminated by the :terminator option. By + # default after callbacks are executed no matter if callback chain was + # terminated or not. This option has no effect if :terminator + # option is set to +nil+. + # + # * :scope - Indicates which methods should be executed when an + # object is used as a callback. + # + # class Audit + # def before(caller) + # puts 'Audit: before is called' + # end + # + # def before_save(caller) + # puts 'Audit: before_save is called' + # end + # end + # + # class Account + # include ActiveSupport::Callbacks + # + # define_callbacks :save + # set_callback :save, :before, Audit.new + # + # def save + # run_callbacks :save do + # puts 'save in main' + # end + # end + # end + # + # In the above case whenever you save an account the method + # Audit#before will be called. On the other hand + # + # define_callbacks :save, scope: [:kind, :name] + # + # would trigger Audit#before_save instead. That's constructed + # by calling #{kind}_#{name} on the given instance. In this + # case "kind" is "before" and "name" is "save". In this context +:kind+ + # and +:name+ have special meanings: +:kind+ refers to the kind of + # callback (before/after/around) and +:name+ refers to the method on + # which callbacks are being defined. + # + # A declaration like + # + # define_callbacks :save, scope: [:name] + # + # would call Audit#save. + # + # ===== Notes + # + # +names+ passed to +define_callbacks+ must not end with + # !, ? or =. + # + # Calling +define_callbacks+ multiple times with the same +names+ will + # overwrite previous callbacks registered with +set_callback+. + def define_callbacks(*names) + options = names.extract_options! + + names.each do |name| + name = name.to_sym + + ([self] + self.descendants).each do |target| + target.set_callbacks name, CallbackChain.new(name, options) + end + + module_eval <<-RUBY, __FILE__, __LINE__ + 1 + def _run_#{name}_callbacks(&block) + run_callbacks #{name.inspect}, &block + end + + def self._#{name}_callbacks + get_callbacks(#{name.inspect}) + end + + def self._#{name}_callbacks=(value) + set_callbacks(#{name.inspect}, value) + end + + def _#{name}_callbacks + __callbacks[#{name.inspect}] + end + RUBY + end + end + + protected + def get_callbacks(name) # :nodoc: + __callbacks[name.to_sym] + end + + def set_callbacks(name, callbacks) # :nodoc: + unless singleton_class.method_defined?(:__callbacks, false) + self.__callbacks = __callbacks.dup + end + self.__callbacks[name.to_sym] = callbacks + self.__callbacks + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/code_generator.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/code_generator.rb new file mode 100644 index 0000000..46f612d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/code_generator.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +module ActiveSupport + class CodeGenerator # :nodoc: + class MethodSet + METHOD_CACHES = Hash.new { |h, k| h[k] = Module.new } + + def initialize(namespace) + @cache = METHOD_CACHES[namespace] + @sources = [] + @methods = {} + end + + def define_cached_method(name, as: name) + name = name.to_sym + as = as.to_sym + @methods.fetch(name) do + unless @cache.method_defined?(as) + yield @sources + end + @methods[name] = as + end + end + + def apply(owner, path, line) + unless @sources.empty? + @cache.module_eval("# frozen_string_literal: true\n" + @sources.join(";"), path, line) + end + @methods.each do |name, as| + owner.define_method(name, @cache.instance_method(as)) + end + end + end + + class << self + def batch(owner, path, line) + if owner.is_a?(CodeGenerator) + yield owner + else + instance = new(owner, path, line) + result = yield instance + instance.execute + result + end + end + end + + def initialize(owner, path, line) + @owner = owner + @path = path + @line = line + @namespaces = Hash.new { |h, k| h[k] = MethodSet.new(k) } + end + + def define_cached_method(name, namespace:, as: name, &block) + @namespaces[namespace].define_cached_method(name, as: as, &block) + end + + def execute + @namespaces.each_value do |method_set| + method_set.apply(@owner, @path, @line - 1) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/concern.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/concern.rb new file mode 100644 index 0000000..9511338 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/concern.rb @@ -0,0 +1,215 @@ +# frozen_string_literal: true + +module ActiveSupport + # A typical module looks like this: + # + # module M + # def self.included(base) + # base.extend ClassMethods + # base.class_eval do + # scope :disabled, -> { where(disabled: true) } + # end + # end + # + # module ClassMethods + # ... + # end + # end + # + # By using ActiveSupport::Concern the above module could instead be + # written as: + # + # require "active_support/concern" + # + # module M + # extend ActiveSupport::Concern + # + # included do + # scope :disabled, -> { where(disabled: true) } + # end + # + # class_methods do + # ... + # end + # end + # + # Moreover, it gracefully handles module dependencies. Given a +Foo+ module + # and a +Bar+ module which depends on the former, we would typically write the + # following: + # + # module Foo + # def self.included(base) + # base.class_eval do + # def self.method_injected_by_foo + # ... + # end + # end + # end + # end + # + # module Bar + # def self.included(base) + # base.method_injected_by_foo + # end + # end + # + # class Host + # include Foo # We need to include this dependency for Bar + # include Bar # Bar is the module that Host really needs + # end + # + # But why should +Host+ care about +Bar+'s dependencies, namely +Foo+? We + # could try to hide these from +Host+ directly including +Foo+ in +Bar+: + # + # module Bar + # include Foo + # def self.included(base) + # base.method_injected_by_foo + # end + # end + # + # class Host + # include Bar + # end + # + # Unfortunately this won't work, since when +Foo+ is included, its base + # is the +Bar+ module, not the +Host+ class. With ActiveSupport::Concern, + # module dependencies are properly resolved: + # + # require "active_support/concern" + # + # module Foo + # extend ActiveSupport::Concern + # included do + # def self.method_injected_by_foo + # ... + # end + # end + # end + # + # module Bar + # extend ActiveSupport::Concern + # include Foo + # + # included do + # self.method_injected_by_foo + # end + # end + # + # class Host + # include Bar # It works, now Bar takes care of its dependencies + # end + # + # === Prepending concerns + # + # Just like include, concerns also support prepend with a corresponding + # prepended do callback. module ClassMethods or class_methods do are + # prepended as well. + # + # prepend is also used for any dependencies. + module Concern + class MultipleIncludedBlocks < StandardError # :nodoc: + def initialize + super "Cannot define multiple 'included' blocks for a Concern" + end + end + + class MultiplePrependBlocks < StandardError # :nodoc: + def initialize + super "Cannot define multiple 'prepended' blocks for a Concern" + end + end + + def self.extended(base) # :nodoc: + base.instance_variable_set(:@_dependencies, []) + end + + def append_features(base) # :nodoc: + if base.instance_variable_defined?(:@_dependencies) + base.instance_variable_get(:@_dependencies) << self + false + else + return false if base < self + @_dependencies.each { |dep| base.include(dep) } + super + base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods) + base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block) + end + end + + def prepend_features(base) # :nodoc: + if base.instance_variable_defined?(:@_dependencies) + base.instance_variable_get(:@_dependencies).unshift self + false + else + return false if base < self + @_dependencies.each { |dep| base.prepend(dep) } + super + base.singleton_class.prepend const_get(:ClassMethods) if const_defined?(:ClassMethods) + base.class_eval(&@_prepended_block) if instance_variable_defined?(:@_prepended_block) + end + end + + # Evaluate given block in context of base class, + # so that you can write class macros here. + # When you define more than one +included+ block, it raises an exception. + def included(base = nil, &block) + if base.nil? + if instance_variable_defined?(:@_included_block) + if @_included_block.source_location != block.source_location + raise MultipleIncludedBlocks + end + else + @_included_block = block + end + else + super + end + end + + # Evaluate given block in context of base class, + # so that you can write class macros here. + # When you define more than one +prepended+ block, it raises an exception. + def prepended(base = nil, &block) + if base.nil? + if instance_variable_defined?(:@_prepended_block) + if @_prepended_block.source_location != block.source_location + raise MultiplePrependBlocks + end + else + @_prepended_block = block + end + else + super + end + end + + # Define class methods from given block. + # You can define private class methods as well. + # + # module Example + # extend ActiveSupport::Concern + # + # class_methods do + # def foo; puts 'foo'; end + # + # private + # def bar; puts 'bar'; end + # end + # end + # + # class Buzz + # include Example + # end + # + # Buzz.foo # => "foo" + # Buzz.bar # => private method 'bar' called for Buzz:Class(NoMethodError) + def class_methods(&class_methods_module_definition) + mod = const_defined?(:ClassMethods, false) ? + const_get(:ClassMethods) : + const_set(:ClassMethods, Module.new) + + mod.module_eval(&class_methods_module_definition) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/concurrency/load_interlock_aware_monitor.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/concurrency/load_interlock_aware_monitor.rb new file mode 100644 index 0000000..9af2bd9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/concurrency/load_interlock_aware_monitor.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require "monitor" + +module ActiveSupport + module Concurrency + # A monitor that will permit dependency loading while blocked waiting for + # the lock. + class LoadInterlockAwareMonitor < Monitor + EXCEPTION_NEVER = { Exception => :never }.freeze + EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze + private_constant :EXCEPTION_NEVER, :EXCEPTION_IMMEDIATE + + # Enters an exclusive section, but allows dependency loading while blocked + def mon_enter + mon_try_enter || + ActiveSupport::Dependencies.interlock.permit_concurrent_loads { super } + end + + def synchronize(&block) + Thread.handle_interrupt(EXCEPTION_NEVER) do + mon_enter + + begin + Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block) + ensure + mon_exit + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/concurrency/share_lock.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/concurrency/share_lock.rb new file mode 100644 index 0000000..fef87c7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/concurrency/share_lock.rb @@ -0,0 +1,226 @@ +# frozen_string_literal: true + +require "thread" +require "monitor" + +module ActiveSupport + module Concurrency + # A share/exclusive lock, otherwise known as a read/write lock. + # + # https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock + class ShareLock + include MonitorMixin + + # We track Thread objects, instead of just using counters, because + # we need exclusive locks to be reentrant, and we need to be able + # to upgrade share locks to exclusive. + + def raw_state # :nodoc: + synchronize do + threads = @sleeping.keys | @sharing.keys | @waiting.keys + threads |= [@exclusive_thread] if @exclusive_thread + + data = {} + + threads.each do |thread| + purpose, compatible = @waiting[thread] + + data[thread] = { + thread: thread, + sharing: @sharing[thread], + exclusive: @exclusive_thread == thread, + purpose: purpose, + compatible: compatible, + waiting: !!@waiting[thread], + sleeper: @sleeping[thread], + } + end + + # NB: Yields while holding our *internal* synchronize lock, + # which is supposed to be used only for a few instructions at + # a time. This allows the caller to inspect additional state + # without things changing out from underneath, but would have + # disastrous effects upon normal operation. Fortunately, this + # method is only intended to be called when things have + # already gone wrong. + yield data + end + end + + def initialize + super() + + @cv = new_cond + + @sharing = Hash.new(0) + @waiting = {} + @sleeping = {} + @exclusive_thread = nil + @exclusive_depth = 0 + end + + # Returns false if +no_wait+ is set and the lock is not + # immediately available. Otherwise, returns true after the lock + # has been acquired. + # + # +purpose+ and +compatible+ work together; while this thread is + # waiting for the exclusive lock, it will yield its share (if any) + # to any other attempt whose +purpose+ appears in this attempt's + # +compatible+ list. This allows a "loose" upgrade, which, being + # less strict, prevents some classes of deadlocks. + # + # For many resources, loose upgrades are sufficient: if a thread + # is awaiting a lock, it is not running any other code. With + # +purpose+ matching, it is possible to yield only to other + # threads whose activity will not interfere. + def start_exclusive(purpose: nil, compatible: [], no_wait: false) + synchronize do + unless @exclusive_thread == Thread.current + if busy_for_exclusive?(purpose) + return false if no_wait + + yield_shares(purpose: purpose, compatible: compatible, block_share: true) do + wait_for(:start_exclusive) { busy_for_exclusive?(purpose) } + end + end + @exclusive_thread = Thread.current + end + @exclusive_depth += 1 + + true + end + end + + # Relinquish the exclusive lock. Must only be called by the thread + # that called start_exclusive (and currently holds the lock). + def stop_exclusive(compatible: []) + synchronize do + raise "invalid unlock" if @exclusive_thread != Thread.current + + @exclusive_depth -= 1 + if @exclusive_depth == 0 + @exclusive_thread = nil + + if eligible_waiters?(compatible) + yield_shares(compatible: compatible, block_share: true) do + wait_for(:stop_exclusive) { @exclusive_thread || eligible_waiters?(compatible) } + end + end + @cv.broadcast + end + end + end + + def start_sharing + synchronize do + if @sharing[Thread.current] > 0 || @exclusive_thread == Thread.current + # We already hold a lock; nothing to wait for + elsif @waiting[Thread.current] + # We're nested inside a +yield_shares+ call: we'll resume as + # soon as there isn't an exclusive lock in our way + wait_for(:start_sharing) { @exclusive_thread } + else + # This is an initial / outermost share call: any outstanding + # requests for an exclusive lock get to go first + wait_for(:start_sharing) { busy_for_sharing?(false) } + end + @sharing[Thread.current] += 1 + end + end + + def stop_sharing + synchronize do + if @sharing[Thread.current] > 1 + @sharing[Thread.current] -= 1 + else + @sharing.delete Thread.current + @cv.broadcast + end + end + end + + # Execute the supplied block while holding the Exclusive lock. If + # +no_wait+ is set and the lock is not immediately available, + # returns +nil+ without yielding. Otherwise, returns the result of + # the block. + # + # See +start_exclusive+ for other options. + def exclusive(purpose: nil, compatible: [], after_compatible: [], no_wait: false) + if start_exclusive(purpose: purpose, compatible: compatible, no_wait: no_wait) + begin + yield + ensure + stop_exclusive(compatible: after_compatible) + end + end + end + + # Execute the supplied block while holding the Share lock. + def sharing + start_sharing + begin + yield + ensure + stop_sharing + end + end + + # Temporarily give up all held Share locks while executing the + # supplied block, allowing any +compatible+ exclusive lock request + # to proceed. + def yield_shares(purpose: nil, compatible: [], block_share: false) + loose_shares = previous_wait = nil + synchronize do + if loose_shares = @sharing.delete(Thread.current) + if previous_wait = @waiting[Thread.current] + purpose = nil unless purpose == previous_wait[0] + compatible &= previous_wait[1] + end + compatible |= [false] unless block_share + @waiting[Thread.current] = [purpose, compatible] + end + + @cv.broadcast + end + + begin + yield + ensure + synchronize do + wait_for(:yield_shares) { @exclusive_thread && @exclusive_thread != Thread.current } + + if previous_wait + @waiting[Thread.current] = previous_wait + else + @waiting.delete Thread.current + end + @sharing[Thread.current] = loose_shares if loose_shares + end + end + end + + private + # Must be called within synchronize + def busy_for_exclusive?(purpose) + busy_for_sharing?(purpose) || + @sharing.size > (@sharing[Thread.current] > 0 ? 1 : 0) + end + + def busy_for_sharing?(purpose) + (@exclusive_thread && @exclusive_thread != Thread.current) || + @waiting.any? { |t, (_, c)| t != Thread.current && !c.include?(purpose) } + end + + def eligible_waiters?(compatible) + @waiting.any? { |t, (p, _)| compatible.include?(p) && @waiting.all? { |t2, (_, c2)| t == t2 || c2.include?(p) } } + end + + def wait_for(method, &block) + @sleeping[Thread.current] = method + @cv.wait_while(&block) + ensure + @sleeping.delete Thread.current + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/configurable.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/configurable.rb new file mode 100644 index 0000000..c97d5fd --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/configurable.rb @@ -0,0 +1,149 @@ +# frozen_string_literal: true + +require "active_support/concern" +require "active_support/ordered_options" + +module ActiveSupport + # Configurable provides a config method to store and retrieve + # configuration options as an OrderedOptions. + module Configurable + extend ActiveSupport::Concern + + class Configuration < ActiveSupport::InheritableOptions + def compile_methods! + self.class.compile_methods!(keys) + end + + # Compiles reader methods so we don't have to go through method_missing. + def self.compile_methods!(keys) + keys.reject { |m| method_defined?(m) }.each do |key| + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{key}; _get(#{key.inspect}); end + RUBY + end + end + end + + module ClassMethods + def config + @_config ||= if respond_to?(:superclass) && superclass.respond_to?(:config) + superclass.config.inheritable_copy + else + # create a new "anonymous" class that will host the compiled reader methods + Class.new(Configuration).new + end + end + + def configure + yield config + end + + # Allows you to add shortcut so that you don't have to refer to attribute + # through config. Also look at the example for config to contrast. + # + # Defines both class and instance config accessors. + # + # class User + # include ActiveSupport::Configurable + # config_accessor :allowed_access + # end + # + # User.allowed_access # => nil + # User.allowed_access = false + # User.allowed_access # => false + # + # user = User.new + # user.allowed_access # => false + # user.allowed_access = true + # user.allowed_access # => true + # + # User.allowed_access # => false + # + # The attribute name must be a valid method name in Ruby. + # + # class User + # include ActiveSupport::Configurable + # config_accessor :"1_Badname" + # end + # # => NameError: invalid config attribute name + # + # To omit the instance writer method, pass instance_writer: false. + # To omit the instance reader method, pass instance_reader: false. + # + # class User + # include ActiveSupport::Configurable + # config_accessor :allowed_access, instance_reader: false, instance_writer: false + # end + # + # User.allowed_access = false + # User.allowed_access # => false + # + # User.new.allowed_access = true # => NoMethodError + # User.new.allowed_access # => NoMethodError + # + # Or pass instance_accessor: false, to omit both instance methods. + # + # class User + # include ActiveSupport::Configurable + # config_accessor :allowed_access, instance_accessor: false + # end + # + # User.allowed_access = false + # User.allowed_access # => false + # + # User.new.allowed_access = true # => NoMethodError + # User.new.allowed_access # => NoMethodError + # + # Also you can pass default or a block to set up the attribute with a default value. + # + # class User + # include ActiveSupport::Configurable + # config_accessor :allowed_access, default: false + # config_accessor :hair_colors do + # [:brown, :black, :blonde, :red] + # end + # end + # + # User.allowed_access # => false + # User.hair_colors # => [:brown, :black, :blonde, :red] + def config_accessor(*names, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil) # :doc: + names.each do |name| + raise NameError.new("invalid config attribute name") unless /\A[_A-Za-z]\w*\z/.match?(name) + + reader, reader_line = "def #{name}; config.#{name}; end", __LINE__ + writer, writer_line = "def #{name}=(value); config.#{name} = value; end", __LINE__ + + singleton_class.class_eval reader, __FILE__, reader_line + singleton_class.class_eval writer, __FILE__, writer_line + + if instance_accessor + class_eval reader, __FILE__, reader_line if instance_reader + class_eval writer, __FILE__, writer_line if instance_writer + end + + send("#{name}=", block_given? ? yield : default) + end + end + private :config_accessor + end + + # Reads and writes attributes from a configuration OrderedOptions. + # + # require "active_support/configurable" + # + # class User + # include ActiveSupport::Configurable + # end + # + # user = User.new + # + # user.config.allowed_access = true + # user.config.level = 1 + # + # user.config.allowed_access # => true + # user.config.level # => 1 + def config + @_config ||= self.class.config.inheritable_copy + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/configuration_file.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/configuration_file.rb new file mode 100644 index 0000000..d025501 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/configuration_file.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module ActiveSupport + # Reads a YAML configuration file, evaluating any ERB, then + # parsing the resulting YAML. + # + # Warns in case of YAML confusing characters, like invisible + # non-breaking spaces. + class ConfigurationFile # :nodoc: + class FormatError < StandardError; end + + def initialize(content_path) + @content_path = content_path.to_s + @content = read content_path + end + + def self.parse(content_path, **options) + new(content_path).parse(**options) + end + + def parse(context: nil, **options) + source = render(context) + if YAML.respond_to?(:unsafe_load) + YAML.unsafe_load(source, **options) || {} + else + YAML.load(source, **options) || {} + end + rescue Psych::SyntaxError => error + raise "YAML syntax error occurred while parsing #{@content_path}. " \ + "Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \ + "Error: #{error.message}" + end + + private + def read(content_path) + require "yaml" + require "erb" + + File.read(content_path).tap do |content| + if content.include?("\u00A0") + warn "#{content_path} contains invisible non-breaking spaces, you may want to remove those" + end + end + end + + def render(context) + erb = ERB.new(@content).tap { |e| e.filename = @content_path } + context ? erb.result(context) : erb.result + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext.rb new file mode 100644 index 0000000..db2be98 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +Dir.glob(File.expand_path("core_ext/*.rb", __dir__)).sort.each do |path| + next if path.end_with?("core_ext/uri.rb") + require path +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array.rb new file mode 100644 index 0000000..81c9f69 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require "active_support/core_ext/array/wrap" +require "active_support/core_ext/array/access" +require "active_support/core_ext/array/conversions" +require "active_support/core_ext/array/deprecated_conversions" unless ENV["RAILS_DISABLE_DEPRECATED_TO_S_CONVERSION"] +require "active_support/core_ext/array/extract" +require "active_support/core_ext/array/extract_options" +require "active_support/core_ext/array/grouping" +require "active_support/core_ext/array/inquiry" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/access.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/access.rb new file mode 100644 index 0000000..8f36669 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/access.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +class Array + # Returns the tail of the array from +position+. + # + # %w( a b c d ).from(0) # => ["a", "b", "c", "d"] + # %w( a b c d ).from(2) # => ["c", "d"] + # %w( a b c d ).from(10) # => [] + # %w().from(0) # => [] + # %w( a b c d ).from(-2) # => ["c", "d"] + # %w( a b c ).from(-10) # => [] + def from(position) + self[position, length] || [] + end + + # Returns the beginning of the array up to +position+. + # + # %w( a b c d ).to(0) # => ["a"] + # %w( a b c d ).to(2) # => ["a", "b", "c"] + # %w( a b c d ).to(10) # => ["a", "b", "c", "d"] + # %w().to(0) # => [] + # %w( a b c d ).to(-2) # => ["a", "b", "c"] + # %w( a b c ).to(-10) # => [] + def to(position) + if position >= 0 + take position + 1 + else + self[0..position] + end + end + + # Returns a new array that includes the passed elements. + # + # [ 1, 2, 3 ].including(4, 5) # => [ 1, 2, 3, 4, 5 ] + # [ [ 0, 1 ] ].including([ [ 1, 0 ] ]) # => [ [ 0, 1 ], [ 1, 0 ] ] + def including(*elements) + self + elements.flatten(1) + end + + # Returns a copy of the Array excluding the specified elements. + # + # ["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") # => ["David", "Rafael"] + # [ [ 0, 1 ], [ 1, 0 ] ].excluding([ [ 1, 0 ] ]) # => [ [ 0, 1 ] ] + # + # Note: This is an optimization of Enumerable#excluding that uses Array#- + # instead of Array#reject for performance reasons. + def excluding(*elements) + self - elements.flatten(1) + end + alias :without :excluding + + # Equal to self[1]. + # + # %w( a b c d e ).second # => "b" + def second + self[1] + end + + # Equal to self[2]. + # + # %w( a b c d e ).third # => "c" + def third + self[2] + end + + # Equal to self[3]. + # + # %w( a b c d e ).fourth # => "d" + def fourth + self[3] + end + + # Equal to self[4]. + # + # %w( a b c d e ).fifth # => "e" + def fifth + self[4] + end + + # Equal to self[41]. Also known as accessing "the reddit". + # + # (1..42).to_a.forty_two # => 42 + def forty_two + self[41] + end + + # Equal to self[-3]. + # + # %w( a b c d e ).third_to_last # => "c" + def third_to_last + self[-3] + end + + # Equal to self[-2]. + # + # %w( a b c d e ).second_to_last # => "d" + def second_to_last + self[-2] + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/conversions.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/conversions.rb new file mode 100644 index 0000000..6835d21 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/conversions.rb @@ -0,0 +1,214 @@ +# frozen_string_literal: true + +require "active_support/core_ext/hash/keys" +require "active_support/core_ext/string/inflections" +require "active_support/core_ext/object/to_param" +require "active_support/core_ext/object/to_query" + +class Array + # Converts the array to a comma-separated sentence where the last element is + # joined by the connector word. + # + # You can pass the following options to change the default behavior. If you + # pass an option key that doesn't exist in the list below, it will raise an + # ArgumentError. + # + # ==== Options + # + # * :words_connector - The sign or word used to join all but the last + # element in arrays with three or more elements (default: ", "). + # * :last_word_connector - The sign or word used to join the last element + # in arrays with three or more elements (default: ", and "). + # * :two_words_connector - The sign or word used to join the elements + # in arrays with two elements (default: " and "). + # * :locale - If +i18n+ is available, you can set a locale and use + # the connector options defined on the 'support.array' namespace in the + # corresponding dictionary file. + # + # ==== Examples + # + # [].to_sentence # => "" + # ['one'].to_sentence # => "one" + # ['one', 'two'].to_sentence # => "one and two" + # ['one', 'two', 'three'].to_sentence # => "one, two, and three" + # + # ['one', 'two'].to_sentence(passing: 'invalid option') + # # => ArgumentError: Unknown key: :passing. Valid keys are: :words_connector, :two_words_connector, :last_word_connector, :locale + # + # ['one', 'two'].to_sentence(two_words_connector: '-') + # # => "one-two" + # + # ['one', 'two', 'three'].to_sentence(words_connector: ' or ', last_word_connector: ' or at least ') + # # => "one or two or at least three" + # + # Using :locale option: + # + # # Given this locale dictionary: + # # + # # es: + # # support: + # # array: + # # words_connector: " o " + # # two_words_connector: " y " + # # last_word_connector: " o al menos " + # + # ['uno', 'dos'].to_sentence(locale: :es) + # # => "uno y dos" + # + # ['uno', 'dos', 'tres'].to_sentence(locale: :es) + # # => "uno o dos o al menos tres" + def to_sentence(options = {}) + options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale) + + default_connectors = { + words_connector: ", ", + two_words_connector: " and ", + last_word_connector: ", and " + } + if options[:locale] != false && defined?(I18n) + i18n_connectors = I18n.translate(:'support.array', locale: options[:locale], default: {}) + default_connectors.merge!(i18n_connectors) + end + options = default_connectors.merge!(options) + + case length + when 0 + +"" + when 1 + +"#{self[0]}" + when 2 + +"#{self[0]}#{options[:two_words_connector]}#{self[1]}" + else + +"#{self[0...-1].join(options[:words_connector])}#{options[:last_word_connector]}#{self[-1]}" + end + end + + # Extends Array#to_s to convert a collection of elements into a + # comma separated id list if :db argument is given as the format. + # + # This method is aliased to to_formatted_s. + # + # Blog.all.to_fs(:db) # => "1,2,3" + # Blog.none.to_fs(:db) # => "null" + # [1,2].to_fs # => "[1, 2]" + def to_fs(format = :default) + case format + when :db + if empty? + "null" + else + collect(&:id).join(",") + end + else + to_default_s + end + end + alias_method :to_formatted_s, :to_fs + alias_method :to_default_s, :to_s + + # Returns a string that represents the array in XML by invoking +to_xml+ + # on each element. Active Record collections delegate their representation + # in XML to this method. + # + # All elements are expected to respond to +to_xml+, if any of them does + # not then an exception is raised. + # + # The root node reflects the class name of the first element in plural + # if all elements belong to the same type and that's not Hash: + # + # customer.projects.to_xml + # + # + # + # + # 20000.0 + # 1567 + # 2008-04-09 + # ... + # + # + # 57230.0 + # 1567 + # 2008-04-15 + # ... + # + # + # + # Otherwise the root element is "objects": + # + # [{ foo: 1, bar: 2}, { baz: 3}].to_xml + # + # + # + # + # 2 + # 1 + # + # + # 3 + # + # + # + # If the collection is empty the root element is "nil-classes" by default: + # + # [].to_xml + # + # + # + # + # To ensure a meaningful root element use the :root option: + # + # customer_with_no_projects.projects.to_xml(root: 'projects') + # + # + # + # + # By default name of the node for the children of root is root.singularize. + # You can change it with the :children option. + # + # The +options+ hash is passed downwards: + # + # Message.all.to_xml(skip_types: true) + # + # + # + # + # 2008-03-07T09:58:18+01:00 + # 1 + # 1 + # 2008-03-07T09:58:18+01:00 + # 1 + # + # + # + def to_xml(options = {}) + require "active_support/builder" unless defined?(Builder::XmlMarkup) + + options = options.dup + options[:indent] ||= 2 + options[:builder] ||= Builder::XmlMarkup.new(indent: options[:indent]) + options[:root] ||= \ + if first.class != Hash && all?(first.class) + underscored = ActiveSupport::Inflector.underscore(first.class.name) + ActiveSupport::Inflector.pluralize(underscored).tr("/", "_") + else + "objects" + end + + builder = options[:builder] + builder.instruct! unless options.delete(:skip_instruct) + + root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options) + children = options.delete(:children) || root.singularize + attributes = options[:skip_types] ? {} : { type: "array" } + + if empty? + builder.tag!(root, attributes) + else + builder.tag!(root, attributes) do + each { |value| ActiveSupport::XmlMini.to_tag(children, value, options) } + yield builder if block_given? + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/deprecated_conversions.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/deprecated_conversions.rb new file mode 100644 index 0000000..b2eabdf --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/deprecated_conversions.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class Array + NOT_SET = Object.new # :nodoc: + def to_s(format = NOT_SET) # :nodoc: + case format + when :db + ActiveSupport::Deprecation.warn( + "Array#to_s(#{format.inspect}) is deprecated. Please use Array#to_fs(#{format.inspect}) instead." + ) + if empty? + "null" + else + collect(&:id).join(",") + end + when NOT_SET + to_default_s + else + ActiveSupport::Deprecation.warn( + "Array#to_s(#{format.inspect}) is deprecated. Please use Array#to_fs(#{format.inspect}) instead." + ) + to_default_s + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/extract.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/extract.rb new file mode 100644 index 0000000..cc5a8a3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/extract.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class Array + # Removes and returns the elements for which the block returns a true value. + # If no block is given, an Enumerator is returned instead. + # + # numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + # odd_numbers = numbers.extract! { |number| number.odd? } # => [1, 3, 5, 7, 9] + # numbers # => [0, 2, 4, 6, 8] + def extract! + return to_enum(:extract!) { size } unless block_given? + + extracted_elements = [] + + reject! do |element| + extracted_elements << element if yield(element) + end + + extracted_elements + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/extract_options.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/extract_options.rb new file mode 100644 index 0000000..8c7cb2e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/extract_options.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +class Hash + # By default, only instances of Hash itself are extractable. + # Subclasses of Hash may implement this method and return + # true to declare themselves as extractable. If a Hash + # is extractable, Array#extract_options! pops it from + # the Array when it is the last element of the Array. + def extractable_options? + instance_of?(Hash) + end +end + +class Array + # Extracts options from a set of arguments. Removes and returns the last + # element in the array if it's a hash, otherwise returns a blank hash. + # + # def options(*args) + # args.extract_options! + # end + # + # options(1, 2) # => {} + # options(1, 2, a: :b) # => {:a=>:b} + def extract_options! + if last.is_a?(Hash) && last.extractable_options? + pop + else + {} + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/grouping.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/grouping.rb new file mode 100644 index 0000000..36993e0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/grouping.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +class Array + # Splits or iterates over the array in groups of size +number+, + # padding any remaining slots with +fill_with+ unless it is +false+. + # + # %w(1 2 3 4 5 6 7 8 9 10).in_groups_of(3) {|group| p group} + # ["1", "2", "3"] + # ["4", "5", "6"] + # ["7", "8", "9"] + # ["10", nil, nil] + # + # %w(1 2 3 4 5).in_groups_of(2, ' ') {|group| p group} + # ["1", "2"] + # ["3", "4"] + # ["5", " "] + # + # %w(1 2 3 4 5).in_groups_of(2, false) {|group| p group} + # ["1", "2"] + # ["3", "4"] + # ["5"] + def in_groups_of(number, fill_with = nil, &block) + if number.to_i <= 0 + raise ArgumentError, + "Group size must be a positive integer, was #{number.inspect}" + end + + if fill_with == false + collection = self + else + # size % number gives how many extra we have; + # subtracting from number gives how many to add; + # modulo number ensures we don't add group of just fill. + padding = (number - size % number) % number + collection = dup.concat(Array.new(padding, fill_with)) + end + + if block_given? + collection.each_slice(number, &block) + else + collection.each_slice(number).to_a + end + end + + # Splits or iterates over the array in +number+ of groups, padding any + # remaining slots with +fill_with+ unless it is +false+. + # + # %w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|group| p group} + # ["1", "2", "3", "4"] + # ["5", "6", "7", nil] + # ["8", "9", "10", nil] + # + # %w(1 2 3 4 5 6 7 8 9 10).in_groups(3, ' ') {|group| p group} + # ["1", "2", "3", "4"] + # ["5", "6", "7", " "] + # ["8", "9", "10", " "] + # + # %w(1 2 3 4 5 6 7).in_groups(3, false) {|group| p group} + # ["1", "2", "3"] + # ["4", "5"] + # ["6", "7"] + def in_groups(number, fill_with = nil, &block) + # size.div number gives minor group size; + # size % number gives how many objects need extra accommodation; + # each group hold either division or division + 1 items. + division = size.div number + modulo = size % number + + # create a new array avoiding dup + groups = [] + start = 0 + + number.times do |index| + length = division + (modulo > 0 && modulo > index ? 1 : 0) + groups << last_group = slice(start, length) + last_group << fill_with if fill_with != false && + modulo > 0 && length == division + start += length + end + + if block_given? + groups.each(&block) + else + groups + end + end + + # Divides the array into one or more subarrays based on a delimiting +value+ + # or the result of an optional block. + # + # [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]] + # (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]] + def split(value = nil, &block) + arr = dup + result = [] + if block_given? + while (idx = arr.index(&block)) + result << arr.shift(idx) + arr.shift + end + else + while (idx = arr.index(value)) + result << arr.shift(idx) + arr.shift + end + end + result << arr + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/inquiry.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/inquiry.rb new file mode 100644 index 0000000..650b106 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/inquiry.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require "active_support/array_inquirer" + +class Array + # Wraps the array in an ActiveSupport::ArrayInquirer object, which gives a + # friendlier way to check its string-like contents. + # + # pets = [:cat, :dog].inquiry + # + # pets.cat? # => true + # pets.ferret? # => false + # + # pets.any?(:cat, :ferret) # => true + # pets.any?(:ferret, :alligator) # => false + def inquiry + ActiveSupport::ArrayInquirer.new(self) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/wrap.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/wrap.rb new file mode 100644 index 0000000..d62f97e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/array/wrap.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +class Array + # Wraps its argument in an array unless it is already an array (or array-like). + # + # Specifically: + # + # * If the argument is +nil+ an empty array is returned. + # * Otherwise, if the argument responds to +to_ary+ it is invoked, and its result returned. + # * Otherwise, returns an array with the argument as its single element. + # + # Array.wrap(nil) # => [] + # Array.wrap([1, 2, 3]) # => [1, 2, 3] + # Array.wrap(0) # => [0] + # + # This method is similar in purpose to Kernel#Array, but there are some differences: + # + # * If the argument responds to +to_ary+ the method is invoked. Kernel#Array + # moves on to try +to_a+ if the returned value is +nil+, but Array.wrap returns + # an array with the argument as its single element right away. + # * If the returned value from +to_ary+ is neither +nil+ nor an +Array+ object, Kernel#Array + # raises an exception, while Array.wrap does not, it just returns the value. + # * It does not call +to_a+ on the argument, if the argument does not respond to +to_ary+ + # it returns an array with the argument as its single element. + # + # The last point is easily explained with some enumerables: + # + # Array(foo: :bar) # => [[:foo, :bar]] + # Array.wrap(foo: :bar) # => [{:foo=>:bar}] + # + # There's also a related idiom that uses the splat operator: + # + # [*object] + # + # which returns [] for +nil+, but calls to Array(object) otherwise. + # + # The differences with Kernel#Array explained above + # apply to the rest of objects. + def self.wrap(object) + if object.nil? + [] + elsif object.respond_to?(:to_ary) + object.to_ary || [object] + else + [object] + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/benchmark.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/benchmark.rb new file mode 100644 index 0000000..f6e1b72 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/benchmark.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require "benchmark" + +class << Benchmark + # Benchmark realtime in milliseconds. + # + # Benchmark.realtime { User.all } + # # => 8.0e-05 + # + # Benchmark.ms { User.all } + # # => 0.074 + def ms(&block) + 1000 * realtime(&block) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/big_decimal.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/big_decimal.rb new file mode 100644 index 0000000..9e6a9d6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/big_decimal.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require "active_support/core_ext/big_decimal/conversions" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/big_decimal/conversions.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/big_decimal/conversions.rb new file mode 100644 index 0000000..76ad584 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/big_decimal/conversions.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require "bigdecimal" +require "bigdecimal/util" + +module ActiveSupport + module BigDecimalWithDefaultFormat # :nodoc: + def to_s(format = "F") + super(format) + end + end +end + +BigDecimal.prepend(ActiveSupport::BigDecimalWithDefaultFormat) diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/class.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/class.rb new file mode 100644 index 0000000..1c110fd --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/class.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +require "active_support/core_ext/class/attribute" +require "active_support/core_ext/class/subclasses" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/class/attribute.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/class/attribute.rb new file mode 100644 index 0000000..ec78845 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/class/attribute.rb @@ -0,0 +1,131 @@ +# frozen_string_literal: true + +require "active_support/core_ext/module/redefine_method" + +class Class + # Declare a class-level attribute whose value is inheritable by subclasses. + # Subclasses can change their own value and it will not impact parent class. + # + # ==== Options + # + # * :instance_reader - Sets the instance reader method (defaults to true). + # * :instance_writer - Sets the instance writer method (defaults to true). + # * :instance_accessor - Sets both instance methods (defaults to true). + # * :instance_predicate - Sets a predicate method (defaults to true). + # * :default - Sets a default value for the attribute (defaults to nil). + # + # ==== Examples + # + # class Base + # class_attribute :setting + # end + # + # class Subclass < Base + # end + # + # Base.setting = true + # Subclass.setting # => true + # Subclass.setting = false + # Subclass.setting # => false + # Base.setting # => true + # + # In the above case as long as Subclass does not assign a value to setting + # by performing Subclass.setting = _something_, Subclass.setting + # would read value assigned to parent class. Once Subclass assigns a value then + # the value assigned by Subclass would be returned. + # + # This matches normal Ruby method inheritance: think of writing an attribute + # on a subclass as overriding the reader method. However, you need to be aware + # when using +class_attribute+ with mutable structures as +Array+ or +Hash+. + # In such cases, you don't want to do changes in place. Instead use setters: + # + # Base.setting = [] + # Base.setting # => [] + # Subclass.setting # => [] + # + # # Appending in child changes both parent and child because it is the same object: + # Subclass.setting << :foo + # Base.setting # => [:foo] + # Subclass.setting # => [:foo] + # + # # Use setters to not propagate changes: + # Base.setting = [] + # Subclass.setting += [:foo] + # Base.setting # => [] + # Subclass.setting # => [:foo] + # + # For convenience, an instance predicate method is defined as well. + # To skip it, pass instance_predicate: false. + # + # Subclass.setting? # => false + # + # Instances may overwrite the class value in the same way: + # + # Base.setting = true + # object = Base.new + # object.setting # => true + # object.setting = false + # object.setting # => false + # Base.setting # => true + # + # To opt out of the instance reader method, pass instance_reader: false. + # + # object.setting # => NoMethodError + # object.setting? # => NoMethodError + # + # To opt out of the instance writer method, pass instance_writer: false. + # + # object.setting = false # => NoMethodError + # + # To opt out of both instance methods, pass instance_accessor: false. + # + # To set a default value for the attribute, pass default:, like so: + # + # class_attribute :settings, default: {} + def class_attribute(*attrs, instance_accessor: true, + instance_reader: instance_accessor, instance_writer: instance_accessor, instance_predicate: true, default: nil) + + class_methods, methods = [], [] + attrs.each do |name| + unless name.is_a?(Symbol) || name.is_a?(String) + raise TypeError, "#{name.inspect} is not a symbol nor a string" + end + + class_methods << <<~RUBY # In case the method exists and is not public + silence_redefinition_of_method def #{name} + end + RUBY + + methods << <<~RUBY if instance_reader + silence_redefinition_of_method def #{name} + defined?(@#{name}) ? @#{name} : self.class.#{name} + end + RUBY + + class_methods << <<~RUBY + silence_redefinition_of_method def #{name}=(value) + redefine_method(:#{name}) { value } if singleton_class? + redefine_singleton_method(:#{name}) { value } + value + end + RUBY + + methods << <<~RUBY if instance_writer + silence_redefinition_of_method(:#{name}=) + attr_writer :#{name} + RUBY + + if instance_predicate + class_methods << "silence_redefinition_of_method def #{name}?; !!self.#{name}; end" + if instance_reader + methods << "silence_redefinition_of_method def #{name}?; !!self.#{name}; end" + end + end + end + + location = caller_locations(1, 1).first + class_eval(["class << self", *class_methods, "end", *methods].join(";").tr("\n", ";"), location.path, location.lineno) + + attrs.each { |name| public_send("#{name}=", default) } + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/class/attribute_accessors.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/class/attribute_accessors.rb new file mode 100644 index 0000000..a77354e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/class/attribute_accessors.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +# cattr_* became mattr_* aliases in 7dfbd91b0780fbd6a1dd9bfbc176e10894871d2d, +# but we keep this around for libraries that directly require it knowing they +# want cattr_*. No need to deprecate. +require "active_support/core_ext/module/attribute_accessors" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/class/subclasses.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/class/subclasses.rb new file mode 100644 index 0000000..3f4c24d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/class/subclasses.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require "active_support/ruby_features" + +class Class + if ActiveSupport::RubyFeatures::CLASS_SUBCLASSES + # Returns an array with all classes that are < than its receiver. + # + # class C; end + # C.descendants # => [] + # + # class B < C; end + # C.descendants # => [B] + # + # class A < B; end + # C.descendants # => [B, A] + # + # class D < C; end + # C.descendants # => [B, A, D] + def descendants + subclasses.concat(subclasses.flat_map(&:descendants)) + end + else + def descendants + ObjectSpace.each_object(singleton_class).reject do |k| + k.singleton_class? || k == self + end + end + end + + # Returns an array with the direct children of +self+. + # + # class Foo; end + # class Bar < Foo; end + # class Baz < Bar; end + # + # Foo.subclasses # => [Bar] + def subclasses + descendants.select { |descendant| descendant.superclass == self } + end unless ActiveSupport::RubyFeatures::CLASS_SUBCLASSES +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date.rb new file mode 100644 index 0000000..b683597 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +require "active_support/core_ext/date/acts_like" +require "active_support/core_ext/date/blank" +require "active_support/core_ext/date/calculations" +require "active_support/core_ext/date/conversions" +require "active_support/core_ext/date/deprecated_conversions" unless ENV["RAILS_DISABLE_DEPRECATED_TO_S_CONVERSION"] +require "active_support/core_ext/date/zones" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date/acts_like.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date/acts_like.rb new file mode 100644 index 0000000..c8077f3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date/acts_like.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require "active_support/core_ext/object/acts_like" + +class Date + # Duck-types as a Date-like class. See Object#acts_like?. + def acts_like_date? + true + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date/blank.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date/blank.rb new file mode 100644 index 0000000..2c59029 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date/blank.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require "date" + +class Date # :nodoc: + # No Date is blank: + # + # Date.today.blank? # => false + # + # @return [false] + def blank? + false + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date/calculations.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date/calculations.rb new file mode 100644 index 0000000..8b27d25 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date/calculations.rb @@ -0,0 +1,146 @@ +# frozen_string_literal: true + +require "date" +require "active_support/duration" +require "active_support/core_ext/object/acts_like" +require "active_support/core_ext/date/zones" +require "active_support/core_ext/time/zones" +require "active_support/core_ext/date_and_time/calculations" + +class Date + include DateAndTime::Calculations + + class << self + attr_accessor :beginning_of_week_default + + # Returns the week start (e.g. +:monday+) for the current request, if this has been set (via Date.beginning_of_week=). + # If Date.beginning_of_week has not been set for the current request, returns the week start specified in config.beginning_of_week. + # If no +config.beginning_of_week+ was specified, returns +:monday+. + def beginning_of_week + ::ActiveSupport::IsolatedExecutionState[:beginning_of_week] || beginning_of_week_default || :monday + end + + # Sets Date.beginning_of_week to a week start (e.g. +:monday+) for current request/thread. + # + # This method accepts any of the following day symbols: + # +:monday+, +:tuesday+, +:wednesday+, +:thursday+, +:friday+, +:saturday+, +:sunday+ + def beginning_of_week=(week_start) + ::ActiveSupport::IsolatedExecutionState[:beginning_of_week] = find_beginning_of_week!(week_start) + end + + # Returns week start day symbol (e.g. +:monday+), or raises an +ArgumentError+ for invalid day symbol. + def find_beginning_of_week!(week_start) + raise ArgumentError, "Invalid beginning of week: #{week_start}" unless ::Date::DAYS_INTO_WEEK.key?(week_start) + week_start + end + + # Returns a new Date representing the date 1 day ago (i.e. yesterday's date). + def yesterday + ::Date.current.yesterday + end + + # Returns a new Date representing the date 1 day after today (i.e. tomorrow's date). + def tomorrow + ::Date.current.tomorrow + end + + # Returns Time.zone.today when Time.zone or config.time_zone are set, otherwise just returns Date.today. + def current + ::Time.zone ? ::Time.zone.today : ::Date.today + end + end + + # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00) + # and then subtracts the specified number of seconds. + def ago(seconds) + in_time_zone.since(-seconds) + end + + # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00) + # and then adds the specified number of seconds + def since(seconds) + in_time_zone.since(seconds) + end + alias :in :since + + # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00) + def beginning_of_day + in_time_zone + end + alias :midnight :beginning_of_day + alias :at_midnight :beginning_of_day + alias :at_beginning_of_day :beginning_of_day + + # Converts Date to a Time (or DateTime if necessary) with the time portion set to the middle of the day (12:00) + def middle_of_day + in_time_zone.middle_of_day + end + alias :midday :middle_of_day + alias :noon :middle_of_day + alias :at_midday :middle_of_day + alias :at_noon :middle_of_day + alias :at_middle_of_day :middle_of_day + + # Converts Date to a Time (or DateTime if necessary) with the time portion set to the end of the day (23:59:59) + def end_of_day + in_time_zone.end_of_day + end + alias :at_end_of_day :end_of_day + + def plus_with_duration(other) # :nodoc: + if ActiveSupport::Duration === other + other.since(self) + else + plus_without_duration(other) + end + end + alias_method :plus_without_duration, :+ + alias_method :+, :plus_with_duration + + def minus_with_duration(other) # :nodoc: + if ActiveSupport::Duration === other + plus_with_duration(-other) + else + minus_without_duration(other) + end + end + alias_method :minus_without_duration, :- + alias_method :-, :minus_with_duration + + # Provides precise Date calculations for years, months, and days. The +options+ parameter takes a hash with + # any of these keys: :years, :months, :weeks, :days. + def advance(options) + d = self + + d = d >> options[:years] * 12 if options[:years] + d = d >> options[:months] if options[:months] + d = d + options[:weeks] * 7 if options[:weeks] + d = d + options[:days] if options[:days] + + d + end + + # Returns a new Date where one or more of the elements have been changed according to the +options+ parameter. + # The +options+ parameter is a hash with a combination of these keys: :year, :month, :day. + # + # Date.new(2007, 5, 12).change(day: 1) # => Date.new(2007, 5, 1) + # Date.new(2007, 5, 12).change(year: 2005, month: 1) # => Date.new(2005, 1, 12) + def change(options) + ::Date.new( + options.fetch(:year, year), + options.fetch(:month, month), + options.fetch(:day, day) + ) + end + + # Allow Date to be compared with Time by converting to DateTime and relying on the <=> from there. + def compare_with_coercion(other) + if other.is_a?(Time) + to_datetime <=> other + else + compare_without_coercion(other) + end + end + alias_method :compare_without_coercion, :<=> + alias_method :<=>, :compare_with_coercion +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date/conversions.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date/conversions.rb new file mode 100644 index 0000000..60112a0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date/conversions.rb @@ -0,0 +1,97 @@ +# frozen_string_literal: true + +require "date" +require "active_support/inflector/methods" +require "active_support/core_ext/date/zones" +require "active_support/core_ext/module/redefine_method" + +class Date + DATE_FORMATS = { + short: "%d %b", + long: "%B %d, %Y", + db: "%Y-%m-%d", + inspect: "%Y-%m-%d", + number: "%Y%m%d", + long_ordinal: lambda { |date| + day_format = ActiveSupport::Inflector.ordinalize(date.day) + date.strftime("%B #{day_format}, %Y") # => "April 25th, 2007" + }, + rfc822: "%d %b %Y", + iso8601: lambda { |date| date.iso8601 } + } + + # Convert to a formatted string. See DATE_FORMATS for predefined formats. + # + # This method is aliased to to_formatted_s. + # + # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007 + # + # date.to_fs(:db) # => "2007-11-10" + # date.to_formatted_s(:db) # => "2007-11-10" + # + # date.to_fs(:short) # => "10 Nov" + # date.to_fs(:number) # => "20071110" + # date.to_fs(:long) # => "November 10, 2007" + # date.to_fs(:long_ordinal) # => "November 10th, 2007" + # date.to_fs(:rfc822) # => "10 Nov 2007" + # date.to_fs(:iso8601) # => "2007-11-10" + # + # == Adding your own date formats to to_fs + # You can add your own formats to the Date::DATE_FORMATS hash. + # Use the format name as the hash key and either a strftime string + # or Proc instance that takes a date argument as the value. + # + # # config/initializers/date_formats.rb + # Date::DATE_FORMATS[:month_and_year] = '%B %Y' + # Date::DATE_FORMATS[:short_ordinal] = ->(date) { date.strftime("%B #{date.day.ordinalize}") } + def to_fs(format = :default) + if formatter = DATE_FORMATS[format] + if formatter.respond_to?(:call) + formatter.call(self).to_s + else + strftime(formatter) + end + else + to_default_s + end + end + alias_method :to_formatted_s, :to_fs + alias_method :to_default_s, :to_s + + # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005" + def readable_inspect + strftime("%a, %d %b %Y") + end + alias_method :default_inspect, :inspect + alias_method :inspect, :readable_inspect + + silence_redefinition_of_method :to_time + + # Converts a Date instance to a Time, where the time is set to the beginning of the day. + # The timezone can be either +:local+ or +:utc+ (default +:local+). + # + # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007 + # + # date.to_time # => 2007-11-10 00:00:00 0800 + # date.to_time(:local) # => 2007-11-10 00:00:00 0800 + # + # date.to_time(:utc) # => 2007-11-10 00:00:00 UTC + # + # NOTE: The +:local+ timezone is Ruby's *process* timezone, i.e. ENV['TZ']. + # If the application's timezone is needed, then use +in_time_zone+ instead. + def to_time(form = :local) + raise ArgumentError, "Expected :local or :utc, got #{form.inspect}." unless [:local, :utc].include?(form) + ::Time.public_send(form, year, month, day) + end + + silence_redefinition_of_method :xmlschema + + # Returns a string which represents the time in used time zone as DateTime + # defined by XML Schema: + # + # date = Date.new(2015, 05, 23) # => Sat, 23 May 2015 + # date.xmlschema # => "2015-05-23T00:00:00+04:00" + def xmlschema + in_time_zone.xmlschema + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date/deprecated_conversions.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date/deprecated_conversions.rb new file mode 100644 index 0000000..f983235 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date/deprecated_conversions.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require "date" + +class Date + NOT_SET = Object.new # :nodoc: + def to_s(format = NOT_SET) # :nodoc: + if formatter = DATE_FORMATS[format] + ActiveSupport::Deprecation.warn( + "Date#to_s(#{format.inspect}) is deprecated. Please use Date#to_fs(#{format.inspect}) instead." + ) + if formatter.respond_to?(:call) + formatter.call(self).to_s + else + strftime(formatter) + end + elsif format == NOT_SET + to_default_s + else + ActiveSupport::Deprecation.warn( + "Date#to_s(#{format.inspect}) is deprecated. Please use Date#to_fs(#{format.inspect}) instead." + ) + to_default_s + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date/zones.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date/zones.rb new file mode 100644 index 0000000..2dcf97c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date/zones.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +require "date" +require "active_support/core_ext/date_and_time/zones" + +class Date + include DateAndTime::Zones +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_and_time/calculations.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_and_time/calculations.rb new file mode 100644 index 0000000..a8e53c4 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_and_time/calculations.rb @@ -0,0 +1,364 @@ +# frozen_string_literal: true + +require "active_support/core_ext/object/try" +require "active_support/core_ext/date_time/conversions" + +module DateAndTime + module Calculations + DAYS_INTO_WEEK = { + sunday: 0, + monday: 1, + tuesday: 2, + wednesday: 3, + thursday: 4, + friday: 5, + saturday: 6 + } + WEEKEND_DAYS = [ 6, 0 ] + + # Returns a new date/time representing yesterday. + def yesterday + advance(days: -1) + end + + # Returns a new date/time representing tomorrow. + def tomorrow + advance(days: 1) + end + + # Returns true if the date/time is today. + def today? + to_date == ::Date.current + end + + # Returns true if the date/time is tomorrow. + def tomorrow? + to_date == ::Date.current.tomorrow + end + alias :next_day? :tomorrow? + + # Returns true if the date/time is yesterday. + def yesterday? + to_date == ::Date.current.yesterday + end + alias :prev_day? :yesterday? + + # Returns true if the date/time is in the past. + def past? + self < self.class.current + end + + # Returns true if the date/time is in the future. + def future? + self > self.class.current + end + + # Returns true if the date/time falls on a Saturday or Sunday. + def on_weekend? + WEEKEND_DAYS.include?(wday) + end + + # Returns true if the date/time does not fall on a Saturday or Sunday. + def on_weekday? + !WEEKEND_DAYS.include?(wday) + end + + # Returns true if the date/time falls before date_or_time. + def before?(date_or_time) + self < date_or_time + end + + # Returns true if the date/time falls after date_or_time. + def after?(date_or_time) + self > date_or_time + end + + # Returns a new date/time the specified number of days ago. + def days_ago(days) + advance(days: -days) + end + + # Returns a new date/time the specified number of days in the future. + def days_since(days) + advance(days: days) + end + + # Returns a new date/time the specified number of weeks ago. + def weeks_ago(weeks) + advance(weeks: -weeks) + end + + # Returns a new date/time the specified number of weeks in the future. + def weeks_since(weeks) + advance(weeks: weeks) + end + + # Returns a new date/time the specified number of months ago. + def months_ago(months) + advance(months: -months) + end + + # Returns a new date/time the specified number of months in the future. + def months_since(months) + advance(months: months) + end + + # Returns a new date/time the specified number of years ago. + def years_ago(years) + advance(years: -years) + end + + # Returns a new date/time the specified number of years in the future. + def years_since(years) + advance(years: years) + end + + # Returns a new date/time at the start of the month. + # + # today = Date.today # => Thu, 18 Jun 2015 + # today.beginning_of_month # => Mon, 01 Jun 2015 + # + # +DateTime+ objects will have a time set to 0:00. + # + # now = DateTime.current # => Thu, 18 Jun 2015 15:23:13 +0000 + # now.beginning_of_month # => Mon, 01 Jun 2015 00:00:00 +0000 + def beginning_of_month + first_hour(change(day: 1)) + end + alias :at_beginning_of_month :beginning_of_month + + # Returns a new date/time at the start of the quarter. + # + # today = Date.today # => Fri, 10 Jul 2015 + # today.beginning_of_quarter # => Wed, 01 Jul 2015 + # + # +DateTime+ objects will have a time set to 0:00. + # + # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000 + # now.beginning_of_quarter # => Wed, 01 Jul 2015 00:00:00 +0000 + def beginning_of_quarter + first_quarter_month = month - (2 + month) % 3 + beginning_of_month.change(month: first_quarter_month) + end + alias :at_beginning_of_quarter :beginning_of_quarter + + # Returns a new date/time at the end of the quarter. + # + # today = Date.today # => Fri, 10 Jul 2015 + # today.end_of_quarter # => Wed, 30 Sep 2015 + # + # +DateTime+ objects will have a time set to 23:59:59. + # + # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000 + # now.end_of_quarter # => Wed, 30 Sep 2015 23:59:59 +0000 + def end_of_quarter + last_quarter_month = month + (12 - month) % 3 + beginning_of_month.change(month: last_quarter_month).end_of_month + end + alias :at_end_of_quarter :end_of_quarter + + # Returns a new date/time at the beginning of the year. + # + # today = Date.today # => Fri, 10 Jul 2015 + # today.beginning_of_year # => Thu, 01 Jan 2015 + # + # +DateTime+ objects will have a time set to 0:00. + # + # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000 + # now.beginning_of_year # => Thu, 01 Jan 2015 00:00:00 +0000 + def beginning_of_year + change(month: 1).beginning_of_month + end + alias :at_beginning_of_year :beginning_of_year + + # Returns a new date/time representing the given day in the next week. + # + # today = Date.today # => Thu, 07 May 2015 + # today.next_week # => Mon, 11 May 2015 + # + # The +given_day_in_next_week+ defaults to the beginning of the week + # which is determined by +Date.beginning_of_week+ or +config.beginning_of_week+ + # when set. + # + # today = Date.today # => Thu, 07 May 2015 + # today.next_week(:friday) # => Fri, 15 May 2015 + # + # +DateTime+ objects have their time set to 0:00 unless +same_time+ is true. + # + # now = DateTime.current # => Thu, 07 May 2015 13:31:16 +0000 + # now.next_week # => Mon, 11 May 2015 00:00:00 +0000 + def next_week(given_day_in_next_week = Date.beginning_of_week, same_time: false) + result = first_hour(weeks_since(1).beginning_of_week.days_since(days_span(given_day_in_next_week))) + same_time ? copy_time_to(result) : result + end + + # Returns a new date/time representing the next weekday. + def next_weekday + if next_day.on_weekend? + next_week(:monday, same_time: true) + else + next_day + end + end + + # Short-hand for months_since(3). + def next_quarter + months_since(3) + end + + # Returns a new date/time representing the given day in the previous week. + # Week is assumed to start on +start_day+, default is + # +Date.beginning_of_week+ or +config.beginning_of_week+ when set. + # DateTime objects have their time set to 0:00 unless +same_time+ is true. + def prev_week(start_day = Date.beginning_of_week, same_time: false) + result = first_hour(weeks_ago(1).beginning_of_week.days_since(days_span(start_day))) + same_time ? copy_time_to(result) : result + end + alias_method :last_week, :prev_week + + # Returns a new date/time representing the previous weekday. + def prev_weekday + if prev_day.on_weekend? + copy_time_to(beginning_of_week(:friday)) + else + prev_day + end + end + alias_method :last_weekday, :prev_weekday + + # Short-hand for months_ago(1). + def last_month + months_ago(1) + end + + # Short-hand for months_ago(3). + def prev_quarter + months_ago(3) + end + alias_method :last_quarter, :prev_quarter + + # Short-hand for years_ago(1). + def last_year + years_ago(1) + end + + # Returns the number of days to the start of the week on the given day. + # Week is assumed to start on +start_day+, default is + # +Date.beginning_of_week+ or +config.beginning_of_week+ when set. + def days_to_week_start(start_day = Date.beginning_of_week) + start_day_number = DAYS_INTO_WEEK.fetch(start_day) + (wday - start_day_number) % 7 + end + + # Returns a new date/time representing the start of this week on the given day. + # Week is assumed to start on +start_day+, default is + # +Date.beginning_of_week+ or +config.beginning_of_week+ when set. + # +DateTime+ objects have their time set to 0:00. + def beginning_of_week(start_day = Date.beginning_of_week) + result = days_ago(days_to_week_start(start_day)) + acts_like?(:time) ? result.midnight : result + end + alias :at_beginning_of_week :beginning_of_week + + # Returns Monday of this week assuming that week starts on Monday. + # +DateTime+ objects have their time set to 0:00. + def monday + beginning_of_week(:monday) + end + + # Returns a new date/time representing the end of this week on the given day. + # Week is assumed to start on +start_day+, default is + # +Date.beginning_of_week+ or +config.beginning_of_week+ when set. + # DateTime objects have their time set to 23:59:59. + def end_of_week(start_day = Date.beginning_of_week) + last_hour(days_since(6 - days_to_week_start(start_day))) + end + alias :at_end_of_week :end_of_week + + # Returns Sunday of this week assuming that week starts on Monday. + # +DateTime+ objects have their time set to 23:59:59. + def sunday + end_of_week(:monday) + end + + # Returns a new date/time representing the end of the month. + # DateTime objects will have a time set to 23:59:59. + def end_of_month + last_day = ::Time.days_in_month(month, year) + last_hour(days_since(last_day - day)) + end + alias :at_end_of_month :end_of_month + + # Returns a new date/time representing the end of the year. + # DateTime objects will have a time set to 23:59:59. + def end_of_year + change(month: 12).end_of_month + end + alias :at_end_of_year :end_of_year + + # Returns a Range representing the whole day of the current date/time. + def all_day + beginning_of_day..end_of_day + end + + # Returns a Range representing the whole week of the current date/time. + # Week starts on start_day, default is Date.beginning_of_week or config.beginning_of_week when set. + def all_week(start_day = Date.beginning_of_week) + beginning_of_week(start_day)..end_of_week(start_day) + end + + # Returns a Range representing the whole month of the current date/time. + def all_month + beginning_of_month..end_of_month + end + + # Returns a Range representing the whole quarter of the current date/time. + def all_quarter + beginning_of_quarter..end_of_quarter + end + + # Returns a Range representing the whole year of the current date/time. + def all_year + beginning_of_year..end_of_year + end + + # Returns a new date/time representing the next occurrence of the specified day of week. + # + # today = Date.today # => Thu, 14 Dec 2017 + # today.next_occurring(:monday) # => Mon, 18 Dec 2017 + # today.next_occurring(:thursday) # => Thu, 21 Dec 2017 + def next_occurring(day_of_week) + from_now = DAYS_INTO_WEEK.fetch(day_of_week) - wday + from_now += 7 unless from_now > 0 + advance(days: from_now) + end + + # Returns a new date/time representing the previous occurrence of the specified day of week. + # + # today = Date.today # => Thu, 14 Dec 2017 + # today.prev_occurring(:monday) # => Mon, 11 Dec 2017 + # today.prev_occurring(:thursday) # => Thu, 07 Dec 2017 + def prev_occurring(day_of_week) + ago = wday - DAYS_INTO_WEEK.fetch(day_of_week) + ago += 7 unless ago > 0 + advance(days: -ago) + end + + private + def first_hour(date_or_time) + date_or_time.acts_like?(:time) ? date_or_time.beginning_of_day : date_or_time + end + + def last_hour(date_or_time) + date_or_time.acts_like?(:time) ? date_or_time.end_of_day : date_or_time + end + + def days_span(day) + (DAYS_INTO_WEEK.fetch(day) - DAYS_INTO_WEEK.fetch(Date.beginning_of_week)) % 7 + end + + def copy_time_to(other) + other.change(hour: hour, min: min, sec: sec, nsec: try(:nsec)) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_and_time/compatibility.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_and_time/compatibility.rb new file mode 100644 index 0000000..bc13cf9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_and_time/compatibility.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require "active_support/core_ext/module/attribute_accessors" + +module DateAndTime + module Compatibility + # If true, +to_time+ preserves the timezone offset of receiver. + # + # NOTE: With Ruby 2.4+ the default for +to_time+ changed from + # converting to the local system time, to preserving the offset + # of the receiver. For backwards compatibility we're overriding + # this behavior, but new apps will have an initializer that sets + # this to true, because the new behavior is preferred. + mattr_accessor :preserve_timezone, instance_writer: false, default: false + + # Change the output of ActiveSupport::TimeZone.utc_to_local. + # + # When +true+, it returns local times with a UTC offset, with +false+ local + # times are returned as UTC. + # + # # Given this zone: + # zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] + # + # # With `utc_to_local_returns_utc_offset_times = false`, local time is converted to UTC: + # zone.utc_to_local(Time.utc(2000, 1)) # => 1999-12-31 19:00:00 UTC + # + # # With `utc_to_local_returns_utc_offset_times = true`, local time is returned with UTC offset: + # zone.utc_to_local(Time.utc(2000, 1)) # => 1999-12-31 19:00:00 -0500 + mattr_accessor :utc_to_local_returns_utc_offset_times, instance_writer: false, default: false + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_and_time/zones.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_and_time/zones.rb new file mode 100644 index 0000000..fb6a27c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_and_time/zones.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module DateAndTime + module Zones + # Returns the simultaneous time in Time.zone if a zone is given or + # if Time.zone_default is set. Otherwise, it returns the current time. + # + # Time.zone = 'Hawaii' # => 'Hawaii' + # Time.utc(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00 + # Date.new(2000).in_time_zone # => Sat, 01 Jan 2000 00:00:00 HST -10:00 + # + # This method is similar to Time#localtime, except that it uses Time.zone as the local zone + # instead of the operating system's time zone. + # + # You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument, + # and the conversion will be based on that zone instead of Time.zone. + # + # Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00 + # Date.new(2000).in_time_zone('Alaska') # => Sat, 01 Jan 2000 00:00:00 AKST -09:00 + def in_time_zone(zone = ::Time.zone) + time_zone = ::Time.find_zone! zone + time = acts_like?(:time) ? self : nil + + if time_zone + time_with_zone(time, time_zone) + else + time || to_time + end + end + + private + def time_with_zone(time, zone) + if time + ActiveSupport::TimeWithZone.new(time.utc? ? time : time.getutc, zone) + else + ActiveSupport::TimeWithZone.new(nil, zone, to_time(:utc)) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_time.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_time.rb new file mode 100644 index 0000000..e23a8be --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_time.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +require "active_support/core_ext/date_time/acts_like" +require "active_support/core_ext/date_time/blank" +require "active_support/core_ext/date_time/calculations" +require "active_support/core_ext/date_time/compatibility" +require "active_support/core_ext/date_time/conversions" +require "active_support/core_ext/date_time/deprecated_conversions" unless ENV["RAILS_DISABLE_DEPRECATED_TO_S_CONVERSION"] diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_time/acts_like.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_time/acts_like.rb new file mode 100644 index 0000000..5dccdfe --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_time/acts_like.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require "date" +require "active_support/core_ext/object/acts_like" + +class DateTime + # Duck-types as a Date-like class. See Object#acts_like?. + def acts_like_date? + true + end + + # Duck-types as a Time-like class. See Object#acts_like?. + def acts_like_time? + true + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_time/blank.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_time/blank.rb new file mode 100644 index 0000000..d1c418c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_time/blank.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require "date" + +class DateTime # :nodoc: + # No DateTime is ever blank: + # + # DateTime.now.blank? # => false + # + # @return [false] + def blank? + false + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_time/calculations.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_time/calculations.rb new file mode 100644 index 0000000..bc670c3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_time/calculations.rb @@ -0,0 +1,211 @@ +# frozen_string_literal: true + +require "date" + +class DateTime + class << self + # Returns Time.zone.now.to_datetime when Time.zone or + # config.time_zone are set, otherwise returns + # Time.now.to_datetime. + def current + ::Time.zone ? ::Time.zone.now.to_datetime : ::Time.now.to_datetime + end + end + + # Returns the number of seconds since 00:00:00. + # + # DateTime.new(2012, 8, 29, 0, 0, 0).seconds_since_midnight # => 0 + # DateTime.new(2012, 8, 29, 12, 34, 56).seconds_since_midnight # => 45296 + # DateTime.new(2012, 8, 29, 23, 59, 59).seconds_since_midnight # => 86399 + def seconds_since_midnight + sec + (min * 60) + (hour * 3600) + end + + # Returns the number of seconds until 23:59:59. + # + # DateTime.new(2012, 8, 29, 0, 0, 0).seconds_until_end_of_day # => 86399 + # DateTime.new(2012, 8, 29, 12, 34, 56).seconds_until_end_of_day # => 41103 + # DateTime.new(2012, 8, 29, 23, 59, 59).seconds_until_end_of_day # => 0 + def seconds_until_end_of_day + end_of_day.to_i - to_i + end + + # Returns the fraction of a second as a +Rational+ + # + # DateTime.new(2012, 8, 29, 0, 0, 0.5).subsec # => (1/2) + def subsec + sec_fraction + end + + # Returns a new DateTime where one or more of the elements have been changed + # according to the +options+ parameter. The time options (:hour, + # :min, :sec) reset cascadingly, so if only the hour is + # passed, then minute and sec is set to 0. If the hour and minute is passed, + # then sec is set to 0. The +options+ parameter takes a hash with any of these + # keys: :year, :month, :day, :hour, + # :min, :sec, :offset, :start. + # + # DateTime.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => DateTime.new(2012, 8, 1, 22, 35, 0) + # DateTime.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => DateTime.new(1981, 8, 1, 22, 35, 0) + # DateTime.new(2012, 8, 29, 22, 35, 0).change(year: 1981, hour: 0) # => DateTime.new(1981, 8, 29, 0, 0, 0) + def change(options) + if new_nsec = options[:nsec] + raise ArgumentError, "Can't change both :nsec and :usec at the same time: #{options.inspect}" if options[:usec] + new_fraction = Rational(new_nsec, 1000000000) + else + new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000)) + new_fraction = Rational(new_usec, 1000000) + end + + raise ArgumentError, "argument out of range" if new_fraction >= 1 + + ::DateTime.civil( + options.fetch(:year, year), + options.fetch(:month, month), + options.fetch(:day, day), + options.fetch(:hour, hour), + options.fetch(:min, options[:hour] ? 0 : min), + options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec) + new_fraction, + options.fetch(:offset, offset), + options.fetch(:start, start) + ) + end + + # Uses Date to provide precise Time calculations for years, months, and days. + # The +options+ parameter takes a hash with any of these keys: :years, + # :months, :weeks, :days, :hours, + # :minutes, :seconds. + def advance(options) + unless options[:weeks].nil? + options[:weeks], partial_weeks = options[:weeks].divmod(1) + options[:days] = options.fetch(:days, 0) + 7 * partial_weeks + end + + unless options[:days].nil? + options[:days], partial_days = options[:days].divmod(1) + options[:hours] = options.fetch(:hours, 0) + 24 * partial_days + end + + d = to_date.advance(options) + datetime_advanced_by_date = change(year: d.year, month: d.month, day: d.day) + seconds_to_advance = \ + options.fetch(:seconds, 0) + + options.fetch(:minutes, 0) * 60 + + options.fetch(:hours, 0) * 3600 + + if seconds_to_advance.zero? + datetime_advanced_by_date + else + datetime_advanced_by_date.since(seconds_to_advance) + end + end + + # Returns a new DateTime representing the time a number of seconds ago. + # Do not use this method in combination with x.months, use months_ago instead! + def ago(seconds) + since(-seconds) + end + + # Returns a new DateTime representing the time a number of seconds since the + # instance time. Do not use this method in combination with x.months, use + # months_since instead! + def since(seconds) + self + Rational(seconds, 86400) + end + alias :in :since + + # Returns a new DateTime representing the start of the day (0:00). + def beginning_of_day + change(hour: 0) + end + alias :midnight :beginning_of_day + alias :at_midnight :beginning_of_day + alias :at_beginning_of_day :beginning_of_day + + # Returns a new DateTime representing the middle of the day (12:00) + def middle_of_day + change(hour: 12) + end + alias :midday :middle_of_day + alias :noon :middle_of_day + alias :at_midday :middle_of_day + alias :at_noon :middle_of_day + alias :at_middle_of_day :middle_of_day + + # Returns a new DateTime representing the end of the day (23:59:59). + def end_of_day + change(hour: 23, min: 59, sec: 59, usec: Rational(999999999, 1000)) + end + alias :at_end_of_day :end_of_day + + # Returns a new DateTime representing the start of the hour (hh:00:00). + def beginning_of_hour + change(min: 0) + end + alias :at_beginning_of_hour :beginning_of_hour + + # Returns a new DateTime representing the end of the hour (hh:59:59). + def end_of_hour + change(min: 59, sec: 59, usec: Rational(999999999, 1000)) + end + alias :at_end_of_hour :end_of_hour + + # Returns a new DateTime representing the start of the minute (hh:mm:00). + def beginning_of_minute + change(sec: 0) + end + alias :at_beginning_of_minute :beginning_of_minute + + # Returns a new DateTime representing the end of the minute (hh:mm:59). + def end_of_minute + change(sec: 59, usec: Rational(999999999, 1000)) + end + alias :at_end_of_minute :end_of_minute + + # Returns a Time instance of the simultaneous time in the system timezone. + def localtime(utc_offset = nil) + utc = new_offset(0) + + Time.utc( + utc.year, utc.month, utc.day, + utc.hour, utc.min, utc.sec + utc.sec_fraction + ).getlocal(utc_offset) + end + alias_method :getlocal, :localtime + + # Returns a Time instance of the simultaneous time in the UTC timezone. + # + # DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)) # => Mon, 21 Feb 2005 10:11:12 -0600 + # DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc # => Mon, 21 Feb 2005 16:11:12 UTC + def utc + utc = new_offset(0) + + Time.utc( + utc.year, utc.month, utc.day, + utc.hour, utc.min, utc.sec + utc.sec_fraction + ) + end + alias_method :getgm, :utc + alias_method :getutc, :utc + alias_method :gmtime, :utc + + # Returns +true+ if offset == 0. + def utc? + offset == 0 + end + + # Returns the offset value in seconds. + def utc_offset + (offset * 86400).to_i + end + + # Layers additional behavior on DateTime#<=> so that Time and + # ActiveSupport::TimeWithZone instances can be compared with a DateTime. + def <=>(other) + if other.respond_to? :to_datetime + super other.to_datetime rescue nil + else + super + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_time/compatibility.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_time/compatibility.rb new file mode 100644 index 0000000..7600a06 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_time/compatibility.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require "active_support/core_ext/date_and_time/compatibility" +require "active_support/core_ext/module/redefine_method" + +class DateTime + include DateAndTime::Compatibility + + silence_redefinition_of_method :to_time + + # Either return an instance of +Time+ with the same UTC offset + # as +self+ or an instance of +Time+ representing the same time + # in the local system timezone depending on the setting of + # on the setting of +ActiveSupport.to_time_preserves_timezone+. + def to_time + preserve_timezone ? getlocal(utc_offset) : getlocal + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_time/conversions.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_time/conversions.rb new file mode 100644 index 0000000..be1c472 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_time/conversions.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +require "date" +require "active_support/inflector/methods" +require "active_support/core_ext/time/conversions" +require "active_support/core_ext/date_time/calculations" +require "active_support/values/time_zone" + +class DateTime + # Convert to a formatted string. See Time::DATE_FORMATS for predefined formats. + # + # This method is aliased to to_formatted_s. + # + # === Examples + # datetime = DateTime.civil(2007, 12, 4, 0, 0, 0, 0) # => Tue, 04 Dec 2007 00:00:00 +0000 + # + # datetime.to_fs(:db) # => "2007-12-04 00:00:00" + # datetime.to_formatted_s(:db) # => "2007-12-04 00:00:00" + # datetime.to_fs(:number) # => "20071204000000" + # datetime.to_fs(:short) # => "04 Dec 00:00" + # datetime.to_fs(:long) # => "December 04, 2007 00:00" + # datetime.to_fs(:long_ordinal) # => "December 4th, 2007 00:00" + # datetime.to_fs(:rfc822) # => "Tue, 04 Dec 2007 00:00:00 +0000" + # datetime.to_fs(:iso8601) # => "2007-12-04T00:00:00+00:00" + # + # == Adding your own datetime formats to to_fs + # DateTime formats are shared with Time. You can add your own to the + # Time::DATE_FORMATS hash. Use the format name as the hash key and + # either a strftime string or Proc instance that takes a time or + # datetime argument as the value. + # + # # config/initializers/time_formats.rb + # Time::DATE_FORMATS[:month_and_year] = '%B %Y' + # Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") } + def to_fs(format = :default) + if formatter = ::Time::DATE_FORMATS[format] + formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter) + else + to_default_s + end + end + alias_method :to_formatted_s, :to_fs + alias_method :to_default_s, :to_s if instance_methods(false).include?(:to_s) + + # Returns a formatted string of the offset from UTC, or an alternative + # string if the time zone is already UTC. + # + # datetime = DateTime.civil(2000, 1, 1, 0, 0, 0, Rational(-6, 24)) + # datetime.formatted_offset # => "-06:00" + # datetime.formatted_offset(false) # => "-0600" + def formatted_offset(colon = true, alternate_utc_string = nil) + utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon) + end + + # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005 14:30:00 +0000". + def readable_inspect + to_fs(:rfc822) + end + alias_method :default_inspect, :inspect + alias_method :inspect, :readable_inspect + + # Returns DateTime with local offset for given year if format is local else + # offset is zero. + # + # DateTime.civil_from_format :local, 2012 + # # => Sun, 01 Jan 2012 00:00:00 +0300 + # DateTime.civil_from_format :local, 2012, 12, 17 + # # => Mon, 17 Dec 2012 00:00:00 +0000 + def self.civil_from_format(utc_or_local, year, month = 1, day = 1, hour = 0, min = 0, sec = 0) + if utc_or_local.to_sym == :local + offset = ::Time.local(year, month, day).utc_offset.to_r / 86400 + else + offset = 0 + end + civil(year, month, day, hour, min, sec, offset) + end + + # Converts +self+ to a floating-point number of seconds, including fractional microseconds, since the Unix epoch. + def to_f + seconds_since_unix_epoch.to_f + sec_fraction + end + + # Converts +self+ to an integer number of seconds since the Unix epoch. + def to_i + seconds_since_unix_epoch.to_i + end + + # Returns the fraction of a second as microseconds + def usec + (sec_fraction * 1_000_000).to_i + end + + # Returns the fraction of a second as nanoseconds + def nsec + (sec_fraction * 1_000_000_000).to_i + end + + private + def offset_in_seconds + (offset * 86400).to_i + end + + def seconds_since_unix_epoch + (jd - 2440588) * 86400 - offset_in_seconds + seconds_since_midnight + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_time/deprecated_conversions.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_time/deprecated_conversions.rb new file mode 100644 index 0000000..7380839 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/date_time/deprecated_conversions.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require "date" + +class DateTime + NOT_SET = Object.new # :nodoc: + def to_s(format = NOT_SET) # :nodoc: + if formatter = ::Time::DATE_FORMATS[format] + ActiveSupport::Deprecation.warn( + "DateTime#to_s(#{format.inspect}) is deprecated. Please use DateTime#to_fs(#{format.inspect}) instead." + ) + formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter) + elsif format == NOT_SET + to_default_s + else + ActiveSupport::Deprecation.warn( + "DateTime#to_s(#{format.inspect}) is deprecated. Please use DateTime#to_fs(#{format.inspect}) instead." + ) + to_default_s + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/digest.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/digest.rb new file mode 100644 index 0000000..ce1427e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/digest.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require "active_support/core_ext/digest/uuid" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/digest/uuid.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/digest/uuid.rb new file mode 100644 index 0000000..3546932 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/digest/uuid.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require "securerandom" +require "openssl" + +module Digest + module UUID + DNS_NAMESPACE = "k\xA7\xB8\x10\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc: + URL_NAMESPACE = "k\xA7\xB8\x11\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc: + OID_NAMESPACE = "k\xA7\xB8\x12\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc: + X500_NAMESPACE = "k\xA7\xB8\x14\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc: + + mattr_accessor :use_rfc4122_namespaced_uuids, instance_accessor: false, default: false + + # Generates a v5 non-random UUID (Universally Unique IDentifier). + # + # Using OpenSSL::Digest::MD5 generates version 3 UUIDs; OpenSSL::Digest::SHA1 generates version 5 UUIDs. + # uuid_from_hash always generates the same UUID for a given name and namespace combination. + # + # See RFC 4122 for details of UUID at: https://www.ietf.org/rfc/rfc4122.txt + def self.uuid_from_hash(hash_class, namespace, name) + if hash_class == Digest::MD5 || hash_class == OpenSSL::Digest::MD5 + version = 3 + elsif hash_class == Digest::SHA1 || hash_class == OpenSSL::Digest::SHA1 + version = 5 + else + raise ArgumentError, "Expected OpenSSL::Digest::SHA1 or OpenSSL::Digest::MD5, got #{hash_class.name}." + end + + uuid_namespace = pack_uuid_namespace(namespace) + + hash = hash_class.new + hash.update(uuid_namespace) + hash.update(name) + + ary = hash.digest.unpack("NnnnnN") + ary[2] = (ary[2] & 0x0FFF) | (version << 12) + ary[3] = (ary[3] & 0x3FFF) | 0x8000 + + "%08x-%04x-%04x-%04x-%04x%08x" % ary + end + + # Convenience method for uuid_from_hash using OpenSSL::Digest::MD5. + def self.uuid_v3(uuid_namespace, name) + uuid_from_hash(OpenSSL::Digest::MD5, uuid_namespace, name) + end + + # Convenience method for uuid_from_hash using OpenSSL::Digest::SHA1. + def self.uuid_v5(uuid_namespace, name) + uuid_from_hash(OpenSSL::Digest::SHA1, uuid_namespace, name) + end + + # Convenience method for SecureRandom.uuid. + def self.uuid_v4 + SecureRandom.uuid + end + + def self.pack_uuid_namespace(namespace) + if [DNS_NAMESPACE, OID_NAMESPACE, URL_NAMESPACE, X500_NAMESPACE].include?(namespace) + namespace + elsif use_rfc4122_namespaced_uuids == true + match_data = namespace.match(/\A(\h{8})-(\h{4})-(\h{4})-(\h{4})-(\h{4})(\h{8})\z/) + + raise ArgumentError, "Only UUIDs are valid namespace identifiers" unless match_data.present? + + match_data.captures.map { |s| s.to_i(16) }.pack("NnnnnN") + else + ActiveSupport::Deprecation.warn <<~WARNING.squish + Providing a namespace ID that is not one of the constants defined on Digest::UUID generates an incorrect UUID value according to RFC 4122. + To enable the correct behavior, set the Rails.application.config.active_support.use_rfc4122_namespaced_uuids configuration option to true. + WARNING + + namespace + end + end + + private_class_method :pack_uuid_namespace + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/enumerable.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/enumerable.rb new file mode 100644 index 0000000..78a8da2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/enumerable.rb @@ -0,0 +1,329 @@ +# frozen_string_literal: true + +module ActiveSupport + module EnumerableCoreExt # :nodoc: + module Constants + private + def const_missing(name) + if name == :SoleItemExpectedError + ::ActiveSupport::EnumerableCoreExt::SoleItemExpectedError + else + super + end + end + end + end +end + +module Enumerable + # Error generated by +sole+ when called on an enumerable that doesn't have + # exactly one item. + class SoleItemExpectedError < StandardError; end + + # HACK: For performance reasons, Enumerable shouldn't have any constants of its own. + # So we move SoleItemExpectedError into ActiveSupport::EnumerableCoreExt. + ActiveSupport::EnumerableCoreExt::SoleItemExpectedError = remove_const(:SoleItemExpectedError) + singleton_class.prepend(ActiveSupport::EnumerableCoreExt::Constants) + + # Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements + # when we omit an identity. + + # :stopdoc: + + # We can't use Refinements here because Refinements with Module which will be prepended + # doesn't work well https://bugs.ruby-lang.org/issues/13446 + alias :_original_sum_with_required_identity :sum + private :_original_sum_with_required_identity + + # :startdoc: + + # Calculates the minimum from the extracted elements. + # + # payments = [Payment.new(5), Payment.new(15), Payment.new(10)] + # payments.minimum(:price) # => 5 + def minimum(key) + map(&key).min + end + + # Calculates the maximum from the extracted elements. + # + # payments = [Payment.new(5), Payment.new(15), Payment.new(10)] + # payments.maximum(:price) # => 15 + def maximum(key) + map(&key).max + end + + # Calculates a sum from the elements. + # + # payments.sum { |p| p.price * p.tax_rate } + # payments.sum(&:price) + # + # The latter is a shortcut for: + # + # payments.inject(0) { |sum, p| sum + p.price } + # + # It can also calculate the sum without the use of a block. + # + # [5, 15, 10].sum # => 30 + # ['foo', 'bar'].sum('') # => "foobar" + # [[1, 2], [3, 1, 5]].sum([]) # => [1, 2, 3, 1, 5] + # + # The default sum of an empty list is zero. You can override this default: + # + # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0) + def sum(identity = nil, &block) + if identity + _original_sum_with_required_identity(identity, &block) + elsif block_given? + map(&block).sum + # we check `first(1) == []` to check if we have an + # empty Enumerable; checking `empty?` would return + # true for `[nil]`, which we want to deprecate to + # keep consistent with Ruby + elsif first.is_a?(Numeric) || first(1) == [] + identity ||= 0 + _original_sum_with_required_identity(identity, &block) + else + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Rails 7.0 has deprecated Enumerable.sum in favor of Ruby's native implementation available since 2.4. + Sum of non-numeric elements requires an initial argument. + MSG + inject(:+) || 0 + end + end + + # Convert an enumerable to a hash, using the block result as the key and the + # element as the value. + # + # people.index_by(&:login) + # # => { "nextangle" => , "chade-" => , ...} + # + # people.index_by { |person| "#{person.first_name} #{person.last_name}" } + # # => { "Chade- Fowlersburg-e" => , "David Heinemeier Hansson" => , ...} + def index_by + if block_given? + result = {} + each { |elem| result[yield(elem)] = elem } + result + else + to_enum(:index_by) { size if respond_to?(:size) } + end + end + + # Convert an enumerable to a hash, using the element as the key and the block + # result as the value. + # + # post = Post.new(title: "hey there", body: "what's up?") + # + # %i( title body ).index_with { |attr_name| post.public_send(attr_name) } + # # => { title: "hey there", body: "what's up?" } + # + # If an argument is passed instead of a block, it will be used as the value + # for all elements: + # + # %i( created_at updated_at ).index_with(Time.now) + # # => { created_at: 2020-03-09 22:31:47, updated_at: 2020-03-09 22:31:47 } + def index_with(default = (no_default = true)) + if block_given? + result = {} + each { |elem| result[elem] = yield(elem) } + result + elsif no_default + to_enum(:index_with) { size if respond_to?(:size) } + else + result = {} + each { |elem| result[elem] = default } + result + end + end + + # Returns +true+ if the enumerable has more than 1 element. Functionally + # equivalent to enum.to_a.size > 1. Can be called with a block too, + # much like any?, so people.many? { |p| p.age > 26 } returns +true+ + # if more than one person is over 26. + def many? + cnt = 0 + if block_given? + any? do |element| + cnt += 1 if yield element + cnt > 1 + end + else + any? { (cnt += 1) > 1 } + end + end + + # Returns a new array that includes the passed elements. + # + # [ 1, 2, 3 ].including(4, 5) + # # => [ 1, 2, 3, 4, 5 ] + # + # ["David", "Rafael"].including %w[ Aaron Todd ] + # # => ["David", "Rafael", "Aaron", "Todd"] + def including(*elements) + to_a.including(*elements) + end + + # The negative of the Enumerable#include?. Returns +true+ if the + # collection does not include the object. + def exclude?(object) + !include?(object) + end + + # Returns a copy of the enumerable excluding the specified elements. + # + # ["David", "Rafael", "Aaron", "Todd"].excluding "Aaron", "Todd" + # # => ["David", "Rafael"] + # + # ["David", "Rafael", "Aaron", "Todd"].excluding %w[ Aaron Todd ] + # # => ["David", "Rafael"] + # + # {foo: 1, bar: 2, baz: 3}.excluding :bar + # # => {foo: 1, baz: 3} + def excluding(*elements) + elements.flatten!(1) + reject { |element| elements.include?(element) } + end + alias :without :excluding + + # Extract the given key from each element in the enumerable. + # + # [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name) + # # => ["David", "Rafael", "Aaron"] + # + # [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pluck(:id, :name) + # # => [[1, "David"], [2, "Rafael"]] + def pluck(*keys) + if keys.many? + map { |element| keys.map { |key| element[key] } } + else + key = keys.first + map { |element| element[key] } + end + end + + # Extract the given key from the first element in the enumerable. + # + # [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pick(:name) + # # => "David" + # + # [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pick(:id, :name) + # # => [1, "David"] + def pick(*keys) + return if none? + + if keys.many? + keys.map { |key| first[key] } + else + first[keys.first] + end + end + + # Returns a new +Array+ without the blank items. + # Uses Object#blank? for determining if an item is blank. + # + # [1, "", nil, 2, " ", [], {}, false, true].compact_blank + # # => [1, 2, true] + # + # Set.new([nil, "", 1, 2]) + # # => [2, 1] (or [1, 2]) + # + # When called on a +Hash+, returns a new +Hash+ without the blank values. + # + # { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank + # # => { b: 1, f: true } + def compact_blank + reject(&:blank?) + end + + # Returns a new +Array+ where the order has been set to that provided in the +series+, based on the +key+ of the + # objects in the original enumerable. + # + # [ Person.find(5), Person.find(3), Person.find(1) ].in_order_of(:id, [ 1, 5, 3 ]) + # # => [ Person.find(1), Person.find(5), Person.find(3) ] + # + # If the +series+ include keys that have no corresponding element in the Enumerable, these are ignored. + # If the Enumerable has additional elements that aren't named in the +series+, these are not included in the result. + def in_order_of(key, series) + index_by(&key).values_at(*series).compact + end + + # Returns the sole item in the enumerable. If there are no items, or more + # than one item, raises +Enumerable::SoleItemExpectedError+. + # + # ["x"].sole # => "x" + # Set.new.sole # => Enumerable::SoleItemExpectedError: no item found + # { a: 1, b: 2 }.sole # => Enumerable::SoleItemExpectedError: multiple items found + def sole + case count + when 1 then return first # rubocop:disable Style/RedundantReturn + when 0 then raise ActiveSupport::EnumerableCoreExt::SoleItemExpectedError, "no item found" + when 2.. then raise ActiveSupport::EnumerableCoreExt::SoleItemExpectedError, "multiple items found" + end + end +end + +class Hash + # Hash#reject has its own definition, so this needs one too. + def compact_blank # :nodoc: + reject { |_k, v| v.blank? } + end + + # Removes all blank values from the +Hash+ in place and returns self. + # Uses Object#blank? for determining if a value is blank. + # + # h = { a: "", b: 1, c: nil, d: [], e: false, f: true } + # h.compact_blank! + # # => { b: 1, f: true } + def compact_blank! + # use delete_if rather than reject! because it always returns self even if nothing changed + delete_if { |_k, v| v.blank? } + end +end + +class Range # :nodoc: + # Optimize range sum to use arithmetic progression if a block is not given and + # we have a range of numeric values. + def sum(identity = nil) + if block_given? || !(first.is_a?(Integer) && last.is_a?(Integer)) + super + else + actual_last = exclude_end? ? (last - 1) : last + if actual_last >= first + sum = identity || 0 + sum + (actual_last - first + 1) * (actual_last + first) / 2 + else + identity || 0 + end + end + end +end + +# Using Refinements here in order not to expose our internal method +using Module.new { + refine Array do + alias :orig_sum :sum + end +} + +class Array # :nodoc: + def sum(init = nil, &block) + if init.is_a?(Numeric) || first.is_a?(Numeric) + init ||= 0 + orig_sum(init, &block) + else + super + end + end + + # Removes all blank elements from the +Array+ in place and returns self. + # Uses Object#blank? for determining if an item is blank. + # + # a = [1, "", nil, 2, " ", [], {}, false, true] + # a.compact_blank! + # # => [1, 2, true] + def compact_blank! + # use delete_if rather than reject! because it always returns self even if nothing changed + delete_if(&:blank?) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/file.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/file.rb new file mode 100644 index 0000000..64553bf --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/file.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require "active_support/core_ext/file/atomic" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/file/atomic.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/file/atomic.rb new file mode 100644 index 0000000..b442ea3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/file/atomic.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require "fileutils" + +class File + # Write to a file atomically. Useful for situations where you don't + # want other processes or threads to see half-written files. + # + # File.atomic_write('important.file') do |file| + # file.write('hello') + # end + # + # This method needs to create a temporary file. By default it will create it + # in the same directory as the destination file. If you don't like this + # behavior you can provide a different directory but it must be on the + # same physical filesystem as the file you're trying to write. + # + # File.atomic_write('/data/something.important', '/data/tmp') do |file| + # file.write('hello') + # end + def self.atomic_write(file_name, temp_dir = dirname(file_name)) + require "tempfile" unless defined?(Tempfile) + + Tempfile.open(".#{basename(file_name)}", temp_dir) do |temp_file| + temp_file.binmode + return_val = yield temp_file + temp_file.close + + old_stat = if exist?(file_name) + # Get original file permissions + stat(file_name) + else + # If not possible, probe which are the default permissions in the + # destination directory. + probe_stat_in(dirname(file_name)) + end + + if old_stat + # Set correct permissions on new file + begin + chown(old_stat.uid, old_stat.gid, temp_file.path) + # This operation will affect filesystem ACL's + chmod(old_stat.mode, temp_file.path) + rescue Errno::EPERM, Errno::EACCES + # Changing file ownership failed, moving on. + end + end + + # Overwrite original file with temp file + rename(temp_file.path, file_name) + return_val + end + end + + # Private utility method. + def self.probe_stat_in(dir) # :nodoc: + basename = [ + ".permissions_check", + Thread.current.object_id, + Process.pid, + rand(1000000) + ].join(".") + + file_name = join(dir, basename) + FileUtils.touch(file_name) + stat(file_name) + rescue Errno::ENOENT + file_name = nil + ensure + FileUtils.rm_f(file_name) if file_name + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash.rb new file mode 100644 index 0000000..2f0901d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require "active_support/core_ext/hash/conversions" +require "active_support/core_ext/hash/deep_merge" +require "active_support/core_ext/hash/deep_transform_values" +require "active_support/core_ext/hash/except" +require "active_support/core_ext/hash/indifferent_access" +require "active_support/core_ext/hash/keys" +require "active_support/core_ext/hash/reverse_merge" +require "active_support/core_ext/hash/slice" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/conversions.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/conversions.rb new file mode 100644 index 0000000..9a1db92 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/conversions.rb @@ -0,0 +1,262 @@ +# frozen_string_literal: true + +require "active_support/core_ext/object/blank" +require "active_support/core_ext/object/to_param" +require "active_support/core_ext/object/to_query" +require "active_support/core_ext/object/try" +require "active_support/core_ext/array/wrap" +require "active_support/core_ext/hash/reverse_merge" +require "active_support/core_ext/string/inflections" + +class Hash + # Returns a string containing an XML representation of its receiver: + # + # { foo: 1, bar: 2 }.to_xml + # # => + # # + # # + # # 1 + # # 2 + # # + # + # To do so, the method loops over the pairs and builds nodes that depend on + # the _values_. Given a pair +key+, +value+: + # + # * If +value+ is a hash there's a recursive call with +key+ as :root. + # + # * If +value+ is an array there's a recursive call with +key+ as :root, + # and +key+ singularized as :children. + # + # * If +value+ is a callable object it must expect one or two arguments. Depending + # on the arity, the callable is invoked with the +options+ hash as first argument + # with +key+ as :root, and +key+ singularized as second argument. The + # callable can add nodes by using options[:builder]. + # + # {foo: lambda { |options, key| options[:builder].b(key) }}.to_xml + # # => "foo" + # + # * If +value+ responds to +to_xml+ the method is invoked with +key+ as :root. + # + # class Foo + # def to_xml(options) + # options[:builder].bar 'fooing!' + # end + # end + # + # { foo: Foo.new }.to_xml(skip_instruct: true) + # # => + # # + # # fooing! + # # + # + # * Otherwise, a node with +key+ as tag is created with a string representation of + # +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added. + # Unless the option :skip_types exists and is true, an attribute "type" is + # added as well according to the following mapping: + # + # XML_TYPE_NAMES = { + # "Symbol" => "symbol", + # "Integer" => "integer", + # "BigDecimal" => "decimal", + # "Float" => "float", + # "TrueClass" => "boolean", + # "FalseClass" => "boolean", + # "Date" => "date", + # "DateTime" => "dateTime", + # "Time" => "dateTime" + # } + # + # By default the root node is "hash", but that's configurable via the :root option. + # + # The default XML builder is a fresh instance of Builder::XmlMarkup. You can + # configure your own builder with the :builder option. The method also accepts + # options like :dasherize and friends, they are forwarded to the builder. + def to_xml(options = {}) + require "active_support/builder" unless defined?(Builder::XmlMarkup) + + options = options.dup + options[:indent] ||= 2 + options[:root] ||= "hash" + options[:builder] ||= Builder::XmlMarkup.new(indent: options[:indent]) + + builder = options[:builder] + builder.instruct! unless options.delete(:skip_instruct) + + root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options) + + builder.tag!(root) do + each { |key, value| ActiveSupport::XmlMini.to_tag(key, value, options) } + yield builder if block_given? + end + end + + class << self + # Returns a Hash containing a collection of pairs when the key is the node name and the value is + # its content + # + # xml = <<-XML + # + # + # 1 + # 2 + # + # XML + # + # hash = Hash.from_xml(xml) + # # => {"hash"=>{"foo"=>1, "bar"=>2}} + # + # +DisallowedType+ is raised if the XML contains attributes with type="yaml" or + # type="symbol". Use Hash.from_trusted_xml to + # parse this XML. + # + # Custom +disallowed_types+ can also be passed in the form of an + # array. + # + # xml = <<-XML + # + # + # 1 + # "David" + # + # XML + # + # hash = Hash.from_xml(xml, ['integer']) + # # => ActiveSupport::XMLConverter::DisallowedType: Disallowed type attribute: "integer" + # + # Note that passing custom disallowed types will override the default types, + # which are Symbol and YAML. + def from_xml(xml, disallowed_types = nil) + ActiveSupport::XMLConverter.new(xml, disallowed_types).to_h + end + + # Builds a Hash from XML just like Hash.from_xml, but also allows Symbol and YAML. + def from_trusted_xml(xml) + from_xml xml, [] + end + end +end + +module ActiveSupport + class XMLConverter # :nodoc: + # Raised if the XML contains attributes with type="yaml" or + # type="symbol". Read Hash#from_xml for more details. + class DisallowedType < StandardError + def initialize(type) + super "Disallowed type attribute: #{type.inspect}" + end + end + + DISALLOWED_TYPES = %w(symbol yaml) + + def initialize(xml, disallowed_types = nil) + @xml = normalize_keys(XmlMini.parse(xml)) + @disallowed_types = disallowed_types || DISALLOWED_TYPES + end + + def to_h + deep_to_h(@xml) + end + + private + def normalize_keys(params) + case params + when Hash + Hash[params.map { |k, v| [k.to_s.tr("-", "_"), normalize_keys(v)] } ] + when Array + params.map { |v| normalize_keys(v) } + else + params + end + end + + def deep_to_h(value) + case value + when Hash + process_hash(value) + when Array + process_array(value) + when String + value + else + raise "can't typecast #{value.class.name} - #{value.inspect}" + end + end + + def process_hash(value) + if value.include?("type") && !value["type"].is_a?(Hash) && @disallowed_types.include?(value["type"]) + raise DisallowedType, value["type"] + end + + if become_array?(value) + _, entries = Array.wrap(value.detect { |k, v| not v.is_a?(String) }) + if entries.nil? || value["__content__"].try(:empty?) + [] + else + case entries + when Array + entries.collect { |v| deep_to_h(v) } + when Hash + [deep_to_h(entries)] + else + raise "can't typecast #{entries.inspect}" + end + end + elsif become_content?(value) + process_content(value) + + elsif become_empty_string?(value) + "" + elsif become_hash?(value) + xml_value = value.transform_values { |v| deep_to_h(v) } + + # Turn { files: { file: # } } into { files: # } so it is compatible with + # how multipart uploaded files from HTML appear + xml_value["file"].is_a?(StringIO) ? xml_value["file"] : xml_value + end + end + + def become_content?(value) + value["type"] == "file" || (value["__content__"] && (value.keys.size == 1 || value["__content__"].present?)) + end + + def become_array?(value) + value["type"] == "array" + end + + def become_empty_string?(value) + # { "string" => true } + # No tests fail when the second term is removed. + value["type"] == "string" && value["nil"] != "true" + end + + def become_hash?(value) + !nothing?(value) && !garbage?(value) + end + + def nothing?(value) + # blank or nil parsed values are represented by nil + value.blank? || value["nil"] == "true" + end + + def garbage?(value) + # If the type is the only element which makes it then + # this still makes the value nil, except if type is + # an XML node(where type['value'] is a Hash) + value["type"] && !value["type"].is_a?(::Hash) && value.size == 1 + end + + def process_content(value) + content = value["__content__"] + if parser = ActiveSupport::XmlMini::PARSING[value["type"]] + parser.arity == 1 ? parser.call(content) : parser.call(content, value) + else + content + end + end + + def process_array(value) + value.map! { |i| deep_to_h(i) } + value.length > 1 ? value : value.first + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/deep_merge.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/deep_merge.rb new file mode 100644 index 0000000..9bc50b7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/deep_merge.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +class Hash + # Returns a new hash with +self+ and +other_hash+ merged recursively. + # + # h1 = { a: true, b: { c: [1, 2, 3] } } + # h2 = { a: false, b: { x: [3, 4, 5] } } + # + # h1.deep_merge(h2) # => { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } } + # + # Like with Hash#merge in the standard library, a block can be provided + # to merge values: + # + # h1 = { a: 100, b: 200, c: { c1: 100 } } + # h2 = { b: 250, c: { c1: 200 } } + # h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val } + # # => { a: 100, b: 450, c: { c1: 300 } } + def deep_merge(other_hash, &block) + dup.deep_merge!(other_hash, &block) + end + + # Same as +deep_merge+, but modifies +self+. + def deep_merge!(other_hash, &block) + merge!(other_hash) do |key, this_val, other_val| + if this_val.is_a?(Hash) && other_val.is_a?(Hash) + this_val.deep_merge(other_val, &block) + elsif block_given? + block.call(key, this_val, other_val) + else + other_val + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/deep_transform_values.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/deep_transform_values.rb new file mode 100644 index 0000000..8ad85c0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/deep_transform_values.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +class Hash + # Returns a new hash with all values converted by the block operation. + # This includes the values from the root hash and from all + # nested hashes and arrays. + # + # hash = { person: { name: 'Rob', age: '28' } } + # + # hash.deep_transform_values{ |value| value.to_s.upcase } + # # => {person: {name: "ROB", age: "28"}} + def deep_transform_values(&block) + _deep_transform_values_in_object(self, &block) + end + + # Destructively converts all values by using the block operation. + # This includes the values from the root hash and from all + # nested hashes and arrays. + def deep_transform_values!(&block) + _deep_transform_values_in_object!(self, &block) + end + + private + # Support methods for deep transforming nested hashes and arrays. + def _deep_transform_values_in_object(object, &block) + case object + when Hash + object.transform_values { |value| _deep_transform_values_in_object(value, &block) } + when Array + object.map { |e| _deep_transform_values_in_object(e, &block) } + else + yield(object) + end + end + + def _deep_transform_values_in_object!(object, &block) + case object + when Hash + object.transform_values! { |value| _deep_transform_values_in_object!(value, &block) } + when Array + object.map! { |e| _deep_transform_values_in_object!(e, &block) } + else + yield(object) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/except.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/except.rb new file mode 100644 index 0000000..ec96929 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/except.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class Hash + # Returns a hash that includes everything except given keys. + # hash = { a: true, b: false, c: nil } + # hash.except(:c) # => { a: true, b: false } + # hash.except(:a, :b) # => { c: nil } + # hash # => { a: true, b: false, c: nil } + # + # This is useful for limiting a set of parameters to everything but a few known toggles: + # @person.update(params[:person].except(:admin)) + def except(*keys) + slice(*self.keys - keys) + end unless method_defined?(:except) + + # Removes the given keys from hash and returns it. + # hash = { a: true, b: false, c: nil } + # hash.except!(:c) # => { a: true, b: false } + # hash # => { a: true, b: false } + def except!(*keys) + keys.each { |key| delete(key) } + self + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/indifferent_access.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/indifferent_access.rb new file mode 100644 index 0000000..4437363 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/indifferent_access.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +require "active_support/hash_with_indifferent_access" + +class Hash + # Returns an ActiveSupport::HashWithIndifferentAccess out of its receiver: + # + # { a: 1 }.with_indifferent_access['a'] # => 1 + def with_indifferent_access + ActiveSupport::HashWithIndifferentAccess.new(self) + end + + # Called when object is nested under an object that receives + # #with_indifferent_access. This method will be called on the current object + # by the enclosing object and is aliased to #with_indifferent_access by + # default. Subclasses of Hash may override this method to return +self+ if + # converting to an ActiveSupport::HashWithIndifferentAccess would not be + # desirable. + # + # b = { b: 1 } + # { a: b }.with_indifferent_access['a'] # calls b.nested_under_indifferent_access + # # => {"b"=>1} + alias nested_under_indifferent_access with_indifferent_access +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/keys.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/keys.rb new file mode 100644 index 0000000..73487e0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/keys.rb @@ -0,0 +1,143 @@ +# frozen_string_literal: true + +class Hash + # Returns a new hash with all keys converted to strings. + # + # hash = { name: 'Rob', age: '28' } + # + # hash.stringify_keys + # # => {"name"=>"Rob", "age"=>"28"} + def stringify_keys + transform_keys(&:to_s) + end + + # Destructively converts all keys to strings. Same as + # +stringify_keys+, but modifies +self+. + def stringify_keys! + transform_keys!(&:to_s) + end + + # Returns a new hash with all keys converted to symbols, as long as + # they respond to +to_sym+. + # + # hash = { 'name' => 'Rob', 'age' => '28' } + # + # hash.symbolize_keys + # # => {:name=>"Rob", :age=>"28"} + def symbolize_keys + transform_keys { |key| key.to_sym rescue key } + end + alias_method :to_options, :symbolize_keys + + # Destructively converts all keys to symbols, as long as they respond + # to +to_sym+. Same as +symbolize_keys+, but modifies +self+. + def symbolize_keys! + transform_keys! { |key| key.to_sym rescue key } + end + alias_method :to_options!, :symbolize_keys! + + # Validates all keys in a hash match *valid_keys, raising + # +ArgumentError+ on a mismatch. + # + # Note that keys are treated differently than HashWithIndifferentAccess, + # meaning that string and symbol keys will not match. + # + # { name: 'Rob', years: '28' }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: :years. Valid keys are: :name, :age" + # { name: 'Rob', age: '28' }.assert_valid_keys('name', 'age') # => raises "ArgumentError: Unknown key: :name. Valid keys are: 'name', 'age'" + # { name: 'Rob', age: '28' }.assert_valid_keys(:name, :age) # => passes, raises nothing + def assert_valid_keys(*valid_keys) + valid_keys.flatten! + each_key do |k| + unless valid_keys.include?(k) + raise ArgumentError.new("Unknown key: #{k.inspect}. Valid keys are: #{valid_keys.map(&:inspect).join(', ')}") + end + end + end + + # Returns a new hash with all keys converted by the block operation. + # This includes the keys from the root hash and from all + # nested hashes and arrays. + # + # hash = { person: { name: 'Rob', age: '28' } } + # + # hash.deep_transform_keys{ |key| key.to_s.upcase } + # # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}} + def deep_transform_keys(&block) + _deep_transform_keys_in_object(self, &block) + end + + # Destructively converts all keys by using the block operation. + # This includes the keys from the root hash and from all + # nested hashes and arrays. + def deep_transform_keys!(&block) + _deep_transform_keys_in_object!(self, &block) + end + + # Returns a new hash with all keys converted to strings. + # This includes the keys from the root hash and from all + # nested hashes and arrays. + # + # hash = { person: { name: 'Rob', age: '28' } } + # + # hash.deep_stringify_keys + # # => {"person"=>{"name"=>"Rob", "age"=>"28"}} + def deep_stringify_keys + deep_transform_keys(&:to_s) + end + + # Destructively converts all keys to strings. + # This includes the keys from the root hash and from all + # nested hashes and arrays. + def deep_stringify_keys! + deep_transform_keys!(&:to_s) + end + + # Returns a new hash with all keys converted to symbols, as long as + # they respond to +to_sym+. This includes the keys from the root hash + # and from all nested hashes and arrays. + # + # hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } } + # + # hash.deep_symbolize_keys + # # => {:person=>{:name=>"Rob", :age=>"28"}} + def deep_symbolize_keys + deep_transform_keys { |key| key.to_sym rescue key } + end + + # Destructively converts all keys to symbols, as long as they respond + # to +to_sym+. This includes the keys from the root hash and from all + # nested hashes and arrays. + def deep_symbolize_keys! + deep_transform_keys! { |key| key.to_sym rescue key } + end + + private + # Support methods for deep transforming nested hashes and arrays. + def _deep_transform_keys_in_object(object, &block) + case object + when Hash + object.each_with_object(self.class.new) do |(key, value), result| + result[yield(key)] = _deep_transform_keys_in_object(value, &block) + end + when Array + object.map { |e| _deep_transform_keys_in_object(e, &block) } + else + object + end + end + + def _deep_transform_keys_in_object!(object, &block) + case object + when Hash + object.keys.each do |key| + value = object.delete(key) + object[yield(key)] = _deep_transform_keys_in_object!(value, &block) + end + object + when Array + object.map! { |e| _deep_transform_keys_in_object!(e, &block) } + else + object + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/reverse_merge.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/reverse_merge.rb new file mode 100644 index 0000000..ef8d592 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/reverse_merge.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class Hash + # Merges the caller into +other_hash+. For example, + # + # options = options.reverse_merge(size: 25, velocity: 10) + # + # is equivalent to + # + # options = { size: 25, velocity: 10 }.merge(options) + # + # This is particularly useful for initializing an options hash + # with default values. + def reverse_merge(other_hash) + other_hash.merge(self) + end + alias_method :with_defaults, :reverse_merge + + # Destructive +reverse_merge+. + def reverse_merge!(other_hash) + replace(reverse_merge(other_hash)) + end + alias_method :reverse_update, :reverse_merge! + alias_method :with_defaults!, :reverse_merge! +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/slice.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/slice.rb new file mode 100644 index 0000000..56bc5de --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/hash/slice.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class Hash + # Replaces the hash with only the given keys. + # Returns a hash containing the removed key/value pairs. + # + # hash = { a: 1, b: 2, c: 3, d: 4 } + # hash.slice!(:a, :b) # => {:c=>3, :d=>4} + # hash # => {:a=>1, :b=>2} + def slice!(*keys) + omit = slice(*self.keys - keys) + hash = slice(*keys) + hash.default = default + hash.default_proc = default_proc if default_proc + replace(hash) + omit + end + + # Removes and returns the key/value pairs matching the given keys. + # + # hash = { a: 1, b: 2, c: 3, d: 4 } + # hash.extract!(:a, :b) # => {:a=>1, :b=>2} + # hash # => {:c=>3, :d=>4} + def extract!(*keys) + keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) } + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/integer.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/integer.rb new file mode 100644 index 0000000..d227013 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/integer.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +require "active_support/core_ext/integer/multiple" +require "active_support/core_ext/integer/inflections" +require "active_support/core_ext/integer/time" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/integer/inflections.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/integer/inflections.rb new file mode 100644 index 0000000..aef3266 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/integer/inflections.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require "active_support/inflector" + +class Integer + # Ordinalize turns a number into an ordinal string used to denote the + # position in an ordered sequence such as 1st, 2nd, 3rd, 4th. + # + # 1.ordinalize # => "1st" + # 2.ordinalize # => "2nd" + # 1002.ordinalize # => "1002nd" + # 1003.ordinalize # => "1003rd" + # -11.ordinalize # => "-11th" + # -1001.ordinalize # => "-1001st" + def ordinalize + ActiveSupport::Inflector.ordinalize(self) + end + + # Ordinal returns the suffix used to denote the position + # in an ordered sequence such as 1st, 2nd, 3rd, 4th. + # + # 1.ordinal # => "st" + # 2.ordinal # => "nd" + # 1002.ordinal # => "nd" + # 1003.ordinal # => "rd" + # -11.ordinal # => "th" + # -1001.ordinal # => "st" + def ordinal + ActiveSupport::Inflector.ordinal(self) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/integer/multiple.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/integer/multiple.rb new file mode 100644 index 0000000..bd57a90 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/integer/multiple.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class Integer + # Check whether the integer is evenly divisible by the argument. + # + # 0.multiple_of?(0) # => true + # 6.multiple_of?(5) # => false + # 10.multiple_of?(2) # => true + def multiple_of?(number) + number == 0 ? self == 0 : self % number == 0 + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/integer/time.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/integer/time.rb new file mode 100644 index 0000000..5efb89c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/integer/time.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require "active_support/duration" +require "active_support/core_ext/numeric/time" + +class Integer + # Returns a Duration instance matching the number of months provided. + # + # 2.months # => 2 months + def months + ActiveSupport::Duration.months(self) + end + alias :month :months + + # Returns a Duration instance matching the number of years provided. + # + # 2.years # => 2 years + def years + ActiveSupport::Duration.years(self) + end + alias :year :years +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/kernel.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/kernel.rb new file mode 100644 index 0000000..7708069 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/kernel.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +require "active_support/core_ext/kernel/concern" +require "active_support/core_ext/kernel/reporting" +require "active_support/core_ext/kernel/singleton_class" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/kernel/concern.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/kernel/concern.rb new file mode 100644 index 0000000..0b2baed --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/kernel/concern.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require "active_support/core_ext/module/concerning" + +module Kernel + module_function + + # A shortcut to define a toplevel concern, not within a module. + # + # See Module::Concerning for more. + def concern(topic, &module_definition) + Object.concern topic, &module_definition + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/kernel/reporting.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/kernel/reporting.rb new file mode 100644 index 0000000..1ae1ae8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/kernel/reporting.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module Kernel + module_function + + # Sets $VERBOSE to +nil+ for the duration of the block and back to its original + # value afterwards. + # + # silence_warnings do + # value = noisy_call # no warning voiced + # end + # + # noisy_call # warning voiced + def silence_warnings(&block) + with_warnings(nil, &block) + end + + # Sets $VERBOSE to +true+ for the duration of the block and back to its + # original value afterwards. + def enable_warnings(&block) + with_warnings(true, &block) + end + + # Sets $VERBOSE for the duration of the block and back to its original + # value afterwards. + def with_warnings(flag) + old_verbose, $VERBOSE = $VERBOSE, flag + yield + ensure + $VERBOSE = old_verbose + end + + # Blocks and ignores any exception passed as argument if raised within the block. + # + # suppress(ZeroDivisionError) do + # 1/0 + # puts 'This code is NOT reached' + # end + # + # puts 'This code gets executed and nothing related to ZeroDivisionError was seen' + def suppress(*exception_classes) + yield + rescue *exception_classes + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/kernel/singleton_class.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/kernel/singleton_class.rb new file mode 100644 index 0000000..31335b6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/kernel/singleton_class.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Kernel + # class_eval on an object acts like +singleton_class.class_eval+. + def class_eval(*args, &block) + singleton_class.class_eval(*args, &block) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/load_error.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/load_error.rb new file mode 100644 index 0000000..03df2dd --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/load_error.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class LoadError + # Returns true if the given path name (except perhaps for the ".rb" + # extension) is the missing file which caused the exception to be raised. + def is_missing?(location) + location.delete_suffix(".rb") == path.to_s.delete_suffix(".rb") + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module.rb new file mode 100644 index 0000000..542af98 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require "active_support/core_ext/module/aliasing" +require "active_support/core_ext/module/introspection" +require "active_support/core_ext/module/anonymous" +require "active_support/core_ext/module/attribute_accessors" +require "active_support/core_ext/module/attribute_accessors_per_thread" +require "active_support/core_ext/module/attr_internal" +require "active_support/core_ext/module/concerning" +require "active_support/core_ext/module/delegation" +require "active_support/core_ext/module/deprecation" +require "active_support/core_ext/module/redefine_method" +require "active_support/core_ext/module/remove_method" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/aliasing.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/aliasing.rb new file mode 100644 index 0000000..6f64d11 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/aliasing.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +class Module + # Allows you to make aliases for attributes, which includes + # getter, setter, and a predicate. + # + # class Content < ActiveRecord::Base + # # has a title attribute + # end + # + # class Email < Content + # alias_attribute :subject, :title + # end + # + # e = Email.find(1) + # e.title # => "Superstars" + # e.subject # => "Superstars" + # e.subject? # => true + # e.subject = "Megastars" + # e.title # => "Megastars" + def alias_attribute(new_name, old_name) + # The following reader methods use an explicit `self` receiver in order to + # support aliases that start with an uppercase letter. Otherwise, they would + # be resolved as constants instead. + module_eval <<-STR, __FILE__, __LINE__ + 1 + def #{new_name}; self.#{old_name}; end # def subject; self.title; end + def #{new_name}?; self.#{old_name}?; end # def subject?; self.title?; end + def #{new_name}=(v); self.#{old_name} = v; end # def subject=(v); self.title = v; end + STR + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/anonymous.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/anonymous.rb new file mode 100644 index 0000000..d1c86b8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/anonymous.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +class Module + # A module may or may not have a name. + # + # module M; end + # M.name # => "M" + # + # m = Module.new + # m.name # => nil + # + # +anonymous?+ method returns true if module does not have a name, false otherwise: + # + # Module.new.anonymous? # => true + # + # module M; end + # M.anonymous? # => false + # + # A module gets a name when it is first assigned to a constant. Either + # via the +module+ or +class+ keyword or by an explicit assignment: + # + # m = Module.new # creates an anonymous module + # m.anonymous? # => true + # M = m # m gets a name here as a side-effect + # m.name # => "M" + # m.anonymous? # => false + def anonymous? + name.nil? + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/attr_internal.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/attr_internal.rb new file mode 100644 index 0000000..3bd66ff --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/attr_internal.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +class Module + # Declares an attribute reader backed by an internally-named instance variable. + def attr_internal_reader(*attrs) + attrs.each { |attr_name| attr_internal_define(attr_name, :reader) } + end + + # Declares an attribute writer backed by an internally-named instance variable. + def attr_internal_writer(*attrs) + attrs.each { |attr_name| attr_internal_define(attr_name, :writer) } + end + + # Declares an attribute reader and writer backed by an internally-named instance + # variable. + def attr_internal_accessor(*attrs) + attr_internal_reader(*attrs) + attr_internal_writer(*attrs) + end + alias_method :attr_internal, :attr_internal_accessor + + class << self; attr_accessor :attr_internal_naming_format end + self.attr_internal_naming_format = "@_%s" + + private + def attr_internal_ivar_name(attr) + Module.attr_internal_naming_format % attr + end + + def attr_internal_define(attr_name, type) + internal_name = attr_internal_ivar_name(attr_name).delete_prefix("@") + # use native attr_* methods as they are faster on some Ruby implementations + public_send("attr_#{type}", internal_name) + attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer + alias_method attr_name, internal_name + remove_method internal_name + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/attribute_accessors.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/attribute_accessors.rb new file mode 100644 index 0000000..f7903af --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/attribute_accessors.rb @@ -0,0 +1,208 @@ +# frozen_string_literal: true + +# == Attribute Accessors +# +# Extends the module object with class/module and instance accessors for +# class/module attributes, just like the native attr* accessors for instance +# attributes. +class Module + # Defines a class attribute and creates a class and instance reader methods. + # The underlying class variable is set to +nil+, if it is not previously + # defined. All class and instance methods created will be public, even if + # this method is called with a private or protected access modifier. + # + # module HairColors + # mattr_reader :hair_colors + # end + # + # HairColors.hair_colors # => nil + # HairColors.class_variable_set("@@hair_colors", [:brown, :black]) + # HairColors.hair_colors # => [:brown, :black] + # + # The attribute name must be a valid method name in Ruby. + # + # module Foo + # mattr_reader :"1_Badname" + # end + # # => NameError: invalid attribute name: 1_Badname + # + # To omit the instance reader method, pass + # instance_reader: false or instance_accessor: false. + # + # module HairColors + # mattr_reader :hair_colors, instance_reader: false + # end + # + # class Person + # include HairColors + # end + # + # Person.new.hair_colors # => NoMethodError + # + # You can set a default value for the attribute. + # + # module HairColors + # mattr_reader :hair_colors, default: [:brown, :black, :blonde, :red] + # end + # + # class Person + # include HairColors + # end + # + # Person.new.hair_colors # => [:brown, :black, :blonde, :red] + def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil, location: nil) + raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class? + location ||= caller_locations(1, 1).first + + definition = [] + syms.each do |sym| + raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym) + + definition << "def self.#{sym}; @@#{sym}; end" + + if instance_reader && instance_accessor + definition << "def #{sym}; @@#{sym}; end" + end + + sym_default_value = (block_given? && default.nil?) ? yield : default + class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}") + end + + module_eval(definition.join(";"), location.path, location.lineno) + end + alias :cattr_reader :mattr_reader + + # Defines a class attribute and creates a class and instance writer methods to + # allow assignment to the attribute. All class and instance methods created + # will be public, even if this method is called with a private or protected + # access modifier. + # + # module HairColors + # mattr_writer :hair_colors + # end + # + # class Person + # include HairColors + # end + # + # HairColors.hair_colors = [:brown, :black] + # Person.class_variable_get("@@hair_colors") # => [:brown, :black] + # Person.new.hair_colors = [:blonde, :red] + # HairColors.class_variable_get("@@hair_colors") # => [:blonde, :red] + # + # To omit the instance writer method, pass + # instance_writer: false or instance_accessor: false. + # + # module HairColors + # mattr_writer :hair_colors, instance_writer: false + # end + # + # class Person + # include HairColors + # end + # + # Person.new.hair_colors = [:blonde, :red] # => NoMethodError + # + # You can set a default value for the attribute. + # + # module HairColors + # mattr_writer :hair_colors, default: [:brown, :black, :blonde, :red] + # end + # + # class Person + # include HairColors + # end + # + # Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red] + def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil, location: nil) + raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class? + location ||= caller_locations(1, 1).first + + definition = [] + syms.each do |sym| + raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym) + definition << "def self.#{sym}=(val); @@#{sym} = val; end" + + if instance_writer && instance_accessor + definition << "def #{sym}=(val); @@#{sym} = val; end" + end + + sym_default_value = (block_given? && default.nil?) ? yield : default + class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}") + end + + module_eval(definition.join(";"), location.path, location.lineno) + end + alias :cattr_writer :mattr_writer + + # Defines both class and instance accessors for class attributes. + # All class and instance methods created will be public, even if + # this method is called with a private or protected access modifier. + # + # module HairColors + # mattr_accessor :hair_colors + # end + # + # class Person + # include HairColors + # end + # + # HairColors.hair_colors = [:brown, :black, :blonde, :red] + # HairColors.hair_colors # => [:brown, :black, :blonde, :red] + # Person.new.hair_colors # => [:brown, :black, :blonde, :red] + # + # If a subclass changes the value then that would also change the value for + # parent class. Similarly if parent class changes the value then that would + # change the value of subclasses too. + # + # class Citizen < Person + # end + # + # Citizen.new.hair_colors << :blue + # Person.new.hair_colors # => [:brown, :black, :blonde, :red, :blue] + # + # To omit the instance writer method, pass instance_writer: false. + # To omit the instance reader method, pass instance_reader: false. + # + # module HairColors + # mattr_accessor :hair_colors, instance_writer: false, instance_reader: false + # end + # + # class Person + # include HairColors + # end + # + # Person.new.hair_colors = [:brown] # => NoMethodError + # Person.new.hair_colors # => NoMethodError + # + # Or pass instance_accessor: false, to omit both instance methods. + # + # module HairColors + # mattr_accessor :hair_colors, instance_accessor: false + # end + # + # class Person + # include HairColors + # end + # + # Person.new.hair_colors = [:brown] # => NoMethodError + # Person.new.hair_colors # => NoMethodError + # + # You can set a default value for the attribute. + # + # module HairColors + # mattr_accessor :hair_colors, default: [:brown, :black, :blonde, :red] + # end + # + # class Person + # include HairColors + # end + # + # Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red] + def mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil, &blk) + location = caller_locations(1, 1).first + mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default, location: location, &blk) + mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor, default: default, location: location) + end + alias :cattr_accessor :mattr_accessor +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb new file mode 100644 index 0000000..e407cde --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb @@ -0,0 +1,157 @@ +# frozen_string_literal: true + +# == Attribute Accessors per Thread +# +# Extends the module object with class/module and instance accessors for +# class/module attributes, just like the native attr* accessors for instance +# attributes, but does so on a per-thread basis. +# +# So the values are scoped within the Thread.current space under the class name +# of the module. +# +# Note that it can also be scoped per-fiber if +Rails.application.config.active_support.isolation_level+ +# is set to +:fiber+. +class Module + # Defines a per-thread class attribute and creates class and instance reader methods. + # The underlying per-thread class variable is set to +nil+, if it is not previously defined. + # + # module Current + # thread_mattr_reader :user + # end + # + # Current.user = "DHH" + # Current.user # => "DHH" + # Thread.new { Current.user }.value # => nil + # + # The attribute name must be a valid method name in Ruby. + # + # module Foo + # thread_mattr_reader :"1_Badname" + # end + # # => NameError: invalid attribute name: 1_Badname + # + # To omit the instance reader method, pass + # instance_reader: false or instance_accessor: false. + # + # class Current + # thread_mattr_reader :user, instance_reader: false + # end + # + # Current.new.user # => NoMethodError + def thread_mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil) # :nodoc: + syms.each do |sym| + raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym) + + # The following generated method concatenates `name` because we want it + # to work with inheritance via polymorphism. + class_eval(<<-EOS, __FILE__, __LINE__ + 1) + def self.#{sym} + @__thread_mattr_#{sym} ||= "attr_\#{name}_#{sym}" + ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}] + end + EOS + + if instance_reader && instance_accessor + class_eval(<<-EOS, __FILE__, __LINE__ + 1) + def #{sym} + self.class.#{sym} + end + EOS + end + + ::ActiveSupport::IsolatedExecutionState["attr_#{name}_#{sym}"] = default unless default.nil? + end + end + alias :thread_cattr_reader :thread_mattr_reader + + # Defines a per-thread class attribute and creates a class and instance writer methods to + # allow assignment to the attribute. + # + # module Current + # thread_mattr_writer :user + # end + # + # Current.user = "DHH" + # Thread.current[:attr_Current_user] # => "DHH" + # + # To omit the instance writer method, pass + # instance_writer: false or instance_accessor: false. + # + # class Current + # thread_mattr_writer :user, instance_writer: false + # end + # + # Current.new.user = "DHH" # => NoMethodError + def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil) # :nodoc: + syms.each do |sym| + raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym) + + # The following generated method concatenates `name` because we want it + # to work with inheritance via polymorphism. + class_eval(<<-EOS, __FILE__, __LINE__ + 1) + def self.#{sym}=(obj) + @__thread_mattr_#{sym} ||= "attr_\#{name}_#{sym}" + ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}] = obj + end + EOS + + if instance_writer && instance_accessor + class_eval(<<-EOS, __FILE__, __LINE__ + 1) + def #{sym}=(obj) + self.class.#{sym} = obj + end + EOS + end + + public_send("#{sym}=", default) unless default.nil? + end + end + alias :thread_cattr_writer :thread_mattr_writer + + # Defines both class and instance accessors for class attributes. + # + # class Account + # thread_mattr_accessor :user + # end + # + # Account.user = "DHH" + # Account.user # => "DHH" + # Account.new.user # => "DHH" + # + # Unlike +mattr_accessor+, values are *not* shared with subclasses or parent classes. + # If a subclass changes the value, the parent class' value is not changed. + # If the parent class changes the value, the value of subclasses is not changed. + # + # class Customer < Account + # end + # + # Account.user # => "DHH" + # Customer.user # => nil + # Customer.user = "Rafael" + # Customer.user # => "Rafael" + # Account.user # => "DHH" + # + # To omit the instance writer method, pass instance_writer: false. + # To omit the instance reader method, pass instance_reader: false. + # + # class Current + # thread_mattr_accessor :user, instance_writer: false, instance_reader: false + # end + # + # Current.new.user = "DHH" # => NoMethodError + # Current.new.user # => NoMethodError + # + # Or pass instance_accessor: false, to omit both instance methods. + # + # class Current + # thread_mattr_accessor :user, instance_accessor: false + # end + # + # Current.new.user = "DHH" # => NoMethodError + # Current.new.user # => NoMethodError + def thread_mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil) + thread_mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default) + thread_mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor) + end + alias :thread_cattr_accessor :thread_mattr_accessor +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/concerning.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/concerning.rb new file mode 100644 index 0000000..36f5f85 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/concerning.rb @@ -0,0 +1,140 @@ +# frozen_string_literal: true + +require "active_support/concern" + +class Module + # = Bite-sized separation of concerns + # + # We often find ourselves with a medium-sized chunk of behavior that we'd + # like to extract, but only mix in to a single class. + # + # Extracting a plain old Ruby object to encapsulate it and collaborate or + # delegate to the original object is often a good choice, but when there's + # no additional state to encapsulate or we're making DSL-style declarations + # about the parent class, introducing new collaborators can obfuscate rather + # than simplify. + # + # The typical route is to just dump everything in a monolithic class, perhaps + # with a comment, as a least-bad alternative. Using modules in separate files + # means tedious sifting to get a big-picture view. + # + # = Dissatisfying ways to separate small concerns + # + # == Using comments: + # + # class Todo < ApplicationRecord + # # Other todo implementation + # # ... + # + # ## Event tracking + # has_many :events + # + # before_create :track_creation + # + # private + # def track_creation + # # ... + # end + # end + # + # == With an inline module: + # + # Noisy syntax. + # + # class Todo < ApplicationRecord + # # Other todo implementation + # # ... + # + # module EventTracking + # extend ActiveSupport::Concern + # + # included do + # has_many :events + # before_create :track_creation + # end + # + # private + # def track_creation + # # ... + # end + # end + # include EventTracking + # end + # + # == Mix-in noise exiled to its own file: + # + # Once our chunk of behavior starts pushing the scroll-to-understand-it + # boundary, we give in and move it to a separate file. At this size, the + # increased overhead can be a reasonable tradeoff even if it reduces our + # at-a-glance perception of how things work. + # + # class Todo < ApplicationRecord + # # Other todo implementation + # # ... + # + # include TodoEventTracking + # end + # + # = Introducing Module#concerning + # + # By quieting the mix-in noise, we arrive at a natural, low-ceremony way to + # separate bite-sized concerns. + # + # class Todo < ApplicationRecord + # # Other todo implementation + # # ... + # + # concerning :EventTracking do + # included do + # has_many :events + # before_create :track_creation + # end + # + # private + # def track_creation + # # ... + # end + # end + # end + # + # Todo.ancestors + # # => [Todo, Todo::EventTracking, ApplicationRecord, Object] + # + # This small step has some wonderful ripple effects. We can + # * grok the behavior of our class in one glance, + # * clean up monolithic junk-drawer classes by separating their concerns, and + # * stop leaning on protected/private for crude "this is internal stuff" modularity. + # + # === Prepending concerning + # + # concerning supports a prepend: true argument which will prepend the + # concern instead of using include for it. + module Concerning + # Define a new concern and mix it in. + def concerning(topic, prepend: false, &block) + method = prepend ? :prepend : :include + __send__(method, concern(topic, &block)) + end + + # A low-cruft shortcut to define a concern. + # + # concern :EventTracking do + # ... + # end + # + # is equivalent to + # + # module EventTracking + # extend ActiveSupport::Concern + # + # ... + # end + def concern(topic, &module_definition) + const_set topic, Module.new { + extend ::ActiveSupport::Concern + module_eval(&module_definition) + } + end + end + include Concerning +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/delegation.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/delegation.rb new file mode 100644 index 0000000..dcec1b1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/delegation.rb @@ -0,0 +1,324 @@ +# frozen_string_literal: true + +require "set" + +class Module + # Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+ + # option is not used. + class DelegationError < NoMethodError; end + + RUBY_RESERVED_KEYWORDS = %w(alias and BEGIN begin break case class def defined? do + else elsif END end ensure false for if in module next nil not or redo rescue retry + return self super then true undef unless until when while yield) + DELEGATION_RESERVED_KEYWORDS = %w(_ arg args block) + DELEGATION_RESERVED_METHOD_NAMES = Set.new( + RUBY_RESERVED_KEYWORDS + DELEGATION_RESERVED_KEYWORDS + ).freeze + + # Provides a +delegate+ class method to easily expose contained objects' + # public methods as your own. + # + # ==== Options + # * :to - Specifies the target object name as a symbol or string + # * :prefix - Prefixes the new method with the target name or a custom prefix + # * :allow_nil - If set to true, prevents a +Module::DelegationError+ + # from being raised + # * :private - If set to true, changes method visibility to private + # + # The macro receives one or more method names (specified as symbols or + # strings) and the name of the target object via the :to option + # (also a symbol or string). + # + # Delegation is particularly useful with Active Record associations: + # + # class Greeter < ActiveRecord::Base + # def hello + # 'hello' + # end + # + # def goodbye + # 'goodbye' + # end + # end + # + # class Foo < ActiveRecord::Base + # belongs_to :greeter + # delegate :hello, to: :greeter + # end + # + # Foo.new.hello # => "hello" + # Foo.new.goodbye # => NoMethodError: undefined method `goodbye' for # + # + # Multiple delegates to the same target are allowed: + # + # class Foo < ActiveRecord::Base + # belongs_to :greeter + # delegate :hello, :goodbye, to: :greeter + # end + # + # Foo.new.goodbye # => "goodbye" + # + # Methods can be delegated to instance variables, class variables, or constants + # by providing them as a symbols: + # + # class Foo + # CONSTANT_ARRAY = [0,1,2,3] + # @@class_array = [4,5,6,7] + # + # def initialize + # @instance_array = [8,9,10,11] + # end + # delegate :sum, to: :CONSTANT_ARRAY + # delegate :min, to: :@@class_array + # delegate :max, to: :@instance_array + # end + # + # Foo.new.sum # => 6 + # Foo.new.min # => 4 + # Foo.new.max # => 11 + # + # It's also possible to delegate a method to the class by using +:class+: + # + # class Foo + # def self.hello + # "world" + # end + # + # delegate :hello, to: :class + # end + # + # Foo.new.hello # => "world" + # + # Delegates can optionally be prefixed using the :prefix option. If the value + # is true, the delegate methods are prefixed with the name of the object being + # delegated to. + # + # Person = Struct.new(:name, :address) + # + # class Invoice < Struct.new(:client) + # delegate :name, :address, to: :client, prefix: true + # end + # + # john_doe = Person.new('John Doe', 'Vimmersvej 13') + # invoice = Invoice.new(john_doe) + # invoice.client_name # => "John Doe" + # invoice.client_address # => "Vimmersvej 13" + # + # It is also possible to supply a custom prefix. + # + # class Invoice < Struct.new(:client) + # delegate :name, :address, to: :client, prefix: :customer + # end + # + # invoice = Invoice.new(john_doe) + # invoice.customer_name # => 'John Doe' + # invoice.customer_address # => 'Vimmersvej 13' + # + # The delegated methods are public by default. + # Pass private: true to change that. + # + # class User < ActiveRecord::Base + # has_one :profile + # delegate :first_name, to: :profile + # delegate :date_of_birth, to: :profile, private: true + # + # def age + # Date.today.year - date_of_birth.year + # end + # end + # + # User.new.first_name # => "Tomas" + # User.new.date_of_birth # => NoMethodError: private method `date_of_birth' called for # + # User.new.age # => 2 + # + # If the target is +nil+ and does not respond to the delegated method a + # +Module::DelegationError+ is raised. If you wish to instead return +nil+, + # use the :allow_nil option. + # + # class User < ActiveRecord::Base + # has_one :profile + # delegate :age, to: :profile + # end + # + # User.new.age + # # => Module::DelegationError: User#age delegated to profile.age, but profile is nil + # + # But if not having a profile yet is fine and should not be an error + # condition: + # + # class User < ActiveRecord::Base + # has_one :profile + # delegate :age, to: :profile, allow_nil: true + # end + # + # User.new.age # nil + # + # Note that if the target is not +nil+ then the call is attempted regardless of the + # :allow_nil option, and thus an exception is still raised if said object + # does not respond to the method: + # + # class Foo + # def initialize(bar) + # @bar = bar + # end + # + # delegate :name, to: :@bar, allow_nil: true + # end + # + # Foo.new("Bar").name # raises NoMethodError: undefined method `name' + # + # The target method must be public, otherwise it will raise +NoMethodError+. + def delegate(*methods, to: nil, prefix: nil, allow_nil: nil, private: nil) + unless to + raise ArgumentError, "Delegation needs a target. Supply a keyword argument 'to' (e.g. delegate :hello, to: :greeter)." + end + + if prefix == true && /^[^a-z_]/.match?(to) + raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method." + end + + method_prefix = \ + if prefix + "#{prefix == true ? to : prefix}_" + else + "" + end + + location = caller_locations(1, 1).first + file, line = location.path, location.lineno + + to = to.to_s + to = "self.#{to}" if DELEGATION_RESERVED_METHOD_NAMES.include?(to) + + method_def = [] + method_names = [] + + methods.map do |method| + method_name = prefix ? "#{method_prefix}#{method}" : method + method_names << method_name.to_sym + + # Attribute writer methods only accept one argument. Makes sure []= + # methods still accept two arguments. + definition = /[^\]]=\z/.match?(method) ? "arg" : "..." + + # The following generated method calls the target exactly once, storing + # the returned value in a dummy variable. + # + # Reason is twofold: On one hand doing less calls is in general better. + # On the other hand it could be that the target has side-effects, + # whereas conceptually, from the user point of view, the delegator should + # be doing one call. + if allow_nil + method = method.to_s + + method_def << + "def #{method_name}(#{definition})" << + " _ = #{to}" << + " if !_.nil? || nil.respond_to?(:#{method})" << + " _.#{method}(#{definition})" << + " end" << + "end" + else + method = method.to_s + method_name = method_name.to_s + + method_def << + "def #{method_name}(#{definition})" << + " _ = #{to}" << + " _.#{method}(#{definition})" << + "rescue NoMethodError => e" << + " if _.nil? && e.name == :#{method}" << + %( raise DelegationError, "#{self}##{method_name} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}") << + " else" << + " raise" << + " end" << + "end" + end + end + module_eval(method_def.join(";"), file, line) + private(*method_names) if private + method_names + end + + # When building decorators, a common pattern may emerge: + # + # class Partition + # def initialize(event) + # @event = event + # end + # + # def person + # detail.person || creator + # end + # + # private + # def respond_to_missing?(name, include_private = false) + # @event.respond_to?(name, include_private) + # end + # + # def method_missing(method, *args, &block) + # @event.send(method, *args, &block) + # end + # end + # + # With Module#delegate_missing_to, the above is condensed to: + # + # class Partition + # delegate_missing_to :@event + # + # def initialize(event) + # @event = event + # end + # + # def person + # detail.person || creator + # end + # end + # + # The target can be anything callable within the object, e.g. instance + # variables, methods, constants, etc. + # + # The delegated method must be public on the target, otherwise it will + # raise +DelegationError+. If you wish to instead return +nil+, + # use the :allow_nil option. + # + # The marshal_dump and _dump methods are exempt from + # delegation due to possible interference when calling + # Marshal.dump(object), should the delegation target method + # of object add or remove instance variables. + def delegate_missing_to(target, allow_nil: nil) + target = target.to_s + target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target) + + module_eval <<-RUBY, __FILE__, __LINE__ + 1 + def respond_to_missing?(name, include_private = false) + # It may look like an oversight, but we deliberately do not pass + # +include_private+, because they do not get delegated. + + return false if name == :marshal_dump || name == :_dump + #{target}.respond_to?(name) || super + end + + def method_missing(method, *args, &block) + if #{target}.respond_to?(method) + #{target}.public_send(method, *args, &block) + else + begin + super + rescue NoMethodError + if #{target}.nil? + if #{allow_nil == true} + nil + else + raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil" + end + else + raise + end + end + end + end + ruby2_keywords(:method_missing) + RUBY + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/deprecation.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/deprecation.rb new file mode 100644 index 0000000..71c42eb --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/deprecation.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class Module + # deprecate :foo + # deprecate bar: 'message' + # deprecate :foo, :bar, baz: 'warning!', qux: 'gone!' + # + # You can also use custom deprecator instance: + # + # deprecate :foo, deprecator: MyLib::Deprecator.new + # deprecate :foo, bar: "warning!", deprecator: MyLib::Deprecator.new + # + # \Custom deprecators must respond to deprecation_warning(deprecated_method_name, message, caller_backtrace) + # method where you can implement your custom warning behavior. + # + # class MyLib::Deprecator + # def deprecation_warning(deprecated_method_name, message, caller_backtrace = nil) + # message = "#{deprecated_method_name} is deprecated and will be removed from MyLibrary | #{message}" + # Kernel.warn message + # end + # end + def deprecate(*method_names) + ActiveSupport::Deprecation.deprecate_methods(self, *method_names) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/introspection.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/introspection.rb new file mode 100644 index 0000000..7cdcab5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/introspection.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require "active_support/core_ext/string/filters" +require "active_support/inflector" + +class Module + # Returns the name of the module containing this one. + # + # M::N.module_parent_name # => "M" + def module_parent_name + if defined?(@parent_name) + @parent_name + else + parent_name = name =~ /::[^:]+\z/ ? -$` : nil + @parent_name = parent_name unless frozen? + parent_name + end + end + + # Returns the module which contains this one according to its name. + # + # module M + # module N + # end + # end + # X = M::N + # + # M::N.module_parent # => M + # X.module_parent # => M + # + # The parent of top-level and anonymous modules is Object. + # + # M.module_parent # => Object + # Module.new.module_parent # => Object + def module_parent + module_parent_name ? ActiveSupport::Inflector.constantize(module_parent_name) : Object + end + + # Returns all the parents of this module according to its name, ordered from + # nested outwards. The receiver is not contained within the result. + # + # module M + # module N + # end + # end + # X = M::N + # + # M.module_parents # => [Object] + # M::N.module_parents # => [M, Object] + # X.module_parents # => [M, Object] + def module_parents + parents = [] + if module_parent_name + parts = module_parent_name.split("::") + until parts.empty? + parents << ActiveSupport::Inflector.constantize(parts * "::") + parts.pop + end + end + parents << Object unless parents.include? Object + parents + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/redefine_method.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/redefine_method.rb new file mode 100644 index 0000000..5bd8e6e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/redefine_method.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +class Module + # Marks the named method as intended to be redefined, if it exists. + # Suppresses the Ruby method redefinition warning. Prefer + # #redefine_method where possible. + def silence_redefinition_of_method(method) + if method_defined?(method) || private_method_defined?(method) + # This suppresses the "method redefined" warning; the self-alias + # looks odd, but means we don't need to generate a unique name + alias_method method, method + end + end + + # Replaces the existing method definition, if there is one, with the passed + # block as its body. + def redefine_method(method, &block) + visibility = method_visibility(method) + silence_redefinition_of_method(method) + define_method(method, &block) + send(visibility, method) + end + + # Replaces the existing singleton method definition, if there is one, with + # the passed block as its body. + def redefine_singleton_method(method, &block) + singleton_class.redefine_method(method, &block) + end + + def method_visibility(method) # :nodoc: + case + when private_method_defined?(method) + :private + when protected_method_defined?(method) + :protected + else + :public + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/remove_method.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/remove_method.rb new file mode 100644 index 0000000..97eb5f9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/module/remove_method.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require "active_support/core_ext/module/redefine_method" + +class Module + # Removes the named method, if it exists. + def remove_possible_method(method) + if method_defined?(method) || private_method_defined?(method) + undef_method(method) + end + end + + # Removes the named singleton method, if it exists. + def remove_possible_singleton_method(method) + singleton_class.remove_possible_method(method) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/name_error.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/name_error.rb new file mode 100644 index 0000000..18ea275 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/name_error.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +class NameError + # Extract the name of the missing constant from the exception message. + # + # begin + # HelloWorld + # rescue NameError => e + # e.missing_name + # end + # # => "HelloWorld" + def missing_name + # Since ruby v2.3.0 `did_you_mean` gem is loaded by default. + # It extends NameError#message with spell corrections which are SLOW. + # We should use original_message message instead. + message = respond_to?(:original_message) ? original_message : self.message + return unless message.start_with?("uninitialized constant ") + + receiver = begin + self.receiver + rescue ArgumentError + nil + end + + if receiver == Object + name.to_s + elsif receiver + "#{real_mod_name(receiver)}::#{self.name}" + else + if match = message.match(/((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/) + match[1] + end + end + end + + # Was this exception raised because the given name was missing? + # + # begin + # HelloWorld + # rescue NameError => e + # e.missing_name?("HelloWorld") + # end + # # => true + def missing_name?(name) + if name.is_a? Symbol + self.name == name + else + missing_name == name.to_s + end + end + + private + UNBOUND_METHOD_MODULE_NAME = Module.instance_method(:name) + private_constant :UNBOUND_METHOD_MODULE_NAME + + def real_mod_name(mod) + UNBOUND_METHOD_MODULE_NAME.bind_call(mod) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/numeric.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/numeric.rb new file mode 100644 index 0000000..9368cb3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/numeric.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +require "active_support/core_ext/numeric/bytes" +require "active_support/core_ext/numeric/time" +require "active_support/core_ext/numeric/conversions" +require "active_support/core_ext/numeric/deprecated_conversions" unless ENV["RAILS_DISABLE_DEPRECATED_TO_S_CONVERSION"] diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/numeric/bytes.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/numeric/bytes.rb new file mode 100644 index 0000000..b002eba --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/numeric/bytes.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +class Numeric + KILOBYTE = 1024 + MEGABYTE = KILOBYTE * 1024 + GIGABYTE = MEGABYTE * 1024 + TERABYTE = GIGABYTE * 1024 + PETABYTE = TERABYTE * 1024 + EXABYTE = PETABYTE * 1024 + + # Enables the use of byte calculations and declarations, like 45.bytes + 2.6.megabytes + # + # 2.bytes # => 2 + def bytes + self + end + alias :byte :bytes + + # Returns the number of bytes equivalent to the kilobytes provided. + # + # 2.kilobytes # => 2048 + def kilobytes + self * KILOBYTE + end + alias :kilobyte :kilobytes + + # Returns the number of bytes equivalent to the megabytes provided. + # + # 2.megabytes # => 2_097_152 + def megabytes + self * MEGABYTE + end + alias :megabyte :megabytes + + # Returns the number of bytes equivalent to the gigabytes provided. + # + # 2.gigabytes # => 2_147_483_648 + def gigabytes + self * GIGABYTE + end + alias :gigabyte :gigabytes + + # Returns the number of bytes equivalent to the terabytes provided. + # + # 2.terabytes # => 2_199_023_255_552 + def terabytes + self * TERABYTE + end + alias :terabyte :terabytes + + # Returns the number of bytes equivalent to the petabytes provided. + # + # 2.petabytes # => 2_251_799_813_685_248 + def petabytes + self * PETABYTE + end + alias :petabyte :petabytes + + # Returns the number of bytes equivalent to the exabytes provided. + # + # 2.exabytes # => 2_305_843_009_213_693_952 + def exabytes + self * EXABYTE + end + alias :exabyte :exabytes +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/numeric/conversions.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/numeric/conversions.rb new file mode 100644 index 0000000..af672dd --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/numeric/conversions.rb @@ -0,0 +1,143 @@ +# frozen_string_literal: true + +require "active_support/core_ext/big_decimal/conversions" +require "active_support/number_helper" + +module ActiveSupport + module NumericWithFormat + # Provides options for converting numbers into formatted strings. + # Options are provided for phone numbers, currency, percentage, + # precision, positional notation, file size, and pretty printing. + # + # This method is aliased to to_formatted_s. + # + # ==== Options + # + # For details on which formats use which options, see ActiveSupport::NumberHelper + # + # ==== Examples + # + # Phone Numbers: + # 5551234.to_fs(:phone) # => "555-1234" + # 1235551234.to_fs(:phone) # => "123-555-1234" + # 1235551234.to_fs(:phone, area_code: true) # => "(123) 555-1234" + # 1235551234.to_fs(:phone, delimiter: ' ') # => "123 555 1234" + # 1235551234.to_fs(:phone, area_code: true, extension: 555) # => "(123) 555-1234 x 555" + # 1235551234.to_fs(:phone, country_code: 1) # => "+1-123-555-1234" + # 1235551234.to_fs(:phone, country_code: 1, extension: 1343, delimiter: '.') + # # => "+1.123.555.1234 x 1343" + # + # Currency: + # 1234567890.50.to_fs(:currency) # => "$1,234,567,890.50" + # 1234567890.506.to_fs(:currency) # => "$1,234,567,890.51" + # 1234567890.506.to_fs(:currency, precision: 3) # => "$1,234,567,890.506" + # 1234567890.506.to_fs(:currency, round_mode: :down) # => "$1,234,567,890.50" + # 1234567890.506.to_fs(:currency, locale: :fr) # => "1 234 567 890,51 €" + # -1234567890.50.to_fs(:currency, negative_format: '(%u%n)') + # # => "($1,234,567,890.50)" + # 1234567890.50.to_fs(:currency, unit: '£', separator: ',', delimiter: '') + # # => "£1234567890,50" + # 1234567890.50.to_fs(:currency, unit: '£', separator: ',', delimiter: '', format: '%n %u') + # # => "1234567890,50 £" + # + # Percentage: + # 100.to_fs(:percentage) # => "100.000%" + # 100.to_fs(:percentage, precision: 0) # => "100%" + # 1000.to_fs(:percentage, delimiter: '.', separator: ',') # => "1.000,000%" + # 302.24398923423.to_fs(:percentage, precision: 5) # => "302.24399%" + # 302.24398923423.to_fs(:percentage, round_mode: :down) # => "302.243%" + # 1000.to_fs(:percentage, locale: :fr) # => "1 000,000%" + # 100.to_fs(:percentage, format: '%n %') # => "100.000 %" + # + # Delimited: + # 12345678.to_fs(:delimited) # => "12,345,678" + # 12345678.05.to_fs(:delimited) # => "12,345,678.05" + # 12345678.to_fs(:delimited, delimiter: '.') # => "12.345.678" + # 12345678.to_fs(:delimited, delimiter: ',') # => "12,345,678" + # 12345678.05.to_fs(:delimited, separator: ' ') # => "12,345,678 05" + # 12345678.05.to_fs(:delimited, locale: :fr) # => "12 345 678,05" + # 98765432.98.to_fs(:delimited, delimiter: ' ', separator: ',') + # # => "98 765 432,98" + # + # Rounded: + # 111.2345.to_fs(:rounded) # => "111.235" + # 111.2345.to_fs(:rounded, precision: 2) # => "111.23" + # 111.2345.to_fs(:rounded, precision: 2, round_mode: :up) # => "111.24" + # 13.to_fs(:rounded, precision: 5) # => "13.00000" + # 389.32314.to_fs(:rounded, precision: 0) # => "389" + # 111.2345.to_fs(:rounded, significant: true) # => "111" + # 111.2345.to_fs(:rounded, precision: 1, significant: true) # => "100" + # 13.to_fs(:rounded, precision: 5, significant: true) # => "13.000" + # 111.234.to_fs(:rounded, locale: :fr) # => "111,234" + # 13.to_fs(:rounded, precision: 5, significant: true, strip_insignificant_zeros: true) + # # => "13" + # 389.32314.to_fs(:rounded, precision: 4, significant: true) # => "389.3" + # 1111.2345.to_fs(:rounded, precision: 2, separator: ',', delimiter: '.') + # # => "1.111,23" + # + # Human-friendly size in Bytes: + # 123.to_fs(:human_size) # => "123 Bytes" + # 1234.to_fs(:human_size) # => "1.21 KB" + # 12345.to_fs(:human_size) # => "12.1 KB" + # 1234567.to_fs(:human_size) # => "1.18 MB" + # 1234567890.to_fs(:human_size) # => "1.15 GB" + # 1234567890123.to_fs(:human_size) # => "1.12 TB" + # 1234567890123456.to_fs(:human_size) # => "1.1 PB" + # 1234567890123456789.to_fs(:human_size) # => "1.07 EB" + # 1234567.to_fs(:human_size, precision: 2) # => "1.2 MB" + # 1234567.to_fs(:human_size, precision: 2, round_mode: :up) # => "1.3 MB" + # 483989.to_fs(:human_size, precision: 2) # => "470 KB" + # 1234567.to_fs(:human_size, precision: 2, separator: ',') # => "1,2 MB" + # 1234567890123.to_fs(:human_size, precision: 5) # => "1.1228 TB" + # 524288000.to_fs(:human_size, precision: 5) # => "500 MB" + # + # Human-friendly format: + # 123.to_fs(:human) # => "123" + # 1234.to_fs(:human) # => "1.23 Thousand" + # 12345.to_fs(:human) # => "12.3 Thousand" + # 1234567.to_fs(:human) # => "1.23 Million" + # 1234567890.to_fs(:human) # => "1.23 Billion" + # 1234567890123.to_fs(:human) # => "1.23 Trillion" + # 1234567890123456.to_fs(:human) # => "1.23 Quadrillion" + # 1234567890123456789.to_fs(:human) # => "1230 Quadrillion" + # 489939.to_fs(:human, precision: 2) # => "490 Thousand" + # 489939.to_fs(:human, precision: 2, round_mode: :down) # => "480 Thousand" + # 489939.to_fs(:human, precision: 4) # => "489.9 Thousand" + # 1234567.to_fs(:human, precision: 4, + # significant: false) # => "1.2346 Million" + # 1234567.to_fs(:human, precision: 1, + # separator: ',', + # significant: false) # => "1,2 Million" + def to_fs(format = nil, options = nil) + return to_s if format.nil? + + case format + when Integer, String + to_s(format) + when :phone + ActiveSupport::NumberHelper.number_to_phone(self, options || {}) + when :currency + ActiveSupport::NumberHelper.number_to_currency(self, options || {}) + when :percentage + ActiveSupport::NumberHelper.number_to_percentage(self, options || {}) + when :delimited + ActiveSupport::NumberHelper.number_to_delimited(self, options || {}) + when :rounded + ActiveSupport::NumberHelper.number_to_rounded(self, options || {}) + when :human + ActiveSupport::NumberHelper.number_to_human(self, options || {}) + when :human_size + ActiveSupport::NumberHelper.number_to_human_size(self, options || {}) + when Symbol + to_s + else + to_s(format) + end + end + alias_method :to_formatted_s, :to_fs + end +end + +Integer.prepend ActiveSupport::NumericWithFormat +Float.prepend ActiveSupport::NumericWithFormat +BigDecimal.prepend ActiveSupport::NumericWithFormat diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/numeric/deprecated_conversions.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/numeric/deprecated_conversions.rb new file mode 100644 index 0000000..5f81dc6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/numeric/deprecated_conversions.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +module ActiveSupport + module DeprecatedNumericWithFormat # :nodoc: + def to_s(format = nil, options = nil) + return super() if format.nil? + + case format + when Integer, String + super(format) + when :phone + ActiveSupport::Deprecation.warn( + "#{self.class}#to_s(#{format.inspect}) is deprecated. Please use #{self.class}#to_fs(#{format.inspect}) instead." + ) + ActiveSupport::NumberHelper.number_to_phone(self, options || {}) + when :currency + ActiveSupport::Deprecation.warn( + "#{self.class}#to_s(#{format.inspect}) is deprecated. Please use #{self.class}#to_fs(#{format.inspect}) instead." + ) + ActiveSupport::NumberHelper.number_to_currency(self, options || {}) + when :percentage + ActiveSupport::Deprecation.warn( + "#{self.class}#to_s(#{format.inspect}) is deprecated. Please use #{self.class}#to_fs(#{format.inspect}) instead." + ) + ActiveSupport::NumberHelper.number_to_percentage(self, options || {}) + when :delimited + ActiveSupport::Deprecation.warn( + "#{self.class}#to_s(#{format.inspect}) is deprecated. Please use #{self.class}#to_fs(#{format.inspect}) instead." + ) + ActiveSupport::NumberHelper.number_to_delimited(self, options || {}) + when :rounded + ActiveSupport::Deprecation.warn( + "#{self.class}#to_s(#{format.inspect}) is deprecated. Please use #{self.class}#to_fs(#{format.inspect}) instead." + ) + ActiveSupport::NumberHelper.number_to_rounded(self, options || {}) + when :human + ActiveSupport::Deprecation.warn( + "#{self.class}#to_s(#{format.inspect}) is deprecated. Please use #{self.class}#to_fs(#{format.inspect}) instead." + ) + ActiveSupport::NumberHelper.number_to_human(self, options || {}) + when :human_size + ActiveSupport::Deprecation.warn( + "#{self.class}#to_s(#{format.inspect}) is deprecated. Please use #{self.class}#to_fs(#{format.inspect}) instead." + ) + ActiveSupport::NumberHelper.number_to_human_size(self, options || {}) + when Symbol + ActiveSupport::Deprecation.warn( + "#{self.class}#to_s(#{format.inspect}) is deprecated. Please use #{self.class}#to_fs(#{format.inspect}) instead." + ) + super() + else + super(format) + end + end + end +end + +Integer.prepend ActiveSupport::DeprecatedNumericWithFormat +Float.prepend ActiveSupport::DeprecatedNumericWithFormat +BigDecimal.prepend ActiveSupport::DeprecatedNumericWithFormat diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/numeric/time.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/numeric/time.rb new file mode 100644 index 0000000..bc4627f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/numeric/time.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require "active_support/duration" +require "active_support/core_ext/time/calculations" +require "active_support/core_ext/time/acts_like" +require "active_support/core_ext/date/calculations" +require "active_support/core_ext/date/acts_like" + +class Numeric + # Returns a Duration instance matching the number of seconds provided. + # + # 2.seconds # => 2 seconds + def seconds + ActiveSupport::Duration.seconds(self) + end + alias :second :seconds + + # Returns a Duration instance matching the number of minutes provided. + # + # 2.minutes # => 2 minutes + def minutes + ActiveSupport::Duration.minutes(self) + end + alias :minute :minutes + + # Returns a Duration instance matching the number of hours provided. + # + # 2.hours # => 2 hours + def hours + ActiveSupport::Duration.hours(self) + end + alias :hour :hours + + # Returns a Duration instance matching the number of days provided. + # + # 2.days # => 2 days + def days + ActiveSupport::Duration.days(self) + end + alias :day :days + + # Returns a Duration instance matching the number of weeks provided. + # + # 2.weeks # => 2 weeks + def weeks + ActiveSupport::Duration.weeks(self) + end + alias :week :weeks + + # Returns a Duration instance matching the number of fortnights provided. + # + # 2.fortnights # => 4 weeks + def fortnights + ActiveSupport::Duration.weeks(self * 2) + end + alias :fortnight :fortnights + + # Returns the number of milliseconds equivalent to the seconds provided. + # Used with the standard time durations. + # + # 2.in_milliseconds # => 2000 + # 1.hour.in_milliseconds # => 3600000 + def in_milliseconds + self * 1000 + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object.rb new file mode 100644 index 0000000..efd34cc --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require "active_support/core_ext/object/acts_like" +require "active_support/core_ext/object/blank" +require "active_support/core_ext/object/duplicable" +require "active_support/core_ext/object/deep_dup" +require "active_support/core_ext/object/try" +require "active_support/core_ext/object/inclusion" + +require "active_support/core_ext/object/conversions" +require "active_support/core_ext/object/instance_variables" + +require "active_support/core_ext/object/json" +require "active_support/core_ext/object/to_param" +require "active_support/core_ext/object/to_query" +require "active_support/core_ext/object/with_options" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/acts_like.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/acts_like.rb new file mode 100644 index 0000000..292826c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/acts_like.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +class Object + # Provides a way to check whether some class acts like some other class based on the existence of + # an appropriately-named marker method. + # + # A class that provides the same interface as SomeClass may define a marker method named + # acts_like_some_class? to signal its compatibility to callers of + # acts_like?(:some_class). + # + # For example, Active Support extends Date to define an acts_like_date? method, + # and extends Time to define acts_like_time?. As a result, developers can call + # x.acts_like?(:time) and x.acts_like?(:date) to test duck-type compatibility, + # and classes that are able to act like Time can also define an acts_like_time? + # method to interoperate. + # + # Note that the marker method is only expected to exist. It isn't called, so its body or return + # value are irrelevant. + # + # ==== Example: A class that provides the same interface as String + # + # This class may define: + # + # class Stringish + # def acts_like_string? + # end + # end + # + # Then client code can query for duck-type-safeness this way: + # + # Stringish.new.acts_like?(:string) # => true + # + def acts_like?(duck) + case duck + when :time + respond_to? :acts_like_time? + when :date + respond_to? :acts_like_date? + when :string + respond_to? :acts_like_string? + else + respond_to? :"acts_like_#{duck}?" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/blank.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/blank.rb new file mode 100644 index 0000000..372f725 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/blank.rb @@ -0,0 +1,155 @@ +# frozen_string_literal: true + +require "concurrent/map" + +class Object + # An object is blank if it's false, empty, or a whitespace string. + # For example, +nil+, '', ' ', [], {}, and +false+ are all blank. + # + # This simplifies + # + # !address || address.empty? + # + # to + # + # address.blank? + # + # @return [true, false] + def blank? + respond_to?(:empty?) ? !!empty? : !self + end + + # An object is present if it's not blank. + # + # @return [true, false] + def present? + !blank? + end + + # Returns the receiver if it's present otherwise returns +nil+. + # object.presence is equivalent to + # + # object.present? ? object : nil + # + # For example, something like + # + # state = params[:state] if params[:state].present? + # country = params[:country] if params[:country].present? + # region = state || country || 'US' + # + # becomes + # + # region = params[:state].presence || params[:country].presence || 'US' + # + # @return [Object] + def presence + self if present? + end +end + +class NilClass + # +nil+ is blank: + # + # nil.blank? # => true + # + # @return [true] + def blank? + true + end +end + +class FalseClass + # +false+ is blank: + # + # false.blank? # => true + # + # @return [true] + def blank? + true + end +end + +class TrueClass + # +true+ is not blank: + # + # true.blank? # => false + # + # @return [false] + def blank? + false + end +end + +class Array + # An array is blank if it's empty: + # + # [].blank? # => true + # [1,2,3].blank? # => false + # + # @return [true, false] + alias_method :blank?, :empty? +end + +class Hash + # A hash is blank if it's empty: + # + # {}.blank? # => true + # { key: 'value' }.blank? # => false + # + # @return [true, false] + alias_method :blank?, :empty? +end + +class String + BLANK_RE = /\A[[:space:]]*\z/ + ENCODED_BLANKS = Concurrent::Map.new do |h, enc| + h[enc] = Regexp.new(BLANK_RE.source.encode(enc), BLANK_RE.options | Regexp::FIXEDENCODING) + end + + # A string is blank if it's empty or contains whitespaces only: + # + # ''.blank? # => true + # ' '.blank? # => true + # "\t\n\r".blank? # => true + # ' blah '.blank? # => false + # + # Unicode whitespace is supported: + # + # "\u00a0".blank? # => true + # + # @return [true, false] + def blank? + # The regexp that matches blank strings is expensive. For the case of empty + # strings we can speed up this method (~3.5x) with an empty? call. The + # penalty for the rest of strings is marginal. + empty? || + begin + BLANK_RE.match?(self) + rescue Encoding::CompatibilityError + ENCODED_BLANKS[self.encoding].match?(self) + end + end +end + +class Numeric # :nodoc: + # No number is blank: + # + # 1.blank? # => false + # 0.blank? # => false + # + # @return [false] + def blank? + false + end +end + +class Time # :nodoc: + # No Time is blank: + # + # Time.now.blank? # => false + # + # @return [false] + def blank? + false + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/conversions.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/conversions.rb new file mode 100644 index 0000000..624fb8d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/conversions.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +require "active_support/core_ext/object/to_param" +require "active_support/core_ext/object/to_query" +require "active_support/core_ext/array/conversions" +require "active_support/core_ext/hash/conversions" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/deep_dup.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/deep_dup.rb new file mode 100644 index 0000000..4aca04a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/deep_dup.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require "active_support/core_ext/object/duplicable" + +class Object + # Returns a deep copy of object if it's duplicable. If it's + # not duplicable, returns +self+. + # + # object = Object.new + # dup = object.deep_dup + # dup.instance_variable_set(:@a, 1) + # + # object.instance_variable_defined?(:@a) # => false + # dup.instance_variable_defined?(:@a) # => true + def deep_dup + duplicable? ? dup : self + end +end + +class Array + # Returns a deep copy of array. + # + # array = [1, [2, 3]] + # dup = array.deep_dup + # dup[1][2] = 4 + # + # array[1][2] # => nil + # dup[1][2] # => 4 + def deep_dup + map(&:deep_dup) + end +end + +class Hash + # Returns a deep copy of hash. + # + # hash = { a: { b: 'b' } } + # dup = hash.deep_dup + # dup[:a][:c] = 'c' + # + # hash[:a][:c] # => nil + # dup[:a][:c] # => "c" + def deep_dup + hash = dup + each_pair do |key, value| + if ::String === key || ::Symbol === key + hash[key] = value.deep_dup + else + hash.delete(key) + hash[key.deep_dup] = value.deep_dup + end + end + hash + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/duplicable.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/duplicable.rb new file mode 100644 index 0000000..c09e893 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/duplicable.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +#-- +# Most objects are cloneable, but not all. For example you can't dup methods: +# +# method(:puts).dup # => TypeError: allocator undefined for Method +# +# Classes may signal their instances are not duplicable removing +dup+/+clone+ +# or raising exceptions from them. So, to dup an arbitrary object you normally +# use an optimistic approach and are ready to catch an exception, say: +# +# arbitrary_object.dup rescue object +# +# Rails dups objects in a few critical spots where they are not that arbitrary. +# That rescue is very expensive (like 40 times slower than a predicate), and it +# is often triggered. +# +# That's why we hardcode the following cases and check duplicable? instead of +# using that rescue idiom. +#++ +class Object + # Can you safely dup this object? + # + # False for method objects; + # true otherwise. + def duplicable? + true + end +end + +class Method + # Methods are not duplicable: + # + # method(:puts).duplicable? # => false + # method(:puts).dup # => TypeError: allocator undefined for Method + def duplicable? + false + end +end + +class UnboundMethod + # Unbound methods are not duplicable: + # + # method(:puts).unbind.duplicable? # => false + # method(:puts).unbind.dup # => TypeError: allocator undefined for UnboundMethod + def duplicable? + false + end +end + +require "singleton" + +module Singleton + # Singleton instances are not duplicable: + # + # Class.new.include(Singleton).instance.dup # TypeError (can't dup instance of singleton + def duplicable? + false + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/inclusion.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/inclusion.rb new file mode 100644 index 0000000..6064e92 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/inclusion.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class Object + # Returns true if this object is included in the argument. Argument must be + # any object which responds to +#include?+. Usage: + # + # characters = ["Konata", "Kagami", "Tsukasa"] + # "Konata".in?(characters) # => true + # + # This will throw an +ArgumentError+ if the argument doesn't respond + # to +#include?+. + def in?(another_object) + another_object.include?(self) + rescue NoMethodError + raise ArgumentError.new("The parameter passed to #in? must respond to #include?") + end + + # Returns the receiver if it's included in the argument otherwise returns +nil+. + # Argument must be any object which responds to +#include?+. Usage: + # + # params[:bucket_type].presence_in %w( project calendar ) + # + # This will throw an +ArgumentError+ if the argument doesn't respond to +#include?+. + # + # @return [Object] + def presence_in(another_object) + in?(another_object) ? self : nil + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/instance_variables.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/instance_variables.rb new file mode 100644 index 0000000..12fdf84 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/instance_variables.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +class Object + # Returns a hash with string keys that maps instance variable names without "@" to their + # corresponding values. + # + # class C + # def initialize(x, y) + # @x, @y = x, y + # end + # end + # + # C.new(0, 1).instance_values # => {"x" => 0, "y" => 1} + def instance_values + Hash[instance_variables.map { |name| [name[1..-1], instance_variable_get(name)] }] + end + + # Returns an array of instance variable names as strings including "@". + # + # class C + # def initialize(x, y) + # @x, @y = x, y + # end + # end + # + # C.new(0, 1).instance_variable_names # => ["@y", "@x"] + def instance_variable_names + instance_variables.map(&:to_s) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/json.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/json.rb new file mode 100644 index 0000000..bd9b6f9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/json.rb @@ -0,0 +1,244 @@ +# frozen_string_literal: true + +# Hack to load json gem first so we can override its to_json. +require "json" +require "bigdecimal" +require "ipaddr" +require "uri/generic" +require "pathname" +require "active_support/core_ext/big_decimal/conversions" # for #to_s +require "active_support/core_ext/hash/except" +require "active_support/core_ext/hash/slice" +require "active_support/core_ext/object/instance_variables" +require "time" +require "active_support/core_ext/time/conversions" +require "active_support/core_ext/date_time/conversions" +require "active_support/core_ext/date/conversions" + +#-- +# The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting +# their default behavior. That said, we need to define the basic to_json method in all of them, +# otherwise they will always use to_json gem implementation, which is backwards incompatible in +# several cases (for instance, the JSON implementation for Hash does not work) with inheritance. +# +# On the other hand, we should avoid conflict with ::JSON.{generate,dump}(obj). Unfortunately, the +# JSON gem's encoder relies on its own to_json implementation to encode objects. Since it always +# passes a ::JSON::State object as the only argument to to_json, we can detect that and forward the +# calls to the original to_json method. +# +# It should be noted that when using ::JSON.{generate,dump} directly, ActiveSupport's encoder is +# bypassed completely. This means that as_json won't be invoked and the JSON gem will simply +# ignore any options it does not natively understand. This also means that ::JSON.{generate,dump} +# should give exactly the same results with or without active support. + +module ActiveSupport + module ToJsonWithActiveSupportEncoder # :nodoc: + def to_json(options = nil) + if options.is_a?(::JSON::State) + # Called from JSON.{generate,dump}, forward it to JSON gem's to_json + super(options) + else + # to_json is being invoked directly, use ActiveSupport's encoder + ActiveSupport::JSON.encode(self, options) + end + end + end +end + +[Enumerable, Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].reverse_each do |klass| + klass.prepend(ActiveSupport::ToJsonWithActiveSupportEncoder) +end + +class Module + def as_json(options = nil) # :nodoc: + name + end +end + +class Object + def as_json(options = nil) # :nodoc: + if respond_to?(:to_hash) + to_hash.as_json(options) + else + instance_values.as_json(options) + end + end +end + +class Struct # :nodoc: + def as_json(options = nil) + Hash[members.zip(values)].as_json(options) + end +end + +class TrueClass + def as_json(options = nil) # :nodoc: + self + end +end + +class FalseClass + def as_json(options = nil) # :nodoc: + self + end +end + +class NilClass + def as_json(options = nil) # :nodoc: + self + end +end + +class String + def as_json(options = nil) # :nodoc: + self + end +end + +class Symbol + def as_json(options = nil) # :nodoc: + to_s + end +end + +class Numeric + def as_json(options = nil) # :nodoc: + self + end +end + +class Float + # Encoding Infinity or NaN to JSON should return "null". The default returns + # "Infinity" or "NaN" which are not valid JSON. + def as_json(options = nil) # :nodoc: + finite? ? self : nil + end +end + +class BigDecimal + # A BigDecimal would be naturally represented as a JSON number. Most libraries, + # however, parse non-integer JSON numbers directly as floats. Clients using + # those libraries would get in general a wrong number and no way to recover + # other than manually inspecting the string with the JSON code itself. + # + # That's why a JSON string is returned. The JSON literal is not numeric, but + # if the other end knows by contract that the data is supposed to be a + # BigDecimal, it still has the chance to post-process the string and get the + # real value. + def as_json(options = nil) # :nodoc: + finite? ? to_s : nil + end +end + +class Regexp + def as_json(options = nil) # :nodoc: + to_s + end +end + +module Enumerable + def as_json(options = nil) # :nodoc: + to_a.as_json(options) + end +end + +class IO + def as_json(options = nil) # :nodoc: + to_s + end +end + +class Range + def as_json(options = nil) # :nodoc: + to_s + end +end + +class Array + def as_json(options = nil) # :nodoc: + map { |v| options ? v.as_json(options.dup) : v.as_json } + end +end + +class Hash + def as_json(options = nil) # :nodoc: + # create a subset of the hash by applying :only or :except + subset = if options + if attrs = options[:only] + slice(*Array(attrs)) + elsif attrs = options[:except] + except(*Array(attrs)) + else + self + end + else + self + end + + result = {} + subset.each do |k, v| + result[k.to_s] = options ? v.as_json(options.dup) : v.as_json + end + result + end +end + +class Time + def as_json(options = nil) # :nodoc: + if ActiveSupport::JSON::Encoding.use_standard_json_time_format + xmlschema(ActiveSupport::JSON::Encoding.time_precision) + else + %(#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}) + end + end +end + +class Date + def as_json(options = nil) # :nodoc: + if ActiveSupport::JSON::Encoding.use_standard_json_time_format + strftime("%Y-%m-%d") + else + strftime("%Y/%m/%d") + end + end +end + +class DateTime + def as_json(options = nil) # :nodoc: + if ActiveSupport::JSON::Encoding.use_standard_json_time_format + xmlschema(ActiveSupport::JSON::Encoding.time_precision) + else + strftime("%Y/%m/%d %H:%M:%S %z") + end + end +end + +class URI::Generic # :nodoc: + def as_json(options = nil) + to_s + end +end + +class Pathname # :nodoc: + def as_json(options = nil) + to_s + end +end + +class IPAddr # :nodoc: + def as_json(options = nil) + to_s + end +end + +class Process::Status # :nodoc: + def as_json(options = nil) + { exitstatus: exitstatus, pid: pid } + end +end + +class Exception + def as_json(options = nil) + to_s + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/to_param.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/to_param.rb new file mode 100644 index 0000000..6d2bdd7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/to_param.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require "active_support/core_ext/object/to_query" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/to_query.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/to_query.rb new file mode 100644 index 0000000..736643b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/to_query.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +require "cgi" + +class Object + # Alias of to_s. + def to_param + to_s + end + + # Converts an object into a string suitable for use as a URL query string, + # using the given key as the param name. + def to_query(key) + "#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}" + end +end + +class NilClass + # Returns +self+. + def to_param + self + end +end + +class TrueClass + # Returns +self+. + def to_param + self + end +end + +class FalseClass + # Returns +self+. + def to_param + self + end +end + +class Array + # Calls to_param on all its elements and joins the result with + # slashes. This is used by url_for in Action Pack. + def to_param + collect(&:to_param).join "/" + end + + # Converts an array into a string suitable for use as a URL query string, + # using the given +key+ as the param name. + # + # ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding" + def to_query(key) + prefix = "#{key}[]" + + if empty? + nil.to_query(prefix) + else + collect { |value| value.to_query(prefix) }.join "&" + end + end +end + +class Hash + # Returns a string representation of the receiver suitable for use as a URL + # query string: + # + # {name: 'David', nationality: 'Danish'}.to_query + # # => "name=David&nationality=Danish" + # + # An optional namespace can be passed to enclose key names: + # + # {name: 'David', nationality: 'Danish'}.to_query('user') + # # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish" + # + # The string pairs "key=value" that conform the query string + # are sorted lexicographically in ascending order. + # + # This method is also aliased as +to_param+. + def to_query(namespace = nil) + query = filter_map do |key, value| + unless (value.is_a?(Hash) || value.is_a?(Array)) && value.empty? + value.to_query(namespace ? "#{namespace}[#{key}]" : key) + end + end + + query.sort! unless namespace.to_s.include?("[]") + query.join("&") + end + + alias_method :to_param, :to_query +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/try.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/try.rb new file mode 100644 index 0000000..c2c7625 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/try.rb @@ -0,0 +1,158 @@ +# frozen_string_literal: true + +require "delegate" + +module ActiveSupport + module Tryable # :nodoc: + def try(*args, &block) + if args.empty? && block_given? + if block.arity == 0 + instance_eval(&block) + else + yield self + end + elsif respond_to?(args.first) + public_send(*args, &block) + end + end + ruby2_keywords(:try) + + def try!(*args, &block) + if args.empty? && block_given? + if block.arity == 0 + instance_eval(&block) + else + yield self + end + else + public_send(*args, &block) + end + end + ruby2_keywords(:try!) + end +end + +class Object + include ActiveSupport::Tryable + + ## + # :method: try + # + # :call-seq: + # try(*args, &block) + # + # Invokes the public method whose name goes as first argument just like + # +public_send+ does, except that if the receiver does not respond to it the + # call returns +nil+ rather than raising an exception. + # + # This method is defined to be able to write + # + # @person.try(:name) + # + # instead of + # + # @person.name if @person + # + # +try+ calls can be chained: + # + # @person.try(:spouse).try(:name) + # + # instead of + # + # @person.spouse.name if @person && @person.spouse + # + # +try+ will also return +nil+ if the receiver does not respond to the method: + # + # @person.try(:non_existing_method) # => nil + # + # instead of + # + # @person.non_existing_method if @person.respond_to?(:non_existing_method) # => nil + # + # +try+ returns +nil+ when called on +nil+ regardless of whether it responds + # to the method: + # + # nil.try(:to_i) # => nil, rather than 0 + # + # Arguments and blocks are forwarded to the method if invoked: + # + # @posts.try(:each_slice, 2) do |a, b| + # ... + # end + # + # The number of arguments in the signature must match. If the object responds + # to the method the call is attempted and +ArgumentError+ is still raised + # in case of argument mismatch. + # + # If +try+ is called without arguments it yields the receiver to a given + # block unless it is +nil+: + # + # @person.try do |p| + # ... + # end + # + # You can also call try with a block without accepting an argument, and the block + # will be instance_eval'ed instead: + # + # @person.try { upcase.truncate(50) } + # + # Please also note that +try+ is defined on +Object+. Therefore, it won't work + # with instances of classes that do not have +Object+ among their ancestors, + # like direct subclasses of +BasicObject+. + + ## + # :method: try! + # + # :call-seq: + # try!(*args, &block) + # + # Same as #try, but raises a +NoMethodError+ exception if the receiver is + # not +nil+ and does not implement the tried method. + # + # "a".try!(:upcase) # => "A" + # nil.try!(:upcase) # => nil + # 123.try!(:upcase) # => NoMethodError: undefined method `upcase' for 123:Integer +end + +class Delegator + include ActiveSupport::Tryable + + ## + # :method: try + # + # :call-seq: + # try(*args, &block) + # + # See Object#try + + ## + # :method: try! + # + # :call-seq: + # try!(*args, &block) + # + # See Object#try! +end + +class NilClass + # Calling +try+ on +nil+ always returns +nil+. + # It becomes especially helpful when navigating through associations that may return +nil+. + # + # nil.try(:name) # => nil + # + # Without +try+ + # @person && @person.children.any? && @person.children.first.name + # + # With +try+ + # @person.try(:children).try(:first).try(:name) + def try(*) + nil + end + + # Calling +try!+ on +nil+ always returns +nil+. + # + # nil.try!(:name) # => nil + def try!(*) + nil + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/with_options.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/with_options.rb new file mode 100644 index 0000000..0f40448 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/object/with_options.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +require "active_support/option_merger" + +class Object + # An elegant way to factor duplication out of options passed to a series of + # method calls. Each method called in the block, with the block variable as + # the receiver, will have its options merged with the default +options+ hash + # provided. Each method called on the block variable must take an options + # hash as its final argument. + # + # Without with_options, this code contains duplication: + # + # class Account < ActiveRecord::Base + # has_many :customers, dependent: :destroy + # has_many :products, dependent: :destroy + # has_many :invoices, dependent: :destroy + # has_many :expenses, dependent: :destroy + # end + # + # Using with_options, we can remove the duplication: + # + # class Account < ActiveRecord::Base + # with_options dependent: :destroy do |assoc| + # assoc.has_many :customers + # assoc.has_many :products + # assoc.has_many :invoices + # assoc.has_many :expenses + # end + # end + # + # It can also be used with an explicit receiver: + # + # I18n.with_options locale: user.locale, scope: 'newsletter' do |i18n| + # subject i18n.t :subject + # body i18n.t :body, user_name: user.name + # end + # + # When you don't pass an explicit receiver, it executes the whole block + # in merging options context: + # + # class Account < ActiveRecord::Base + # with_options dependent: :destroy do + # has_many :customers + # has_many :products + # has_many :invoices + # has_many :expenses + # end + # end + # + # with_options can also be nested since the call is forwarded to its receiver. + # + # NOTE: Each nesting level will merge inherited defaults in addition to their own. + # + # class Post < ActiveRecord::Base + # with_options if: :persisted?, length: { minimum: 50 } do + # validates :content, if: -> { content.present? } + # end + # end + # + # The code is equivalent to: + # + # validates :content, length: { minimum: 50 }, if: -> { content.present? } + # + # Hence the inherited default for +if+ key is ignored. + # + # NOTE: You cannot call class methods implicitly inside of with_options. + # You can access these methods using the class name instead: + # + # class Phone < ActiveRecord::Base + # enum phone_number_type: { home: 0, office: 1, mobile: 2 } + # + # with_options presence: true do + # validates :phone_number_type, inclusion: { in: Phone.phone_number_types.keys } + # end + # end + # + # When the block argument is omitted, the decorated Object instance is returned: + # + # module MyStyledHelpers + # def styled + # with_options style: "color: red;" + # end + # end + # + # # styled.link_to "I'm red", "/" + # # #=> I'm red + # + # # styled.button_tag "I'm red too!" + # # #=> + # + def with_options(options, &block) + option_merger = ActiveSupport::OptionMerger.new(self, options) + + if block + block.arity.zero? ? option_merger.instance_eval(&block) : block.call(option_merger) + else + option_merger + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/pathname.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/pathname.rb new file mode 100644 index 0000000..611a43e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/pathname.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require "active_support/core_ext/pathname/existence" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/pathname/existence.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/pathname/existence.rb new file mode 100644 index 0000000..9bb9b17 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/pathname/existence.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class Pathname + # Returns the receiver if the named file exists otherwise returns +nil+. + # pathname.existence is equivalent to + # + # pathname.exist? ? pathname : nil + # + # For example, something like + # + # content = pathname.read if pathname.exist? + # + # becomes + # + # content = pathname.existence&.read + # + # @return [Pathname] + def existence + self if exist? + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/range.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/range.rb new file mode 100644 index 0000000..ba6bc9b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/range.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require "active_support/core_ext/range/conversions" +require "active_support/core_ext/range/deprecated_conversions" unless ENV["RAILS_DISABLE_DEPRECATED_TO_S_CONVERSION"] +require "active_support/core_ext/range/compare_range" +require "active_support/core_ext/range/overlaps" +require "active_support/core_ext/range/each" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/range/compare_range.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/range/compare_range.rb new file mode 100644 index 0000000..affbbeb --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/range/compare_range.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module ActiveSupport + module CompareWithRange + # Extends the default Range#=== to support range comparisons. + # (1..5) === (1..5) # => true + # (1..5) === (2..3) # => true + # (1..5) === (1...6) # => true + # (1..5) === (2..6) # => false + # + # The native Range#=== behavior is untouched. + # ('a'..'f') === ('c') # => true + # (5..9) === (11) # => false + # + # The given range must be fully bounded, with both start and end. + def ===(value) + if value.is_a?(::Range) + is_backwards_op = value.exclude_end? ? :>= : :> + return false if value.begin && value.end && value.begin.public_send(is_backwards_op, value.end) + # 1...10 includes 1..9 but it does not include 1..10. + # 1..10 includes 1...11 but it does not include 1...12. + operator = exclude_end? && !value.exclude_end? ? :< : :<= + value_max = !exclude_end? && value.exclude_end? ? value.max : value.last + super(value.first) && (self.end.nil? || value_max.public_send(operator, last)) + else + super + end + end + + # Extends the default Range#include? to support range comparisons. + # (1..5).include?(1..5) # => true + # (1..5).include?(2..3) # => true + # (1..5).include?(1...6) # => true + # (1..5).include?(2..6) # => false + # + # The native Range#include? behavior is untouched. + # ('a'..'f').include?('c') # => true + # (5..9).include?(11) # => false + # + # The given range must be fully bounded, with both start and end. + def include?(value) + if value.is_a?(::Range) + is_backwards_op = value.exclude_end? ? :>= : :> + return false if value.begin && value.end && value.begin.public_send(is_backwards_op, value.end) + # 1...10 includes 1..9 but it does not include 1..10. + # 1..10 includes 1...11 but it does not include 1...12. + operator = exclude_end? && !value.exclude_end? ? :< : :<= + value_max = !exclude_end? && value.exclude_end? ? value.max : value.last + super(value.first) && (self.end.nil? || value_max.public_send(operator, last)) + else + super + end + end + end +end + +Range.prepend(ActiveSupport::CompareWithRange) diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/range/conversions.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/range/conversions.rb new file mode 100644 index 0000000..1c80ea3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/range/conversions.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module ActiveSupport + module RangeWithFormat + RANGE_FORMATS = { + db: -> (start, stop) do + case start + when String then "BETWEEN '#{start}' AND '#{stop}'" + else + "BETWEEN '#{start.to_fs(:db)}' AND '#{stop.to_fs(:db)}'" + end + end + } + + # Convert range to a formatted string. See RANGE_FORMATS for predefined formats. + # + # This method is aliased to to_formatted_s. + # + # range = (1..100) # => 1..100 + # + # range.to_s # => "1..100" + # range.to_fs(:db) # => "BETWEEN '1' AND '100'" + # + # == Adding your own range formats to to_s + # You can add your own formats to the Range::RANGE_FORMATS hash. + # Use the format name as the hash key and a Proc instance. + # + # # config/initializers/range_formats.rb + # Range::RANGE_FORMATS[:short] = ->(start, stop) { "Between #{start.to_fs(:db)} and #{stop.to_fs(:db)}" } + def to_fs(format = :default) + if formatter = RANGE_FORMATS[format] + formatter.call(first, last) + else + to_s + end + end + alias_method :to_formatted_s, :to_fs + end +end + +Range.prepend(ActiveSupport::RangeWithFormat) diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/range/deprecated_conversions.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/range/deprecated_conversions.rb new file mode 100644 index 0000000..86b377f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/range/deprecated_conversions.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module ActiveSupport + module DeprecatedRangeWithFormat # :nodoc: + NOT_SET = Object.new # :nodoc: + def to_s(format = NOT_SET) + if formatter = RangeWithFormat::RANGE_FORMATS[format] + ActiveSupport::Deprecation.warn( + "Range#to_s(#{format.inspect}) is deprecated. Please use Range#to_fs(#{format.inspect}) instead." + ) + formatter.call(first, last) + elsif format == NOT_SET + super() + else + ActiveSupport::Deprecation.warn( + "Range#to_s(#{format.inspect}) is deprecated. Please use Range#to_fs(#{format.inspect}) instead." + ) + super() + end + end + alias_method :to_default_s, :to_s + deprecate :to_default_s + end +end + +Range.prepend(ActiveSupport::DeprecatedRangeWithFormat) diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/range/each.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/range/each.rb new file mode 100644 index 0000000..1c44cc8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/range/each.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +require "active_support/time_with_zone" + +module ActiveSupport + module EachTimeWithZone # :nodoc: + def each(&block) + ensure_iteration_allowed + super + end + + def step(n = 1, &block) + ensure_iteration_allowed + super + end + + private + def ensure_iteration_allowed + raise TypeError, "can't iterate from #{first.class}" if first.is_a?(TimeWithZone) + end + end +end + +Range.prepend(ActiveSupport::EachTimeWithZone) diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/range/include_time_with_zone.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/range/include_time_with_zone.rb new file mode 100644 index 0000000..f2378d4 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/range/include_time_with_zone.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +# frozen_string_literal: true + +ActiveSupport::Deprecation.warn(<<-MSG.squish) + `active_support/core_ext/range/include_time_with_zone` is deprecated and will be removed in Rails 7.1. +MSG diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/range/overlaps.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/range/overlaps.rb new file mode 100644 index 0000000..c286988 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/range/overlaps.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class Range + # Compare two ranges and see if they overlap each other + # (1..5).overlaps?(4..6) # => true + # (1..5).overlaps?(7..9) # => false + def overlaps?(other) + other.begin == self.begin || cover?(other.begin) || other.cover?(self.begin) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/regexp.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/regexp.rb new file mode 100644 index 0000000..15534ff --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/regexp.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class Regexp + # Returns +true+ if the regexp has the multiline flag set. + # + # (/./).multiline? # => false + # (/./m).multiline? # => true + # + # Regexp.new(".").multiline? # => false + # Regexp.new(".", Regexp::MULTILINE).multiline? # => true + def multiline? + options & MULTILINE == MULTILINE + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/securerandom.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/securerandom.rb new file mode 100644 index 0000000..fa6b68b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/securerandom.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require "securerandom" + +module SecureRandom + BASE58_ALPHABET = ("0".."9").to_a + ("A".."Z").to_a + ("a".."z").to_a - ["0", "O", "I", "l"] + BASE36_ALPHABET = ("0".."9").to_a + ("a".."z").to_a + + # SecureRandom.base58 generates a random base58 string. + # + # The argument _n_ specifies the length of the random string to be generated. + # + # If _n_ is not specified or is +nil+, 16 is assumed. It may be larger in the future. + # + # The result may contain alphanumeric characters except 0, O, I, and l. + # + # p SecureRandom.base58 # => "4kUgL2pdQMSCQtjE" + # p SecureRandom.base58(24) # => "77TMHrHJFvFDwodq8w7Ev2m7" + def self.base58(n = 16) + SecureRandom.random_bytes(n).unpack("C*").map do |byte| + idx = byte % 64 + idx = SecureRandom.random_number(58) if idx >= 58 + BASE58_ALPHABET[idx] + end.join + end + + # SecureRandom.base36 generates a random base36 string in lowercase. + # + # The argument _n_ specifies the length of the random string to be generated. + # + # If _n_ is not specified or is +nil+, 16 is assumed. It may be larger in the future. + # This method can be used over +base58+ if a deterministic case key is necessary. + # + # The result will contain alphanumeric characters in lowercase. + # + # p SecureRandom.base36 # => "4kugl2pdqmscqtje" + # p SecureRandom.base36(24) # => "77tmhrhjfvfdwodq8w7ev2m7" + def self.base36(n = 16) + SecureRandom.random_bytes(n).unpack("C*").map do |byte| + idx = byte % 64 + idx = SecureRandom.random_number(36) if idx >= 36 + BASE36_ALPHABET[idx] + end.join + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string.rb new file mode 100644 index 0000000..757d15c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require "active_support/core_ext/string/conversions" +require "active_support/core_ext/string/filters" +require "active_support/core_ext/string/multibyte" +require "active_support/core_ext/string/starts_ends_with" +require "active_support/core_ext/string/inflections" +require "active_support/core_ext/string/access" +require "active_support/core_ext/string/behavior" +require "active_support/core_ext/string/output_safety" +require "active_support/core_ext/string/exclude" +require "active_support/core_ext/string/strip" +require "active_support/core_ext/string/inquiry" +require "active_support/core_ext/string/indent" +require "active_support/core_ext/string/zones" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/access.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/access.rb new file mode 100644 index 0000000..f6a14c0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/access.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +class String + # If you pass a single integer, returns a substring of one character at that + # position. The first character of the string is at position 0, the next at + # position 1, and so on. If a range is supplied, a substring containing + # characters at offsets given by the range is returned. In both cases, if an + # offset is negative, it is counted from the end of the string. Returns +nil+ + # if the initial offset falls outside the string. Returns an empty string if + # the beginning of the range is greater than the end of the string. + # + # str = "hello" + # str.at(0) # => "h" + # str.at(1..3) # => "ell" + # str.at(-2) # => "l" + # str.at(-2..-1) # => "lo" + # str.at(5) # => nil + # str.at(5..-1) # => "" + # + # If a Regexp is given, the matching portion of the string is returned. + # If a String is given, that given string is returned if it occurs in + # the string. In both cases, +nil+ is returned if there is no match. + # + # str = "hello" + # str.at(/lo/) # => "lo" + # str.at(/ol/) # => nil + # str.at("lo") # => "lo" + # str.at("ol") # => nil + def at(position) + self[position] + end + + # Returns a substring from the given position to the end of the string. + # If the position is negative, it is counted from the end of the string. + # + # str = "hello" + # str.from(0) # => "hello" + # str.from(3) # => "lo" + # str.from(-2) # => "lo" + # + # You can mix it with +to+ method and do fun things like: + # + # str = "hello" + # str.from(0).to(-1) # => "hello" + # str.from(1).to(-2) # => "ell" + def from(position) + self[position, length] + end + + # Returns a substring from the beginning of the string to the given position. + # If the position is negative, it is counted from the end of the string. + # + # str = "hello" + # str.to(0) # => "h" + # str.to(3) # => "hell" + # str.to(-2) # => "hell" + # + # You can mix it with +from+ method and do fun things like: + # + # str = "hello" + # str.from(0).to(-1) # => "hello" + # str.from(1).to(-2) # => "ell" + def to(position) + position += size if position < 0 + self[0, position + 1] || +"" + end + + # Returns the first character. If a limit is supplied, returns a substring + # from the beginning of the string until it reaches the limit value. If the + # given limit is greater than or equal to the string length, returns a copy of self. + # + # str = "hello" + # str.first # => "h" + # str.first(1) # => "h" + # str.first(2) # => "he" + # str.first(0) # => "" + # str.first(6) # => "hello" + def first(limit = 1) + self[0, limit] || raise(ArgumentError, "negative limit") + end + + # Returns the last character of the string. If a limit is supplied, returns a substring + # from the end of the string until it reaches the limit value (counting backwards). If + # the given limit is greater than or equal to the string length, returns a copy of self. + # + # str = "hello" + # str.last # => "o" + # str.last(1) # => "o" + # str.last(2) # => "lo" + # str.last(0) # => "" + # str.last(6) # => "hello" + def last(limit = 1) + self[[length - limit, 0].max, limit] || raise(ArgumentError, "negative limit") + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/behavior.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/behavior.rb new file mode 100644 index 0000000..35a5aa7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/behavior.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class String + # Enables more predictable duck-typing on String-like classes. See Object#acts_like?. + def acts_like_string? + true + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/conversions.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/conversions.rb new file mode 100644 index 0000000..58e3289 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/conversions.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require "date" +require "active_support/core_ext/time/calculations" + +class String + # Converts a string to a Time value. + # The +form+ can be either +:utc+ or +:local+ (default +:local+). + # + # The time is parsed using Time.parse method. + # If +form+ is +:local+, then the time is in the system timezone. + # If the date part is missing then the current date is used and if + # the time part is missing then it is assumed to be 00:00:00. + # + # "13-12-2012".to_time # => 2012-12-13 00:00:00 +0100 + # "06:12".to_time # => 2012-12-13 06:12:00 +0100 + # "2012-12-13 06:12".to_time # => 2012-12-13 06:12:00 +0100 + # "2012-12-13T06:12".to_time # => 2012-12-13 06:12:00 +0100 + # "2012-12-13T06:12".to_time(:utc) # => 2012-12-13 06:12:00 UTC + # "12/13/2012".to_time # => ArgumentError: argument out of range + # "1604326192".to_time # => ArgumentError: argument out of range + def to_time(form = :local) + parts = Date._parse(self, false) + used_keys = %i(year mon mday hour min sec sec_fraction offset) + return if (parts.keys & used_keys).empty? + + now = Time.now + time = Time.new( + parts.fetch(:year, now.year), + parts.fetch(:mon, now.month), + parts.fetch(:mday, now.day), + parts.fetch(:hour, 0), + parts.fetch(:min, 0), + parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0), + parts.fetch(:offset, form == :utc ? 0 : nil) + ) + + form == :utc ? time.utc : time.to_time + end + + # Converts a string to a Date value. + # + # "1-1-2012".to_date # => Sun, 01 Jan 2012 + # "01/01/2012".to_date # => Sun, 01 Jan 2012 + # "2012-12-13".to_date # => Thu, 13 Dec 2012 + # "12/13/2012".to_date # => ArgumentError: invalid date + def to_date + ::Date.parse(self, false) unless blank? + end + + # Converts a string to a DateTime value. + # + # "1-1-2012".to_datetime # => Sun, 01 Jan 2012 00:00:00 +0000 + # "01/01/2012 23:59:59".to_datetime # => Sun, 01 Jan 2012 23:59:59 +0000 + # "2012-12-13 12:50".to_datetime # => Thu, 13 Dec 2012 12:50:00 +0000 + # "12/13/2012".to_datetime # => ArgumentError: invalid date + def to_datetime + ::DateTime.parse(self, false) unless blank? + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/exclude.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/exclude.rb new file mode 100644 index 0000000..8e46268 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/exclude.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class String + # The inverse of String#include?. Returns true if the string + # does not include the other string. + # + # "hello".exclude? "lo" # => false + # "hello".exclude? "ol" # => true + # "hello".exclude? ?h # => false + def exclude?(string) + !include?(string) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/filters.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/filters.rb new file mode 100644 index 0000000..dc163c5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/filters.rb @@ -0,0 +1,145 @@ +# frozen_string_literal: true + +class String + # Returns the string, first removing all whitespace on both ends of + # the string, and then changing remaining consecutive whitespace + # groups into one space each. + # + # Note that it handles both ASCII and Unicode whitespace. + # + # %{ Multi-line + # string }.squish # => "Multi-line string" + # " foo bar \n \t boo".squish # => "foo bar boo" + def squish + dup.squish! + end + + # Performs a destructive squish. See String#squish. + # str = " foo bar \n \t boo" + # str.squish! # => "foo bar boo" + # str # => "foo bar boo" + def squish! + gsub!(/[[:space:]]+/, " ") + strip! + self + end + + # Returns a new string with all occurrences of the patterns removed. + # str = "foo bar test" + # str.remove(" test") # => "foo bar" + # str.remove(" test", /bar/) # => "foo " + # str # => "foo bar test" + def remove(*patterns) + dup.remove!(*patterns) + end + + # Alters the string by removing all occurrences of the patterns. + # str = "foo bar test" + # str.remove!(" test", /bar/) # => "foo " + # str # => "foo " + def remove!(*patterns) + patterns.each do |pattern| + gsub! pattern, "" + end + + self + end + + # Truncates a given +text+ after a given length if +text+ is longer than length: + # + # 'Once upon a time in a world far far away'.truncate(27) + # # => "Once upon a time in a wo..." + # + # Pass a string or regexp :separator to truncate +text+ at a natural break: + # + # 'Once upon a time in a world far far away'.truncate(27, separator: ' ') + # # => "Once upon a time in a..." + # + # 'Once upon a time in a world far far away'.truncate(27, separator: /\s/) + # # => "Once upon a time in a..." + # + # The last characters will be replaced with the :omission string (defaults to "...") + # for a total length not exceeding length: + # + # 'And they found that many people were sleeping better.'.truncate(25, omission: '... (continued)') + # # => "And they f... (continued)" + def truncate(truncate_at, options = {}) + return dup unless length > truncate_at + + omission = options[:omission] || "..." + length_with_room_for_omission = truncate_at - omission.length + stop = \ + if options[:separator] + rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission + else + length_with_room_for_omission + end + + +"#{self[0, stop]}#{omission}" + end + + # Truncates +text+ to at most bytesize bytes in length without + # breaking string encoding by splitting multibyte characters or breaking + # grapheme clusters ("perceptual characters") by truncating at combining + # characters. + # + # >> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".size + # => 20 + # >> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".bytesize + # => 80 + # >> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".truncate_bytes(20) + # => "🔪🔪🔪🔪…" + # + # The truncated text ends with the :omission string, defaulting + # to "…", for a total length not exceeding bytesize. + def truncate_bytes(truncate_at, omission: "…") + omission ||= "" + + case + when bytesize <= truncate_at + dup + when omission.bytesize > truncate_at + raise ArgumentError, "Omission #{omission.inspect} is #{omission.bytesize}, larger than the truncation length of #{truncate_at} bytes" + when omission.bytesize == truncate_at + omission.dup + else + self.class.new.tap do |cut| + cut_at = truncate_at - omission.bytesize + + each_grapheme_cluster do |grapheme| + if cut.bytesize + grapheme.bytesize <= cut_at + cut << grapheme + else + break + end + end + + cut << omission + end + end + end + + # Truncates a given +text+ after a given number of words (words_count): + # + # 'Once upon a time in a world far far away'.truncate_words(4) + # # => "Once upon a time..." + # + # Pass a string or regexp :separator to specify a different separator of words: + # + # 'Once
upon
a
time
in
a
world'.truncate_words(5, separator: '
') + # # => "Once
upon
a
time
in..." + # + # The last characters will be replaced with the :omission string (defaults to "..."): + # + # 'And they found that many people were sleeping better.'.truncate_words(5, omission: '... (continued)') + # # => "And they found that many... (continued)" + def truncate_words(words_count, options = {}) + sep = options[:separator] || /\s+/ + sep = Regexp.escape(sep.to_s) unless Regexp === sep + if self =~ /\A((?>.+?#{sep}){#{words_count - 1}}.+?)#{sep}.*/m + $1 + (options[:omission] || "...") + else + dup + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/indent.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/indent.rb new file mode 100644 index 0000000..af9d181 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/indent.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +class String + # Same as +indent+, except it indents the receiver in-place. + # + # Returns the indented string, or +nil+ if there was nothing to indent. + def indent!(amount, indent_string = nil, indent_empty_lines = false) + indent_string = indent_string || self[/^[ \t]/] || " " + re = indent_empty_lines ? /^/ : /^(?!$)/ + gsub!(re, indent_string * amount) + end + + # Indents the lines in the receiver: + # + # < + # def some_method + # some_code + # end + # + # The second argument, +indent_string+, specifies which indent string to + # use. The default is +nil+, which tells the method to make a guess by + # peeking at the first indented line, and fallback to a space if there is + # none. + # + # " foo".indent(2) # => " foo" + # "foo\n\t\tbar".indent(2) # => "\t\tfoo\n\t\t\t\tbar" + # "foo".indent(2, "\t") # => "\t\tfoo" + # + # While +indent_string+ is typically one space or tab, it may be any string. + # + # The third argument, +indent_empty_lines+, is a flag that says whether + # empty lines should be indented. Default is false. + # + # "foo\n\nbar".indent(2) # => " foo\n\n bar" + # "foo\n\nbar".indent(2, nil, true) # => " foo\n \n bar" + # + def indent(amount, indent_string = nil, indent_empty_lines = false) + dup.tap { |_| _.indent!(amount, indent_string, indent_empty_lines) } + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/inflections.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/inflections.rb new file mode 100644 index 0000000..e0749f9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/inflections.rb @@ -0,0 +1,293 @@ +# frozen_string_literal: true + +require "active_support/inflector/methods" +require "active_support/inflector/transliterate" + +# String inflections define new methods on the String class to transform names for different purposes. +# For instance, you can figure out the name of a table from the name of a class. +# +# 'ScaleScore'.tableize # => "scale_scores" +# +class String + # Returns the plural form of the word in the string. + # + # If the optional parameter +count+ is specified, + # the singular form will be returned if count == 1. + # For any other value of +count+ the plural will be returned. + # + # If the optional parameter +locale+ is specified, + # the word will be pluralized as a word of that language. + # By default, this parameter is set to :en. + # You must define your own inflection rules for languages other than English. + # + # 'post'.pluralize # => "posts" + # 'octopus'.pluralize # => "octopi" + # 'sheep'.pluralize # => "sheep" + # 'words'.pluralize # => "words" + # 'the blue mailman'.pluralize # => "the blue mailmen" + # 'CamelOctopus'.pluralize # => "CamelOctopi" + # 'apple'.pluralize(1) # => "apple" + # 'apple'.pluralize(2) # => "apples" + # 'ley'.pluralize(:es) # => "leyes" + # 'ley'.pluralize(1, :es) # => "ley" + # + # See ActiveSupport::Inflector.pluralize. + def pluralize(count = nil, locale = :en) + locale = count if count.is_a?(Symbol) + if count == 1 + dup + else + ActiveSupport::Inflector.pluralize(self, locale) + end + end + + # The reverse of +pluralize+, returns the singular form of a word in a string. + # + # If the optional parameter +locale+ is specified, + # the word will be singularized as a word of that language. + # By default, this parameter is set to :en. + # You must define your own inflection rules for languages other than English. + # + # 'posts'.singularize # => "post" + # 'octopi'.singularize # => "octopus" + # 'sheep'.singularize # => "sheep" + # 'word'.singularize # => "word" + # 'the blue mailmen'.singularize # => "the blue mailman" + # 'CamelOctopi'.singularize # => "CamelOctopus" + # 'leyes'.singularize(:es) # => "ley" + # + # See ActiveSupport::Inflector.singularize. + def singularize(locale = :en) + ActiveSupport::Inflector.singularize(self, locale) + end + + # +constantize+ tries to find a declared constant with the name specified + # in the string. It raises a NameError when the name is not in CamelCase + # or is not initialized. + # + # 'Module'.constantize # => Module + # 'Class'.constantize # => Class + # 'blargle'.constantize # => NameError: wrong constant name blargle + # + # See ActiveSupport::Inflector.constantize. + def constantize + ActiveSupport::Inflector.constantize(self) + end + + # +safe_constantize+ tries to find a declared constant with the name specified + # in the string. It returns +nil+ when the name is not in CamelCase + # or is not initialized. + # + # 'Module'.safe_constantize # => Module + # 'Class'.safe_constantize # => Class + # 'blargle'.safe_constantize # => nil + # + # See ActiveSupport::Inflector.safe_constantize. + def safe_constantize + ActiveSupport::Inflector.safe_constantize(self) + end + + # By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize + # is set to :lower then camelize produces lowerCamelCase. + # + # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces. + # + # 'active_record'.camelize # => "ActiveRecord" + # 'active_record'.camelize(:lower) # => "activeRecord" + # 'active_record/errors'.camelize # => "ActiveRecord::Errors" + # 'active_record/errors'.camelize(:lower) # => "activeRecord::Errors" + # + # +camelize+ is also aliased as +camelcase+. + # + # See ActiveSupport::Inflector.camelize. + def camelize(first_letter = :upper) + case first_letter + when :upper + ActiveSupport::Inflector.camelize(self, true) + when :lower + ActiveSupport::Inflector.camelize(self, false) + else + raise ArgumentError, "Invalid option, use either :upper or :lower." + end + end + alias_method :camelcase, :camelize + + # Capitalizes all the words and replaces some characters in the string to create + # a nicer looking title. +titleize+ is meant for creating pretty output. It is not + # used in the Rails internals. + # + # The trailing '_id','Id'.. can be kept and capitalized by setting the + # optional parameter +keep_id_suffix+ to true. + # By default, this parameter is false. + # + # 'man from the boondocks'.titleize # => "Man From The Boondocks" + # 'x-men: the last stand'.titleize # => "X Men: The Last Stand" + # 'string_ending_with_id'.titleize(keep_id_suffix: true) # => "String Ending With Id" + # + # +titleize+ is also aliased as +titlecase+. + # + # See ActiveSupport::Inflector.titleize. + def titleize(keep_id_suffix: false) + ActiveSupport::Inflector.titleize(self, keep_id_suffix: keep_id_suffix) + end + alias_method :titlecase, :titleize + + # The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string. + # + # +underscore+ will also change '::' to '/' to convert namespaces to paths. + # + # 'ActiveModel'.underscore # => "active_model" + # 'ActiveModel::Errors'.underscore # => "active_model/errors" + # + # See ActiveSupport::Inflector.underscore. + def underscore + ActiveSupport::Inflector.underscore(self) + end + + # Replaces underscores with dashes in the string. + # + # 'puni_puni'.dasherize # => "puni-puni" + # + # See ActiveSupport::Inflector.dasherize. + def dasherize + ActiveSupport::Inflector.dasherize(self) + end + + # Removes the module part from the constant expression in the string. + # + # 'ActiveSupport::Inflector::Inflections'.demodulize # => "Inflections" + # 'Inflections'.demodulize # => "Inflections" + # '::Inflections'.demodulize # => "Inflections" + # ''.demodulize # => '' + # + # See ActiveSupport::Inflector.demodulize. + # + # See also +deconstantize+. + def demodulize + ActiveSupport::Inflector.demodulize(self) + end + + # Removes the rightmost segment from the constant expression in the string. + # + # 'Net::HTTP'.deconstantize # => "Net" + # '::Net::HTTP'.deconstantize # => "::Net" + # 'String'.deconstantize # => "" + # '::String'.deconstantize # => "" + # ''.deconstantize # => "" + # + # See ActiveSupport::Inflector.deconstantize. + # + # See also +demodulize+. + def deconstantize + ActiveSupport::Inflector.deconstantize(self) + end + + # Replaces special characters in a string so that it may be used as part of a 'pretty' URL. + # + # If the optional parameter +locale+ is specified, + # the word will be parameterized as a word of that language. + # By default, this parameter is set to nil and it will use + # the configured I18n.locale. + # + # class Person + # def to_param + # "#{id}-#{name.parameterize}" + # end + # end + # + # @person = Person.find(1) + # # => # + # + # <%= link_to(@person.name, person_path) %> + # # => Donald E. Knuth + # + # To preserve the case of the characters in a string, use the +preserve_case+ argument. + # + # class Person + # def to_param + # "#{id}-#{name.parameterize(preserve_case: true)}" + # end + # end + # + # @person = Person.find(1) + # # => # + # + # <%= link_to(@person.name, person_path) %> + # # => Donald E. Knuth + # + # See ActiveSupport::Inflector.parameterize. + def parameterize(separator: "-", preserve_case: false, locale: nil) + ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case, locale: locale) + end + + # Creates the name of a table like Rails does for models to table names. This method + # uses the +pluralize+ method on the last word in the string. + # + # 'RawScaledScorer'.tableize # => "raw_scaled_scorers" + # 'ham_and_egg'.tableize # => "ham_and_eggs" + # 'fancyCategory'.tableize # => "fancy_categories" + # + # See ActiveSupport::Inflector.tableize. + def tableize + ActiveSupport::Inflector.tableize(self) + end + + # Creates a class name from a plural table name like Rails does for table names to models. + # Note that this returns a string and not a class. (To convert to an actual class + # follow +classify+ with +constantize+.) + # + # 'ham_and_eggs'.classify # => "HamAndEgg" + # 'posts'.classify # => "Post" + # + # See ActiveSupport::Inflector.classify. + def classify + ActiveSupport::Inflector.classify(self) + end + + # Capitalizes the first word, turns underscores into spaces, and (by default)strips a + # trailing '_id' if present. + # Like +titleize+, this is meant for creating pretty output. + # + # The capitalization of the first word can be turned off by setting the + # optional parameter +capitalize+ to false. + # By default, this parameter is true. + # + # The trailing '_id' can be kept and capitalized by setting the + # optional parameter +keep_id_suffix+ to true. + # By default, this parameter is false. + # + # 'employee_salary'.humanize # => "Employee salary" + # 'author_id'.humanize # => "Author" + # 'author_id'.humanize(capitalize: false) # => "author" + # '_id'.humanize # => "Id" + # 'author_id'.humanize(keep_id_suffix: true) # => "Author id" + # + # See ActiveSupport::Inflector.humanize. + def humanize(capitalize: true, keep_id_suffix: false) + ActiveSupport::Inflector.humanize(self, capitalize: capitalize, keep_id_suffix: keep_id_suffix) + end + + # Converts just the first character to uppercase. + # + # 'what a Lovely Day'.upcase_first # => "What a Lovely Day" + # 'w'.upcase_first # => "W" + # ''.upcase_first # => "" + # + # See ActiveSupport::Inflector.upcase_first. + def upcase_first + ActiveSupport::Inflector.upcase_first(self) + end + + # Creates a foreign key name from a class name. + # +separate_class_name_and_id_with_underscore+ sets whether + # the method should put '_' between the name and 'id'. + # + # 'Message'.foreign_key # => "message_id" + # 'Message'.foreign_key(false) # => "messageid" + # 'Admin::Post'.foreign_key # => "post_id" + # + # See ActiveSupport::Inflector.foreign_key. + def foreign_key(separate_class_name_and_id_with_underscore = true) + ActiveSupport::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/inquiry.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/inquiry.rb new file mode 100644 index 0000000..a3b42da --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/inquiry.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require "active_support/string_inquirer" +require "active_support/environment_inquirer" + +class String + # Wraps the current string in the ActiveSupport::StringInquirer class, + # which gives you a prettier way to test for equality. + # + # env = 'production'.inquiry + # env.production? # => true + # env.development? # => false + def inquiry + ActiveSupport::StringInquirer.new(self) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/multibyte.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/multibyte.rb new file mode 100644 index 0000000..0542121 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/multibyte.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require "active_support/multibyte" + +class String + # == Multibyte proxy + # + # +mb_chars+ is a multibyte safe proxy for string methods. + # + # It creates and returns an instance of the ActiveSupport::Multibyte::Chars class which + # encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy + # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string. + # + # >> "lj".mb_chars.upcase.to_s + # => "LJ" + # + # NOTE: Ruby 2.4 and later support native Unicode case mappings: + # + # >> "lj".upcase + # => "LJ" + # + # == Method chaining + # + # All the methods on the Chars proxy which normally return a string will return a Chars object. This allows + # method chaining on the result of any of these methods. + # + # name.mb_chars.reverse.length # => 12 + # + # == Interoperability and configuration + # + # The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between + # String and Char work like expected. The bang! methods change the internal string representation in the Chars + # object. Interoperability problems can be resolved easily with a +to_s+ call. + # + # For more information about the methods defined on the Chars proxy see ActiveSupport::Multibyte::Chars. For + # information about how to change the default Multibyte behavior see ActiveSupport::Multibyte. + def mb_chars + ActiveSupport::Multibyte.proxy_class.new(self) + end + + # Returns +true+ if string has utf_8 encoding. + # + # utf_8_str = "some string".encode "UTF-8" + # iso_str = "some string".encode "ISO-8859-1" + # + # utf_8_str.is_utf8? # => true + # iso_str.is_utf8? # => false + def is_utf8? + case encoding + when Encoding::UTF_8, Encoding::US_ASCII + valid_encoding? + when Encoding::ASCII_8BIT + dup.force_encoding(Encoding::UTF_8).valid_encoding? + else + false + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/output_safety.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/output_safety.rb new file mode 100644 index 0000000..597428c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/output_safety.rb @@ -0,0 +1,367 @@ +# frozen_string_literal: true + +require "erb" +require "active_support/core_ext/module/redefine_method" +require "active_support/multibyte/unicode" + +class ERB + module Util + HTML_ESCAPE = { "&" => "&", ">" => ">", "<" => "<", '"' => """, "'" => "'" } + JSON_ESCAPE = { "&" => '\u0026', ">" => '\u003e', "<" => '\u003c', "\u2028" => '\u2028', "\u2029" => '\u2029' } + HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+)|(#[xX][\dA-Fa-f]+));)/ + JSON_ESCAPE_REGEXP = /[\u2028\u2029&><]/u + + # Following XML requirements: https://www.w3.org/TR/REC-xml/#NT-Name + TAG_NAME_START_REGEXP_SET = "@:A-Z_a-z\u{C0}-\u{D6}\u{D8}-\u{F6}\u{F8}-\u{2FF}\u{370}-\u{37D}\u{37F}-\u{1FFF}" \ + "\u{200C}-\u{200D}\u{2070}-\u{218F}\u{2C00}-\u{2FEF}\u{3001}-\u{D7FF}\u{F900}-\u{FDCF}" \ + "\u{FDF0}-\u{FFFD}\u{10000}-\u{EFFFF}" + TAG_NAME_START_REGEXP = /[^#{TAG_NAME_START_REGEXP_SET}]/ + TAG_NAME_FOLLOWING_REGEXP = /[^#{TAG_NAME_START_REGEXP_SET}\-.0-9\u{B7}\u{0300}-\u{036F}\u{203F}-\u{2040}]/ + TAG_NAME_REPLACEMENT_CHAR = "_" + + # A utility method for escaping HTML tag characters. + # This method is also aliased as h. + # + # puts html_escape('is a > 0 & a < 10?') + # # => is a > 0 & a < 10? + def html_escape(s) + unwrapped_html_escape(s).html_safe + end + + silence_redefinition_of_method :h + alias h html_escape + + module_function :h + + singleton_class.silence_redefinition_of_method :html_escape + module_function :html_escape + + # HTML escapes strings but doesn't wrap them with an ActiveSupport::SafeBuffer. + # This method is not for public consumption! Seriously! + def unwrapped_html_escape(s) # :nodoc: + s = s.to_s + if s.html_safe? + s + else + CGI.escapeHTML(ActiveSupport::Multibyte::Unicode.tidy_bytes(s)) + end + end + module_function :unwrapped_html_escape + + # A utility method for escaping HTML without affecting existing escaped entities. + # + # html_escape_once('1 < 2 & 3') + # # => "1 < 2 & 3" + # + # html_escape_once('<< Accept & Checkout') + # # => "<< Accept & Checkout" + def html_escape_once(s) + result = ActiveSupport::Multibyte::Unicode.tidy_bytes(s.to_s).gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE) + s.html_safe? ? result.html_safe : result + end + + module_function :html_escape_once + + # A utility method for escaping HTML entities in JSON strings. Specifically, the + # &, > and < characters are replaced with their equivalent unicode escaped form - + # \u0026, \u003e, and \u003c. The Unicode sequences \u2028 and \u2029 are also + # escaped as they are treated as newline characters in some JavaScript engines. + # These sequences have identical meaning as the original characters inside the + # context of a JSON string, so assuming the input is a valid and well-formed + # JSON value, the output will have equivalent meaning when parsed: + # + # json = JSON.generate({ name: ""}) + # # => "{\"name\":\"\"}" + # + # json_escape(json) + # # => "{\"name\":\"\\u003C/script\\u003E\\u003Cscript\\u003Ealert('PWNED!!!')\\u003C/script\\u003E\"}" + # + # JSON.parse(json) == JSON.parse(json_escape(json)) + # # => true + # + # The intended use case for this method is to escape JSON strings before including + # them inside a script tag to avoid XSS vulnerability: + # + # + # + # It is necessary to +raw+ the result of +json_escape+, so that quotation marks + # don't get converted to " entities. +json_escape+ doesn't + # automatically flag the result as HTML safe, since the raw value is unsafe to + # use inside HTML attributes. + # + # If your JSON is being used downstream for insertion into the DOM, be aware of + # whether or not it is being inserted via html(). Most jQuery plugins do this. + # If that is the case, be sure to +html_escape+ or +sanitize+ any user-generated + # content returned by your JSON. + # + # If you need to output JSON elsewhere in your HTML, you can just do something + # like this, as any unsafe characters (including quotation marks) will be + # automatically escaped for you: + # + #
...
+ # + # WARNING: this helper only works with valid JSON. Using this on non-JSON values + # will open up serious XSS vulnerabilities. For example, if you replace the + # +current_user.to_json+ in the example above with user input instead, the browser + # will happily eval() that string as JavaScript. + # + # The escaping performed in this method is identical to those performed in the + # Active Support JSON encoder when +ActiveSupport.escape_html_entities_in_json+ is + # set to true. Because this transformation is idempotent, this helper can be + # applied even if +ActiveSupport.escape_html_entities_in_json+ is already true. + # + # Therefore, when you are unsure if +ActiveSupport.escape_html_entities_in_json+ + # is enabled, or if you are unsure where your JSON string originated from, it + # is recommended that you always apply this helper (other libraries, such as the + # JSON gem, do not provide this kind of protection by default; also some gems + # might override +to_json+ to bypass Active Support's encoder). + def json_escape(s) + result = s.to_s.gsub(JSON_ESCAPE_REGEXP, JSON_ESCAPE) + s.html_safe? ? result.html_safe : result + end + + module_function :json_escape + + # A utility method for escaping XML names of tags and names of attributes. + # + # xml_name_escape('1 < 2 & 3') + # # => "1___2___3" + # + # It follows the requirements of the specification: https://www.w3.org/TR/REC-xml/#NT-Name + def xml_name_escape(name) + name = name.to_s + return "" if name.blank? + + starting_char = name[0].gsub(TAG_NAME_START_REGEXP, TAG_NAME_REPLACEMENT_CHAR) + + return starting_char if name.size == 1 + + following_chars = name[1..-1].gsub(TAG_NAME_FOLLOWING_REGEXP, TAG_NAME_REPLACEMENT_CHAR) + + starting_char + following_chars + end + module_function :xml_name_escape + end +end + +class Object + def html_safe? + false + end +end + +class Numeric + def html_safe? + true + end +end + +module ActiveSupport # :nodoc: + class SafeBuffer < String + UNSAFE_STRING_METHODS = %w( + capitalize chomp chop delete delete_prefix delete_suffix + downcase lstrip next reverse rstrip scrub slice squeeze strip + succ swapcase tr tr_s unicode_normalize upcase + ) + + UNSAFE_STRING_METHODS_WITH_BACKREF = %w(gsub sub) + + alias_method :original_concat, :concat + private :original_concat + + # Raised when ActiveSupport::SafeBuffer#safe_concat is called on unsafe buffers. + class SafeConcatError < StandardError + def initialize + super "Could not concatenate to the buffer because it is not html safe." + end + end + + def [](*args) + if html_safe? + new_string = super + + return unless new_string + + new_safe_buffer = new_string.is_a?(SafeBuffer) ? new_string : SafeBuffer.new(new_string) + new_safe_buffer.instance_variable_set :@html_safe, true + new_safe_buffer + else + to_str[*args] + end + end + + def safe_concat(value) + raise SafeConcatError unless html_safe? + original_concat(value) + end + + def initialize(str = "") + @html_safe = true + super + end + + def initialize_copy(other) + super + @html_safe = other.html_safe? + end + + def clone_empty + self[0, 0] + end + + def concat(value) + unless value.nil? + super(implicit_html_escape_interpolated_argument(value)) + end + self + end + alias << concat + + def insert(index, value) + super(index, implicit_html_escape_interpolated_argument(value)) + end + + def prepend(value) + super(implicit_html_escape_interpolated_argument(value)) + end + + def replace(value) + super(implicit_html_escape_interpolated_argument(value)) + end + + def []=(*args) + if args.length == 3 + super(args[0], args[1], implicit_html_escape_interpolated_argument(args[2])) + else + super(args[0], implicit_html_escape_interpolated_argument(args[1])) + end + end + + def +(other) + dup.concat(other) + end + + def *(*) + new_string = super + new_safe_buffer = new_string.is_a?(SafeBuffer) ? new_string : SafeBuffer.new(new_string) + new_safe_buffer.instance_variable_set(:@html_safe, @html_safe) + new_safe_buffer + end + + def %(args) + case args + when Hash + escaped_args = args.transform_values { |arg| explicit_html_escape_interpolated_argument(arg) } + else + escaped_args = Array(args).map { |arg| explicit_html_escape_interpolated_argument(arg) } + end + + self.class.new(super(escaped_args)) + end + + def html_safe? + defined?(@html_safe) && @html_safe + end + + def to_s + self + end + + def to_param + to_str + end + + def encode_with(coder) + coder.represent_object nil, to_str + end + + UNSAFE_STRING_METHODS.each do |unsafe_method| + if unsafe_method.respond_to?(unsafe_method) + class_eval <<-EOT, __FILE__, __LINE__ + 1 + def #{unsafe_method}(*args, &block) # def capitalize(*args, &block) + to_str.#{unsafe_method}(*args, &block) # to_str.capitalize(*args, &block) + end # end + + def #{unsafe_method}!(*args) # def capitalize!(*args) + @html_safe = false # @html_safe = false + super # super + end # end + EOT + end + end + + UNSAFE_STRING_METHODS_WITH_BACKREF.each do |unsafe_method| + class_eval <<-EOT, __FILE__, __LINE__ + 1 + def #{unsafe_method}(*args, &block) # def gsub(*args, &block) + if block # if block + to_str.#{unsafe_method}(*args) { |*params| # to_str.gsub(*args) { |*params| + set_block_back_references(block, $~) # set_block_back_references(block, $~) + block.call(*params) # block.call(*params) + } # } + else # else + to_str.#{unsafe_method}(*args) # to_str.gsub(*args) + end # end + end # end + + def #{unsafe_method}!(*args, &block) # def gsub!(*args, &block) + @html_safe = false # @html_safe = false + if block # if block + super(*args) { |*params| # super(*args) { |*params| + set_block_back_references(block, $~) # set_block_back_references(block, $~) + block.call(*params) # block.call(*params) + } # } + else # else + super # super + end # end + end # end + EOT + end + + private + def explicit_html_escape_interpolated_argument(arg) + (!html_safe? || arg.html_safe?) ? arg : CGI.escapeHTML(arg.to_s) + end + + def implicit_html_escape_interpolated_argument(arg) + if !html_safe? || arg.html_safe? + arg + else + arg_string = begin + arg.to_str + rescue NoMethodError => error + if error.name == :to_str + str = arg.to_s + ActiveSupport::Deprecation.warn <<~MSG.squish + Implicit conversion of #{arg.class} into String by ActiveSupport::SafeBuffer + is deprecated and will be removed in Rails 7.1. + You must explicitly cast it to a String. + MSG + str + else + raise + end + end + CGI.escapeHTML(arg_string) + end + end + + def set_block_back_references(block, match_data) + block.binding.eval("proc { |m| $~ = m }").call(match_data) + rescue ArgumentError + # Can't create binding from C level Proc + end + end +end + +class String + # Marks a string as trusted safe. It will be inserted into HTML with no + # additional escaping performed. It is your responsibility to ensure that the + # string contains no malicious content. This method is equivalent to the + # +raw+ helper in views. It is recommended that you use +sanitize+ instead of + # this method. It should never be called on user input. + def html_safe + ActiveSupport::SafeBuffer.new(self) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/starts_ends_with.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/starts_ends_with.rb new file mode 100644 index 0000000..1e21637 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/starts_ends_with.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +class String + alias :starts_with? :start_with? + alias :ends_with? :end_with? +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/strip.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/strip.rb new file mode 100644 index 0000000..60e9952 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/strip.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class String + # Strips indentation in heredocs. + # + # For example in + # + # if options[:usage] + # puts <<-USAGE.strip_heredoc + # This command does such and such. + # + # Supported options are: + # -h This message + # ... + # USAGE + # end + # + # the user would see the usage message aligned against the left margin. + # + # Technically, it looks for the least indented non-empty line + # in the whole string, and removes that amount of leading whitespace. + def strip_heredoc + gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, "").tap do |stripped| + stripped.freeze if frozen? + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/zones.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/zones.rb new file mode 100644 index 0000000..55dc231 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/string/zones.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require "active_support/core_ext/string/conversions" +require "active_support/core_ext/time/zones" + +class String + # Converts String to a TimeWithZone in the current zone if Time.zone or Time.zone_default + # is set, otherwise converts String to a Time via String#to_time + def in_time_zone(zone = ::Time.zone) + if zone + ::Time.find_zone!(zone).parse(self) + else + to_time + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/symbol.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/symbol.rb new file mode 100644 index 0000000..709fed2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/symbol.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require "active_support/core_ext/symbol/starts_ends_with" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/symbol/starts_ends_with.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/symbol/starts_ends_with.rb new file mode 100644 index 0000000..4f85217 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/symbol/starts_ends_with.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +class Symbol + alias :starts_with? :start_with? + alias :ends_with? :end_with? +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/time.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/time.rb new file mode 100644 index 0000000..9716257 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/time.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +require "active_support/core_ext/time/acts_like" +require "active_support/core_ext/time/calculations" +require "active_support/core_ext/time/compatibility" +require "active_support/core_ext/time/conversions" +require "active_support/core_ext/time/deprecated_conversions" unless ENV["RAILS_DISABLE_DEPRECATED_TO_S_CONVERSION"] +require "active_support/core_ext/time/zones" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/time/acts_like.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/time/acts_like.rb new file mode 100644 index 0000000..8572b49 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/time/acts_like.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require "active_support/core_ext/object/acts_like" + +class Time + # Duck-types as a Time-like class. See Object#acts_like?. + def acts_like_time? + true + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/time/calculations.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/time/calculations.rb new file mode 100644 index 0000000..31ef3f2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/time/calculations.rb @@ -0,0 +1,364 @@ +# frozen_string_literal: true + +require "active_support/duration" +require "active_support/core_ext/time/conversions" +require "active_support/time_with_zone" +require "active_support/core_ext/time/zones" +require "active_support/core_ext/date_and_time/calculations" +require "active_support/core_ext/date/calculations" +require "active_support/core_ext/module/remove_method" + +class Time + include DateAndTime::Calculations + + COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + + class << self + # Overriding case equality method so that it returns true for ActiveSupport::TimeWithZone instances + def ===(other) + super || (self == Time && other.is_a?(ActiveSupport::TimeWithZone)) + end + + # Returns the number of days in the given month. + # If no year is specified, it will use the current year. + def days_in_month(month, year = current.year) + if month == 2 && ::Date.gregorian_leap?(year) + 29 + else + COMMON_YEAR_DAYS_IN_MONTH[month] + end + end + + # Returns the number of days in the given year. + # If no year is specified, it will use the current year. + def days_in_year(year = current.year) + days_in_month(2, year) + 337 + end + + # Returns Time.zone.now when Time.zone or config.time_zone are set, otherwise just returns Time.now. + def current + ::Time.zone ? ::Time.zone.now : ::Time.now + end + + # Layers additional behavior on Time.at so that ActiveSupport::TimeWithZone and DateTime + # instances can be used when called with a single argument + def at_with_coercion(*args, **kwargs) + return at_without_coercion(*args, **kwargs) if args.size != 1 || !kwargs.empty? + + # Time.at can be called with a time or numerical value + time_or_number = args.first + + if time_or_number.is_a?(ActiveSupport::TimeWithZone) + at_without_coercion(time_or_number.to_r).getlocal + elsif time_or_number.is_a?(DateTime) + at_without_coercion(time_or_number.to_f).getlocal + else + at_without_coercion(time_or_number) + end + end + alias_method :at_without_coercion, :at + alias_method :at, :at_with_coercion + + # Creates a +Time+ instance from an RFC 3339 string. + # + # Time.rfc3339('1999-12-31T14:00:00-10:00') # => 2000-01-01 00:00:00 -1000 + # + # If the time or offset components are missing then an +ArgumentError+ will be raised. + # + # Time.rfc3339('1999-12-31') # => ArgumentError: invalid date + def rfc3339(str) + parts = Date._rfc3339(str) + + raise ArgumentError, "invalid date" if parts.empty? + + Time.new( + parts.fetch(:year), + parts.fetch(:mon), + parts.fetch(:mday), + parts.fetch(:hour), + parts.fetch(:min), + parts.fetch(:sec) + parts.fetch(:sec_fraction, 0), + parts.fetch(:offset) + ) + end + end + + # Returns the number of seconds since 00:00:00. + # + # Time.new(2012, 8, 29, 0, 0, 0).seconds_since_midnight # => 0.0 + # Time.new(2012, 8, 29, 12, 34, 56).seconds_since_midnight # => 45296.0 + # Time.new(2012, 8, 29, 23, 59, 59).seconds_since_midnight # => 86399.0 + def seconds_since_midnight + to_i - change(hour: 0).to_i + (usec / 1.0e+6) + end + + # Returns the number of seconds until 23:59:59. + # + # Time.new(2012, 8, 29, 0, 0, 0).seconds_until_end_of_day # => 86399 + # Time.new(2012, 8, 29, 12, 34, 56).seconds_until_end_of_day # => 41103 + # Time.new(2012, 8, 29, 23, 59, 59).seconds_until_end_of_day # => 0 + def seconds_until_end_of_day + end_of_day.to_i - to_i + end + + # Returns the fraction of a second as a +Rational+ + # + # Time.new(2012, 8, 29, 0, 0, 0.5).sec_fraction # => (1/2) + def sec_fraction + subsec + end + + unless Time.method_defined?(:floor) + def floor(precision = 0) + change(nsec: 0) + subsec.floor(precision) + end + end + + # Restricted Ruby version due to a bug in `Time#ceil` + # See https://bugs.ruby-lang.org/issues/17025 for more details + if RUBY_VERSION <= "2.8" + remove_possible_method :ceil + def ceil(precision = 0) + change(nsec: 0) + subsec.ceil(precision) + end + end + + # Returns a new Time where one or more of the elements have been changed according + # to the +options+ parameter. The time options (:hour, :min, + # :sec, :usec, :nsec) reset cascadingly, so if only + # the hour is passed, then minute, sec, usec, and nsec is set to 0. If the hour + # and minute is passed, then sec, usec, and nsec is set to 0. The +options+ parameter + # takes a hash with any of these keys: :year, :month, :day, + # :hour, :min, :sec, :usec, :nsec, + # :offset. Pass either :usec or :nsec, not both. + # + # Time.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => Time.new(2012, 8, 1, 22, 35, 0) + # Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => Time.new(1981, 8, 1, 22, 35, 0) + # Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, hour: 0) # => Time.new(1981, 8, 29, 0, 0, 0) + def change(options) + new_year = options.fetch(:year, year) + new_month = options.fetch(:month, month) + new_day = options.fetch(:day, day) + new_hour = options.fetch(:hour, hour) + new_min = options.fetch(:min, options[:hour] ? 0 : min) + new_sec = options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec) + new_offset = options.fetch(:offset, nil) + + if new_nsec = options[:nsec] + raise ArgumentError, "Can't change both :nsec and :usec at the same time: #{options.inspect}" if options[:usec] + new_usec = Rational(new_nsec, 1000) + else + new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000)) + end + + raise ArgumentError, "argument out of range" if new_usec >= 1000000 + + new_sec += Rational(new_usec, 1000000) + + if new_offset + ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, new_offset) + elsif utc? + ::Time.utc(new_year, new_month, new_day, new_hour, new_min, new_sec) + elsif zone&.respond_to?(:utc_to_local) + ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, zone) + elsif zone + ::Time.local(new_year, new_month, new_day, new_hour, new_min, new_sec) + else + ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, utc_offset) + end + end + + # Uses Date to provide precise Time calculations for years, months, and days + # according to the proleptic Gregorian calendar. The +options+ parameter + # takes a hash with any of these keys: :years, :months, + # :weeks, :days, :hours, :minutes, + # :seconds. + # + # Time.new(2015, 8, 1, 14, 35, 0).advance(seconds: 1) # => 2015-08-01 14:35:01 -0700 + # Time.new(2015, 8, 1, 14, 35, 0).advance(minutes: 1) # => 2015-08-01 14:36:00 -0700 + # Time.new(2015, 8, 1, 14, 35, 0).advance(hours: 1) # => 2015-08-01 15:35:00 -0700 + # Time.new(2015, 8, 1, 14, 35, 0).advance(days: 1) # => 2015-08-02 14:35:00 -0700 + # Time.new(2015, 8, 1, 14, 35, 0).advance(weeks: 1) # => 2015-08-08 14:35:00 -0700 + def advance(options) + unless options[:weeks].nil? + options[:weeks], partial_weeks = options[:weeks].divmod(1) + options[:days] = options.fetch(:days, 0) + 7 * partial_weeks + end + + unless options[:days].nil? + options[:days], partial_days = options[:days].divmod(1) + options[:hours] = options.fetch(:hours, 0) + 24 * partial_days + end + + d = to_date.gregorian.advance(options) + time_advanced_by_date = change(year: d.year, month: d.month, day: d.day) + seconds_to_advance = \ + options.fetch(:seconds, 0) + + options.fetch(:minutes, 0) * 60 + + options.fetch(:hours, 0) * 3600 + + if seconds_to_advance.zero? + time_advanced_by_date + else + time_advanced_by_date.since(seconds_to_advance) + end + end + + # Returns a new Time representing the time a number of seconds ago, this is basically a wrapper around the Numeric extension + def ago(seconds) + since(-seconds) + end + + # Returns a new Time representing the time a number of seconds since the instance time + def since(seconds) + self + seconds + rescue + to_datetime.since(seconds) + end + alias :in :since + + # Returns a new Time representing the start of the day (0:00) + def beginning_of_day + change(hour: 0) + end + alias :midnight :beginning_of_day + alias :at_midnight :beginning_of_day + alias :at_beginning_of_day :beginning_of_day + + # Returns a new Time representing the middle of the day (12:00) + def middle_of_day + change(hour: 12) + end + alias :midday :middle_of_day + alias :noon :middle_of_day + alias :at_midday :middle_of_day + alias :at_noon :middle_of_day + alias :at_middle_of_day :middle_of_day + + # Returns a new Time representing the end of the day, 23:59:59.999999 + def end_of_day + change( + hour: 23, + min: 59, + sec: 59, + usec: Rational(999999999, 1000) + ) + end + alias :at_end_of_day :end_of_day + + # Returns a new Time representing the start of the hour (x:00) + def beginning_of_hour + change(min: 0) + end + alias :at_beginning_of_hour :beginning_of_hour + + # Returns a new Time representing the end of the hour, x:59:59.999999 + def end_of_hour + change( + min: 59, + sec: 59, + usec: Rational(999999999, 1000) + ) + end + alias :at_end_of_hour :end_of_hour + + # Returns a new Time representing the start of the minute (x:xx:00) + def beginning_of_minute + change(sec: 0) + end + alias :at_beginning_of_minute :beginning_of_minute + + # Returns a new Time representing the end of the minute, x:xx:59.999999 + def end_of_minute + change( + sec: 59, + usec: Rational(999999999, 1000) + ) + end + alias :at_end_of_minute :end_of_minute + + def plus_with_duration(other) # :nodoc: + if ActiveSupport::Duration === other + other.since(self) + else + plus_without_duration(other) + end + end + alias_method :plus_without_duration, :+ + alias_method :+, :plus_with_duration + + def minus_with_duration(other) # :nodoc: + if ActiveSupport::Duration === other + other.until(self) + else + minus_without_duration(other) + end + end + alias_method :minus_without_duration, :- + alias_method :-, :minus_with_duration + + # Time#- can also be used to determine the number of seconds between two Time instances. + # We're layering on additional behavior so that ActiveSupport::TimeWithZone instances + # are coerced into values that Time#- will recognize + def minus_with_coercion(other) + other = other.comparable_time if other.respond_to?(:comparable_time) + other.is_a?(DateTime) ? to_f - other.to_f : minus_without_coercion(other) + end + alias_method :minus_without_coercion, :- + alias_method :-, :minus_with_coercion # rubocop:disable Lint/DuplicateMethods + + # Layers additional behavior on Time#<=> so that DateTime and ActiveSupport::TimeWithZone instances + # can be chronologically compared with a Time + def compare_with_coercion(other) + # we're avoiding Time#to_datetime and Time#to_time because they're expensive + if other.class == Time + compare_without_coercion(other) + elsif other.is_a?(Time) + compare_without_coercion(other.to_time) + else + to_datetime <=> other + end + end + alias_method :compare_without_coercion, :<=> + alias_method :<=>, :compare_with_coercion + + # Layers additional behavior on Time#eql? so that ActiveSupport::TimeWithZone instances + # can be eql? to an equivalent Time + def eql_with_coercion(other) + # if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do eql? comparison + other = other.comparable_time if other.respond_to?(:comparable_time) + eql_without_coercion(other) + end + alias_method :eql_without_coercion, :eql? + alias_method :eql?, :eql_with_coercion + + # Returns a new time the specified number of days ago. + def prev_day(days = 1) + advance(days: -days) + end + + # Returns a new time the specified number of days in the future. + def next_day(days = 1) + advance(days: days) + end + + # Returns a new time the specified number of months ago. + def prev_month(months = 1) + advance(months: -months) + end + + # Returns a new time the specified number of months in the future. + def next_month(months = 1) + advance(months: months) + end + + # Returns a new time the specified number of years ago. + def prev_year(years = 1) + advance(years: -years) + end + + # Returns a new time the specified number of years in the future. + def next_year(years = 1) + advance(years: years) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/time/compatibility.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/time/compatibility.rb new file mode 100644 index 0000000..495e4f3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/time/compatibility.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require "active_support/core_ext/date_and_time/compatibility" +require "active_support/core_ext/module/redefine_method" + +class Time + include DateAndTime::Compatibility + + silence_redefinition_of_method :to_time + + # Either return +self+ or the time in the local system timezone depending + # on the setting of +ActiveSupport.to_time_preserves_timezone+. + def to_time + preserve_timezone ? self : getlocal + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/time/conversions.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/time/conversions.rb new file mode 100644 index 0000000..aeb8e14 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/time/conversions.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +require "time" +require "active_support/inflector/methods" +require "active_support/values/time_zone" + +class Time + DATE_FORMATS = { + db: "%Y-%m-%d %H:%M:%S", + inspect: "%Y-%m-%d %H:%M:%S.%9N %z", + number: "%Y%m%d%H%M%S", + nsec: "%Y%m%d%H%M%S%9N", + usec: "%Y%m%d%H%M%S%6N", + time: "%H:%M", + short: "%d %b %H:%M", + long: "%B %d, %Y %H:%M", + long_ordinal: lambda { |time| + day_format = ActiveSupport::Inflector.ordinalize(time.day) + time.strftime("%B #{day_format}, %Y %H:%M") + }, + rfc822: lambda { |time| + offset_format = time.formatted_offset(false) + time.strftime("%a, %d %b %Y %H:%M:%S #{offset_format}") + }, + iso8601: lambda { |time| time.iso8601 } + } + + # Converts to a formatted string. See DATE_FORMATS for built-in formats. + # + # This method is aliased to to_formatted_s. + # + # time = Time.now # => 2007-01-18 06:10:17 -06:00 + # + # time.to_fs(:time) # => "06:10" + # time.to_formatted_s(:time) # => "06:10" + # + # time.to_fs(:db) # => "2007-01-18 06:10:17" + # time.to_fs(:number) # => "20070118061017" + # time.to_fs(:short) # => "18 Jan 06:10" + # time.to_fs(:long) # => "January 18, 2007 06:10" + # time.to_fs(:long_ordinal) # => "January 18th, 2007 06:10" + # time.to_fs(:rfc822) # => "Thu, 18 Jan 2007 06:10:17 -0600" + # time.to_fs(:iso8601) # => "2007-01-18T06:10:17-06:00" + # + # == Adding your own time formats to +to_fs+ + # You can add your own formats to the Time::DATE_FORMATS hash. + # Use the format name as the hash key and either a strftime string + # or Proc instance that takes a time argument as the value. + # + # # config/initializers/time_formats.rb + # Time::DATE_FORMATS[:month_and_year] = '%B %Y' + # Time::DATE_FORMATS[:short_ordinal] = ->(time) { time.strftime("%B #{time.day.ordinalize}") } + def to_fs(format = :default) + if formatter = DATE_FORMATS[format] + formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter) + else + # Change to `to_s` when deprecation is gone. Also deprecate `to_default_s`. + to_default_s + end + end + alias_method :to_formatted_s, :to_fs + alias_method :to_default_s, :to_s + + # Returns a formatted string of the offset from UTC, or an alternative + # string if the time zone is already UTC. + # + # Time.local(2000).formatted_offset # => "-06:00" + # Time.local(2000).formatted_offset(false) # => "-0600" + def formatted_offset(colon = true, alternate_utc_string = nil) + utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon) + end + + # Aliased to +xmlschema+ for compatibility with +DateTime+ + alias_method :rfc3339, :xmlschema +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/time/deprecated_conversions.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/time/deprecated_conversions.rb new file mode 100644 index 0000000..2fe730b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/time/deprecated_conversions.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require "time" + +class Time + NOT_SET = Object.new # :nodoc: + def to_s(format = NOT_SET) # :nodoc: + if formatter = DATE_FORMATS[format] + ActiveSupport::Deprecation.warn( + "Time#to_s(#{format.inspect}) is deprecated. Please use Time#to_fs(#{format.inspect}) instead." + ) + formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter) + elsif format == NOT_SET + to_default_s + else + ActiveSupport::Deprecation.warn( + "Time#to_s(#{format.inspect}) is deprecated. Please use Time#to_fs(#{format.inspect}) instead." + ) + to_default_s + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/time/zones.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/time/zones.rb new file mode 100644 index 0000000..21ba4d1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/time/zones.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +require "active_support/time_with_zone" +require "active_support/core_ext/time/acts_like" +require "active_support/core_ext/date_and_time/zones" + +class Time + include DateAndTime::Zones + class << self + attr_accessor :zone_default + + # Returns the TimeZone for the current request, if this has been set (via Time.zone=). + # If Time.zone has not been set for the current request, returns the TimeZone specified in config.time_zone. + def zone + ::ActiveSupport::IsolatedExecutionState[:time_zone] || zone_default + end + + # Sets Time.zone to a TimeZone object for the current request/thread. + # + # This method accepts any of the following: + # + # * A Rails TimeZone object. + # * An identifier for a Rails TimeZone object (e.g., "Eastern Time (US & Canada)", -5.hours). + # * A TZInfo::Timezone object. + # * An identifier for a TZInfo::Timezone object (e.g., "America/New_York"). + # + # Here's an example of how you might set Time.zone on a per request basis and reset it when the request is done. + # current_user.time_zone just needs to return a string identifying the user's preferred time zone: + # + # class ApplicationController < ActionController::Base + # around_action :set_time_zone + # + # def set_time_zone + # if logged_in? + # Time.use_zone(current_user.time_zone) { yield } + # else + # yield + # end + # end + # end + def zone=(time_zone) + ::ActiveSupport::IsolatedExecutionState[:time_zone] = find_zone!(time_zone) + end + + # Allows override of Time.zone locally inside supplied block; + # resets Time.zone to existing value when done. + # + # class ApplicationController < ActionController::Base + # around_action :set_time_zone + # + # private + # + # def set_time_zone + # Time.use_zone(current_user.timezone) { yield } + # end + # end + # + # NOTE: This won't affect any ActiveSupport::TimeWithZone + # objects that have already been created, e.g. any model timestamp + # attributes that have been read before the block will remain in + # the application's default timezone. + def use_zone(time_zone) + new_zone = find_zone!(time_zone) + begin + old_zone, ::Time.zone = ::Time.zone, new_zone + yield + ensure + ::Time.zone = old_zone + end + end + + # Returns a TimeZone instance matching the time zone provided. + # Accepts the time zone in any format supported by Time.zone=. + # Raises an +ArgumentError+ for invalid time zones. + # + # Time.find_zone! "America/New_York" # => # + # Time.find_zone! "EST" # => # + # Time.find_zone! -5.hours # => # + # Time.find_zone! nil # => nil + # Time.find_zone! false # => false + # Time.find_zone! "NOT-A-TIMEZONE" # => ArgumentError: Invalid Timezone: NOT-A-TIMEZONE + def find_zone!(time_zone) + return time_zone unless time_zone + + ActiveSupport::TimeZone[time_zone] || raise(ArgumentError, "Invalid Timezone: #{time_zone}") + end + + # Returns a TimeZone instance matching the time zone provided. + # Accepts the time zone in any format supported by Time.zone=. + # Returns +nil+ for invalid time zones. + # + # Time.find_zone "America/New_York" # => # + # Time.find_zone "NOT-A-TIMEZONE" # => nil + def find_zone(time_zone) + find_zone!(time_zone) rescue nil + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/uri.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/uri.rb new file mode 100644 index 0000000..9811477 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/core_ext/uri.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +ActiveSupport::Deprecation.warn(<<-MSG.squish) + `active_support/core_ext/uri` is deprecated and will be removed in Rails 7.1. +MSG diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/current_attributes.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/current_attributes.rb new file mode 100644 index 0000000..b4c6a5d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/current_attributes.rb @@ -0,0 +1,226 @@ +# frozen_string_literal: true + +require "active_support/callbacks" +require "active_support/core_ext/enumerable" +require "active_support/core_ext/module/delegation" + +module ActiveSupport + # Abstract super class that provides a thread-isolated attributes singleton, which resets automatically + # before and after each request. This allows you to keep all the per-request attributes easily + # available to the whole system. + # + # The following full app-like example demonstrates how to use a Current class to + # facilitate easy access to the global, per-request attributes without passing them deeply + # around everywhere: + # + # # app/models/current.rb + # class Current < ActiveSupport::CurrentAttributes + # attribute :account, :user + # attribute :request_id, :user_agent, :ip_address + # + # resets { Time.zone = nil } + # + # def user=(user) + # super + # self.account = user.account + # Time.zone = user.time_zone + # end + # end + # + # # app/controllers/concerns/authentication.rb + # module Authentication + # extend ActiveSupport::Concern + # + # included do + # before_action :authenticate + # end + # + # private + # def authenticate + # if authenticated_user = User.find_by(id: cookies.encrypted[:user_id]) + # Current.user = authenticated_user + # else + # redirect_to new_session_url + # end + # end + # end + # + # # app/controllers/concerns/set_current_request_details.rb + # module SetCurrentRequestDetails + # extend ActiveSupport::Concern + # + # included do + # before_action do + # Current.request_id = request.uuid + # Current.user_agent = request.user_agent + # Current.ip_address = request.ip + # end + # end + # end + # + # class ApplicationController < ActionController::Base + # include Authentication + # include SetCurrentRequestDetails + # end + # + # class MessagesController < ApplicationController + # def create + # Current.account.messages.create(message_params) + # end + # end + # + # class Message < ApplicationRecord + # belongs_to :creator, default: -> { Current.user } + # after_create { |message| Event.create(record: message) } + # end + # + # class Event < ApplicationRecord + # before_create do + # self.request_id = Current.request_id + # self.user_agent = Current.user_agent + # self.ip_address = Current.ip_address + # end + # end + # + # A word of caution: It's easy to overdo a global singleton like Current and tangle your model as a result. + # Current should only be used for a few, top-level globals, like account, user, and request details. + # The attributes stuck in Current should be used by more or less all actions on all requests. If you start + # sticking controller-specific attributes in there, you're going to create a mess. + class CurrentAttributes + include ActiveSupport::Callbacks + define_callbacks :reset + + class << self + # Returns singleton instance for this class in this thread. If none exists, one is created. + def instance + current_instances[current_instances_key] ||= new + end + + # Declares one or more attributes that will be given both class and instance accessor methods. + def attribute(*names) + ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |owner| + names.each do |name| + owner.define_cached_method(name, namespace: :current_attributes) do |batch| + batch << + "def #{name}" << + "attributes[:#{name}]" << + "end" + end + owner.define_cached_method("#{name}=", namespace: :current_attributes) do |batch| + batch << + "def #{name}=(value)" << + "attributes[:#{name}] = value" << + "end" + end + end + end + + ActiveSupport::CodeGenerator.batch(singleton_class, __FILE__, __LINE__) do |owner| + names.each do |name| + owner.define_cached_method(name, namespace: :current_attributes_delegation) do |batch| + batch << + "def #{name}" << + "instance.#{name}" << + "end" + end + owner.define_cached_method("#{name}=", namespace: :current_attributes_delegation) do |batch| + batch << + "def #{name}=(value)" << + "instance.#{name} = value" << + "end" + end + end + end + end + + # Calls this block before #reset is called on the instance. Used for resetting external collaborators that depend on current values. + def before_reset(&block) + set_callback :reset, :before, &block + end + + # Calls this block after #reset is called on the instance. Used for resetting external collaborators, like Time.zone. + def resets(&block) + set_callback :reset, :after, &block + end + alias_method :after_reset, :resets + + delegate :set, :reset, to: :instance + + def reset_all # :nodoc: + current_instances.each_value(&:reset) + end + + def clear_all # :nodoc: + reset_all + current_instances.clear + end + + private + def generated_attribute_methods + @generated_attribute_methods ||= Module.new.tap { |mod| include mod } + end + + def current_instances + IsolatedExecutionState[:current_attributes_instances] ||= {} + end + + def current_instances_key + @current_instances_key ||= name.to_sym + end + + def method_missing(name, *args, &block) + # Caches the method definition as a singleton method of the receiver. + # + # By letting #delegate handle it, we avoid an enclosure that'll capture args. + singleton_class.delegate name, to: :instance + + send(name, *args, &block) + end + ruby2_keywords(:method_missing) + + def respond_to_missing?(name, _) + super || instance.respond_to?(name) + end + end + + attr_accessor :attributes + + def initialize + @attributes = {} + end + + # Expose one or more attributes within a block. Old values are returned after the block concludes. + # Example demonstrating the common use of needing to set Current attributes outside the request-cycle: + # + # class Chat::PublicationJob < ApplicationJob + # def perform(attributes, room_number, creator) + # Current.set(person: creator) do + # Chat::Publisher.publish(attributes: attributes, room_number: room_number) + # end + # end + # end + def set(set_attributes) + old_attributes = compute_attributes(set_attributes.keys) + assign_attributes(set_attributes) + yield + ensure + assign_attributes(old_attributes) + end + + # Reset all attributes. Should be called before and after actions, when used as a per-request singleton. + def reset + run_callbacks :reset do + self.attributes = {} + end + end + + private + def assign_attributes(new_attributes) + new_attributes.each { |key, value| public_send("#{key}=", value) } + end + + def compute_attributes(keys) + keys.index_with { |key| public_send(key) } + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/current_attributes/test_helper.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/current_attributes/test_helper.rb new file mode 100644 index 0000000..2016384 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/current_attributes/test_helper.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module ActiveSupport::CurrentAttributes::TestHelper # :nodoc: + def before_setup + ActiveSupport::CurrentAttributes.reset_all + super + end + + def after_teardown + super + ActiveSupport::CurrentAttributes.reset_all + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/dependencies.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/dependencies.rb new file mode 100644 index 0000000..bb1fc46 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/dependencies.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +require "set" +require "active_support/dependencies/interlock" + +module ActiveSupport # :nodoc: + module Dependencies # :nodoc: + require_relative "dependencies/require_dependency" + + singleton_class.attr_accessor :interlock + @interlock = Interlock.new + + # :doc: + + # Execute the supplied block without interference from any + # concurrent loads. + def self.run_interlock(&block) + interlock.running(&block) + end + + # Execute the supplied block while holding an exclusive lock, + # preventing any other thread from being inside a #run_interlock + # block at the same time. + def self.load_interlock(&block) + interlock.loading(&block) + end + + # Execute the supplied block while holding an exclusive lock, + # preventing any other thread from being inside a #run_interlock + # block at the same time. + def self.unload_interlock(&block) + interlock.unloading(&block) + end + + # :nodoc: + + # The array of directories from which we autoload and reload, if reloading + # is enabled. The public interface to push directories to this collection + # from applications or engines is config.autoload_paths. + # + # This collection is allowed to have intersection with autoload_once_paths. + # Common directories are not reloaded. + singleton_class.attr_accessor :autoload_paths + self.autoload_paths = [] + + # The array of directories from which we autoload and never reload, even if + # reloading is enabled. The public interface to push directories to this + # collection from applications or engines is config.autoload_once_paths. + singleton_class.attr_accessor :autoload_once_paths + self.autoload_once_paths = [] + + # This is a private set that collects all eager load paths during bootstrap. + # Useful for Zeitwerk integration. The public interface to push custom + # directories to this collection from applications or engines is + # config.eager_load_paths. + singleton_class.attr_accessor :_eager_load_paths + self._eager_load_paths = Set.new + + # If reloading is enabled, this private set holds autoloaded classes tracked + # by the descendants tracker. It is populated by an on_load callback in the + # main autoloader. Used to clear state. + singleton_class.attr_accessor :_autoloaded_tracked_classes + self._autoloaded_tracked_classes = Set.new + + # If reloading is enabled, this private attribute stores the main autoloader + # of a Rails application. It is `nil` otherwise. + # + # The public interface for this autoloader is `Rails.autoloaders.main`. + singleton_class.attr_accessor :autoloader + + # Private method that reloads constants autoloaded by the main autoloader. + # + # Rails.application.reloader.reload! is the public interface for application + # reload. That involves more things, like deleting unloaded classes from the + # internal state of the descendants tracker, or reloading routes. + def self.clear + unload_interlock do + _autoloaded_tracked_classes.clear + autoloader.reload + end + end + + # Private method used by require_dependency. + def self.search_for_file(relpath) + relpath += ".rb" unless relpath.end_with?(".rb") + autoload_paths.each do |autoload_path| + abspath = File.join(autoload_path, relpath) + return abspath if File.file?(abspath) + end + nil + end + + # Private method that helps configuring the autoloaders. + def self.eager_load?(path) + _eager_load_paths.member?(path) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/dependencies/autoload.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/dependencies/autoload.rb new file mode 100644 index 0000000..1cee85d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/dependencies/autoload.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require "active_support/inflector/methods" + +module ActiveSupport + # Autoload and eager load conveniences for your library. + # + # This module allows you to define autoloads based on + # Rails conventions (i.e. no need to define the path + # it is automatically guessed based on the filename) + # and also define a set of constants that needs to be + # eager loaded: + # + # module MyLib + # extend ActiveSupport::Autoload + # + # autoload :Model + # + # eager_autoload do + # autoload :Cache + # end + # end + # + # Then your library can be eager loaded by simply calling: + # + # MyLib.eager_load! + module Autoload + def self.extended(base) # :nodoc: + base.class_eval do + @_autoloads = {} + @_under_path = nil + @_at_path = nil + @_eager_autoload = false + end + end + + def autoload(const_name, path = @_at_path) + unless path + full = [name, @_under_path, const_name.to_s].compact.join("::") + path = Inflector.underscore(full) + end + + if @_eager_autoload + @_autoloads[const_name] = path + end + + super const_name, path + end + + def autoload_under(path) + @_under_path, old_path = path, @_under_path + yield + ensure + @_under_path = old_path + end + + def autoload_at(path) + @_at_path, old_path = path, @_at_path + yield + ensure + @_at_path = old_path + end + + def eager_autoload + old_eager, @_eager_autoload = @_eager_autoload, true + yield + ensure + @_eager_autoload = old_eager + end + + def eager_load! + @_autoloads.each_value { |file| require file } + end + + def autoloads + @_autoloads + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/dependencies/interlock.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/dependencies/interlock.rb new file mode 100644 index 0000000..e0e32e8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/dependencies/interlock.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require "active_support/concurrency/share_lock" + +module ActiveSupport # :nodoc: + module Dependencies # :nodoc: + class Interlock + def initialize # :nodoc: + @lock = ActiveSupport::Concurrency::ShareLock.new + end + + def loading(&block) + @lock.exclusive(purpose: :load, compatible: [:load], after_compatible: [:load], &block) + end + + def unloading(&block) + @lock.exclusive(purpose: :unload, compatible: [:load, :unload], after_compatible: [:load, :unload], &block) + end + + def start_unloading + @lock.start_exclusive(purpose: :unload, compatible: [:load, :unload]) + end + + def done_unloading + @lock.stop_exclusive(compatible: [:load, :unload]) + end + + def start_running + @lock.start_sharing + end + + def done_running + @lock.stop_sharing + end + + def running(&block) + @lock.sharing(&block) + end + + def permit_concurrent_loads(&block) + @lock.yield_shares(compatible: [:load], &block) + end + + def raw_state(&block) # :nodoc: + @lock.raw_state(&block) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/dependencies/require_dependency.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/dependencies/require_dependency.rb new file mode 100644 index 0000000..403f5fa --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/dependencies/require_dependency.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module ActiveSupport::Dependencies::RequireDependency + # Warning: This method is obsolete. The semantics of the autoloader + # match Ruby's and you do not need to be defensive with load order anymore. + # Just refer to classes and modules normally. + # + # Engines that do not control the mode in which their parent application runs + # should call +require_dependency+ where needed in case the runtime mode is + # +:classic+. + def require_dependency(filename) + filename = filename.to_path if filename.respond_to?(:to_path) + + unless filename.is_a?(String) + raise ArgumentError, "the file name must be either a String or implement #to_path -- you passed #{filename.inspect}" + end + + if abspath = ActiveSupport::Dependencies.search_for_file(filename) + require abspath + else + require filename + end + end + + # We could define require_dependency in Object directly, but a module makes + # the extension apparent if you list ancestors. + Object.prepend(self) +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation.rb new file mode 100644 index 0000000..d728386 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require "singleton" + +module ActiveSupport + # \Deprecation specifies the API used by Rails to deprecate methods, instance + # variables, objects, and constants. + class Deprecation + # active_support.rb sets an autoload for ActiveSupport::Deprecation. + # + # If these requires were at the top of the file the constant would not be + # defined by the time their files were loaded. Since some of them reopen + # ActiveSupport::Deprecation its autoload would be triggered, resulting in + # a circular require warning for active_support/deprecation.rb. + # + # So, we define the constant first, and load dependencies later. + require "active_support/deprecation/instance_delegator" + require "active_support/deprecation/behaviors" + require "active_support/deprecation/reporting" + require "active_support/deprecation/disallowed" + require "active_support/deprecation/constant_accessor" + require "active_support/deprecation/method_wrappers" + require "active_support/deprecation/proxy_wrappers" + require "active_support/core_ext/module/deprecation" + require "concurrent/atomic/thread_local_var" + + include Singleton + include InstanceDelegator + include Behavior + include Reporting + include Disallowed + include MethodWrapper + + # The version number in which the deprecated behavior will be removed, by default. + attr_accessor :deprecation_horizon + + # It accepts two parameters on initialization. The first is a version of library + # and the second is a library name. + # + # ActiveSupport::Deprecation.new('2.0', 'MyLibrary') + def initialize(deprecation_horizon = "7.1", gem_name = "Rails") + self.gem_name = gem_name + self.deprecation_horizon = deprecation_horizon + # By default, warnings are not silenced and debugging is off. + self.silenced = false + self.debug = false + @silenced_thread = Concurrent::ThreadLocalVar.new(false) + @explicitly_allowed_warnings = Concurrent::ThreadLocalVar.new(nil) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation/behaviors.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation/behaviors.rb new file mode 100644 index 0000000..bb31fba --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation/behaviors.rb @@ -0,0 +1,125 @@ +# frozen_string_literal: true + +require "active_support/notifications" + +module ActiveSupport + # Raised when ActiveSupport::Deprecation::Behavior#behavior is set with :raise. + # You would set :raise, as a behavior to raise errors and proactively report exceptions from deprecations. + class DeprecationException < StandardError + end + + class Deprecation + # Default warning behaviors per Rails.env. + DEFAULT_BEHAVIORS = { + raise: ->(message, callstack, deprecation_horizon, gem_name) { + e = DeprecationException.new(message) + e.set_backtrace(callstack.map(&:to_s)) + raise e + }, + + stderr: ->(message, callstack, deprecation_horizon, gem_name) { + $stderr.puts(message) + $stderr.puts callstack.join("\n ") if debug + }, + + log: ->(message, callstack, deprecation_horizon, gem_name) { + logger = + if defined?(Rails.logger) && Rails.logger + Rails.logger + else + require "active_support/logger" + ActiveSupport::Logger.new($stderr) + end + logger.warn message + logger.debug callstack.join("\n ") if debug + }, + + notify: ->(message, callstack, deprecation_horizon, gem_name) { + notification_name = "deprecation.#{gem_name.underscore.tr('/', '_')}" + ActiveSupport::Notifications.instrument(notification_name, + message: message, + callstack: callstack, + gem_name: gem_name, + deprecation_horizon: deprecation_horizon) + }, + + silence: ->(message, callstack, deprecation_horizon, gem_name) { }, + } + + # Behavior module allows to determine how to display deprecation messages. + # You can create a custom behavior or set any from the +DEFAULT_BEHAVIORS+ + # constant. Available behaviors are: + # + # [+raise+] Raise ActiveSupport::DeprecationException. + # [+stderr+] Log all deprecation warnings to $stderr. + # [+log+] Log all deprecation warnings to +Rails.logger+. + # [+notify+] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+. + # [+silence+] Do nothing. On Rails, set config.active_support.report_deprecations = false to disable all behaviors. + # + # Setting behaviors only affects deprecations that happen after boot time. + # For more information you can read the documentation of the +behavior=+ method. + module Behavior + # Whether to print a backtrace along with the warning. + attr_accessor :debug + + # Returns the current behavior or if one isn't set, defaults to +:stderr+. + def behavior + @behavior ||= [DEFAULT_BEHAVIORS[:stderr]] + end + + # Returns the current behavior for disallowed deprecations or if one isn't set, defaults to +:raise+. + def disallowed_behavior + @disallowed_behavior ||= [DEFAULT_BEHAVIORS[:raise]] + end + + # Sets the behavior to the specified value. Can be a single value, array, + # or an object that responds to +call+. + # + # Available behaviors: + # + # [+raise+] Raise ActiveSupport::DeprecationException. + # [+stderr+] Log all deprecation warnings to $stderr. + # [+log+] Log all deprecation warnings to +Rails.logger+. + # [+notify+] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+. + # [+silence+] Do nothing. + # + # Setting behaviors only affects deprecations that happen after boot time. + # Deprecation warnings raised by gems are not affected by this setting + # because they happen before Rails boots up. + # + # ActiveSupport::Deprecation.behavior = :stderr + # ActiveSupport::Deprecation.behavior = [:stderr, :log] + # ActiveSupport::Deprecation.behavior = MyCustomHandler + # ActiveSupport::Deprecation.behavior = ->(message, callstack, deprecation_horizon, gem_name) { + # # custom stuff + # } + # + # If you are using Rails, you can set config.active_support.report_deprecations = false to disable + # all deprecation behaviors. This is similar to the +silence+ option but more performant. + def behavior=(behavior) + @behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || arity_coerce(b) } + end + + # Sets the behavior for disallowed deprecations (those configured by + # ActiveSupport::Deprecation.disallowed_warnings=) to the specified + # value. As with +behavior=+, this can be a single value, array, or an + # object that responds to +call+. + def disallowed_behavior=(behavior) + @disallowed_behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || arity_coerce(b) } + end + + private + def arity_coerce(behavior) + unless behavior.respond_to?(:call) + raise ArgumentError, "#{behavior.inspect} is not a valid deprecation behavior." + end + + if behavior.respond_to?(:arity) && behavior.arity == 2 + -> message, callstack, _, _ { behavior.call(message, callstack) } + else + behavior + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation/constant_accessor.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation/constant_accessor.rb new file mode 100644 index 0000000..1ed0015 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation/constant_accessor.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module ActiveSupport + class Deprecation + # DeprecatedConstantAccessor transforms a constant into a deprecated one by + # hooking +const_missing+. + # + # It takes the names of an old (deprecated) constant and of a new constant + # (both in string form) and optionally a deprecator. The deprecator defaults + # to +ActiveSupport::Deprecator+ if none is specified. + # + # The deprecated constant now returns the same object as the new one rather + # than a proxy object, so it can be used transparently in +rescue+ blocks + # etc. + # + # PLANETS = %w(mercury venus earth mars jupiter saturn uranus neptune pluto) + # + # # (In a later update, the original implementation of `PLANETS` has been removed.) + # + # PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune) + # include ActiveSupport::Deprecation::DeprecatedConstantAccessor + # deprecate_constant 'PLANETS', 'PLANETS_POST_2006' + # + # PLANETS.map { |planet| planet.capitalize } + # # => DEPRECATION WARNING: PLANETS is deprecated! Use PLANETS_POST_2006 instead. + # (Backtrace information…) + # ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"] + module DeprecatedConstantAccessor + def self.included(base) + require "active_support/inflector/methods" + + extension = Module.new do + def const_missing(missing_const_name) + if class_variable_defined?(:@@_deprecated_constants) + if (replacement = class_variable_get(:@@_deprecated_constants)[missing_const_name.to_s]) + replacement[:deprecator].warn(replacement[:message] || "#{name}::#{missing_const_name} is deprecated! Use #{replacement[:new]} instead.", caller_locations) + return ActiveSupport::Inflector.constantize(replacement[:new].to_s) + end + end + super + end + + def deprecate_constant(const_name, new_constant, message: nil, deprecator: ActiveSupport::Deprecation.instance) + class_variable_set(:@@_deprecated_constants, {}) unless class_variable_defined?(:@@_deprecated_constants) + class_variable_get(:@@_deprecated_constants)[const_name.to_s] = { new: new_constant, message: message, deprecator: deprecator } + end + end + base.singleton_class.prepend extension + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation/disallowed.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation/disallowed.rb new file mode 100644 index 0000000..096ecaa --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation/disallowed.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +module ActiveSupport + class Deprecation + module Disallowed + # Sets the criteria used to identify deprecation messages which should be + # disallowed. Can be an array containing strings, symbols, or regular + # expressions. (Symbols are treated as strings). These are compared against + # the text of the generated deprecation warning. + # + # Additionally the scalar symbol +:all+ may be used to treat all + # deprecations as disallowed. + # + # Deprecations matching a substring or regular expression will be handled + # using the configured +ActiveSupport::Deprecation.disallowed_behavior+ + # rather than +ActiveSupport::Deprecation.behavior+ + attr_writer :disallowed_warnings + + # Returns the configured criteria used to identify deprecation messages + # which should be treated as disallowed. + def disallowed_warnings + @disallowed_warnings ||= [] + end + + private + def deprecation_disallowed?(message) + disallowed = ActiveSupport::Deprecation.disallowed_warnings + return false if explicitly_allowed?(message) + return true if disallowed == :all + disallowed.any? do |rule| + case rule + when String, Symbol + message.include?(rule.to_s) + when Regexp + rule.match?(message) + end + end + end + + def explicitly_allowed?(message) + allowances = @explicitly_allowed_warnings.value + return false unless allowances + return true if allowances == :all + allowances = [allowances] unless allowances.kind_of?(Array) + allowances.any? do |rule| + case rule + when String, Symbol + message.include?(rule.to_s) + when Regexp + rule.match?(message) + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation/instance_delegator.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation/instance_delegator.rb new file mode 100644 index 0000000..59dd30a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation/instance_delegator.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require "active_support/core_ext/module/delegation" + +module ActiveSupport + class Deprecation + module InstanceDelegator # :nodoc: + def self.included(base) + base.extend(ClassMethods) + base.singleton_class.prepend(OverrideDelegators) + base.public_class_method :new + end + + module ClassMethods # :nodoc: + def include(included_module) + included_module.instance_methods.each { |m| method_added(m) } + super + end + + def method_added(method_name) + singleton_class.delegate(method_name, to: :instance) + end + end + + module OverrideDelegators # :nodoc: + def warn(message = nil, callstack = nil) + callstack ||= caller_locations(2) + super + end + + def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil) + caller_backtrace ||= caller_locations(2) + super + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation/method_wrappers.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation/method_wrappers.rb new file mode 100644 index 0000000..5437598 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation/method_wrappers.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +require "active_support/core_ext/array/extract_options" +require "active_support/core_ext/module/redefine_method" + +module ActiveSupport + class Deprecation + module MethodWrapper + # Declare that a method has been deprecated. + # + # class Fred + # def aaa; end + # def bbb; end + # def ccc; end + # def ddd; end + # def eee; end + # end + # + # Using the default deprecator: + # ActiveSupport::Deprecation.deprecate_methods(Fred, :aaa, bbb: :zzz, ccc: 'use Bar#ccc instead') + # # => Fred + # + # Fred.new.aaa + # # DEPRECATION WARNING: aaa is deprecated and will be removed from Rails 5.1. (called from irb_binding at (irb):10) + # # => nil + # + # Fred.new.bbb + # # DEPRECATION WARNING: bbb is deprecated and will be removed from Rails 5.1 (use zzz instead). (called from irb_binding at (irb):11) + # # => nil + # + # Fred.new.ccc + # # DEPRECATION WARNING: ccc is deprecated and will be removed from Rails 5.1 (use Bar#ccc instead). (called from irb_binding at (irb):12) + # # => nil + # + # Passing in a custom deprecator: + # custom_deprecator = ActiveSupport::Deprecation.new('next-release', 'MyGem') + # ActiveSupport::Deprecation.deprecate_methods(Fred, ddd: :zzz, deprecator: custom_deprecator) + # # => [:ddd] + # + # Fred.new.ddd + # DEPRECATION WARNING: ddd is deprecated and will be removed from MyGem next-release (use zzz instead). (called from irb_binding at (irb):15) + # # => nil + # + # Using a custom deprecator directly: + # custom_deprecator = ActiveSupport::Deprecation.new('next-release', 'MyGem') + # custom_deprecator.deprecate_methods(Fred, eee: :zzz) + # # => [:eee] + # + # Fred.new.eee + # DEPRECATION WARNING: eee is deprecated and will be removed from MyGem next-release (use zzz instead). (called from irb_binding at (irb):18) + # # => nil + def deprecate_methods(target_module, *method_names) + options = method_names.extract_options! + deprecator = options.delete(:deprecator) || self + method_names += options.keys + mod = nil + + method_names.each do |method_name| + message = options[method_name] + if target_module.method_defined?(method_name) || target_module.private_method_defined?(method_name) + method = target_module.instance_method(method_name) + target_module.module_eval do + redefine_method(method_name) do |*args, &block| + deprecator.deprecation_warning(method_name, message) + method.bind_call(self, *args, &block) + end + ruby2_keywords(method_name) + end + else + mod ||= Module.new + mod.module_eval do + define_method(method_name) do |*args, &block| + deprecator.deprecation_warning(method_name, message) + super(*args, &block) + end + ruby2_keywords(method_name) + end + end + end + + target_module.prepend(mod) if mod + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation/proxy_wrappers.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation/proxy_wrappers.rb new file mode 100644 index 0000000..1584f71 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation/proxy_wrappers.rb @@ -0,0 +1,177 @@ +# frozen_string_literal: true + +module ActiveSupport + class Deprecation + class DeprecationProxy # :nodoc: + def self.new(*args, &block) + object = args.first + + return object unless object + super + end + + instance_methods.each { |m| undef_method m unless /^__|^object_id$/.match?(m) } + + # Don't give a deprecation warning on inspect since test/unit and error + # logs rely on it for diagnostics. + def inspect + target.inspect + end + + private + def method_missing(called, *args, &block) + warn caller_locations, called, args + target.__send__(called, *args, &block) + end + end + + # DeprecatedObjectProxy transforms an object into a deprecated one. It + # takes an object, a deprecation message, and optionally a deprecator. The + # deprecator defaults to +ActiveSupport::Deprecator+ if none is specified. + # + # deprecated_object = ActiveSupport::Deprecation::DeprecatedObjectProxy.new(Object.new, "This object is now deprecated") + # # => # + # + # deprecated_object.to_s + # DEPRECATION WARNING: This object is now deprecated. + # (Backtrace) + # # => "#" + class DeprecatedObjectProxy < DeprecationProxy + def initialize(object, message, deprecator = ActiveSupport::Deprecation.instance) + @object = object + @message = message + @deprecator = deprecator + end + + private + def target + @object + end + + def warn(callstack, called, args) + @deprecator.warn(@message, callstack) + end + end + + # DeprecatedInstanceVariableProxy transforms an instance variable into a + # deprecated one. It takes an instance of a class, a method on that class + # and an instance variable. It optionally takes a deprecator as the last + # argument. The deprecator defaults to +ActiveSupport::Deprecator+ if none + # is specified. + # + # class Example + # def initialize + # @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request, :@request) + # @_request = :special_request + # end + # + # def request + # @_request + # end + # + # def old_request + # @request + # end + # end + # + # example = Example.new + # # => # + # + # example.old_request.to_s + # # => DEPRECATION WARNING: @request is deprecated! Call request.to_s instead of + # @request.to_s + # (Backtrace information…) + # "special_request" + # + # example.request.to_s + # # => "special_request" + class DeprecatedInstanceVariableProxy < DeprecationProxy + def initialize(instance, method, var = "@#{method}", deprecator = ActiveSupport::Deprecation.instance) + @instance = instance + @method = method + @var = var + @deprecator = deprecator + end + + private + def target + @instance.__send__(@method) + end + + def warn(callstack, called, args) + @deprecator.warn("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}", callstack) + end + end + + # DeprecatedConstantProxy transforms a constant into a deprecated one. It + # takes the names of an old (deprecated) constant and of a new constant + # (both in string form) and optionally a deprecator. The deprecator defaults + # to +ActiveSupport::Deprecator+ if none is specified. The deprecated constant + # now returns the value of the new one. + # + # PLANETS = %w(mercury venus earth mars jupiter saturn uranus neptune pluto) + # + # # (In a later update, the original implementation of `PLANETS` has been removed.) + # + # PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune) + # PLANETS = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('PLANETS', 'PLANETS_POST_2006') + # + # PLANETS.map { |planet| planet.capitalize } + # # => DEPRECATION WARNING: PLANETS is deprecated! Use PLANETS_POST_2006 instead. + # (Backtrace information…) + # ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"] + class DeprecatedConstantProxy < Module + def self.new(*args, **options, &block) + object = args.first + + return object unless object + super + end + + def initialize(old_const, new_const, deprecator = ActiveSupport::Deprecation.instance, message: "#{old_const} is deprecated! Use #{new_const} instead.") + Kernel.require "active_support/inflector/methods" + + @old_const = old_const + @new_const = new_const + @deprecator = deprecator + @message = message + end + + instance_methods.each { |m| undef_method m unless /^__|^object_id$/.match?(m) } + + # Don't give a deprecation warning on inspect since test/unit and error + # logs rely on it for diagnostics. + def inspect + target.inspect + end + + # Don't give a deprecation warning on methods that IRB may invoke + # during tab-completion. + delegate :hash, :instance_methods, :name, :respond_to?, to: :target + + # Returns the class of the new constant. + # + # PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune) + # PLANETS = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('PLANETS', 'PLANETS_POST_2006') + # PLANETS.class # => Array + def class + target.class + end + + private + def target + ActiveSupport::Inflector.constantize(@new_const.to_s) + end + + def const_missing(name) + @deprecator.warn(@message, caller_locations) + target.const_get(name) + end + + def method_missing(called, *args, &block) + @deprecator.warn(@message, caller_locations) + target.__send__(called, *args, &block) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation/reporting.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation/reporting.rb new file mode 100644 index 0000000..51514eb --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/deprecation/reporting.rb @@ -0,0 +1,157 @@ +# frozen_string_literal: true + +require "rbconfig" + +module ActiveSupport + class Deprecation + module Reporting + # Whether to print a message (silent mode) + attr_writer :silenced + # Name of gem where method is deprecated + attr_accessor :gem_name + + # Outputs a deprecation warning to the output configured by + # ActiveSupport::Deprecation.behavior. + # + # ActiveSupport::Deprecation.warn('something broke!') + # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)" + def warn(message = nil, callstack = nil) + return if silenced + + callstack ||= caller_locations(2) + deprecation_message(callstack, message).tap do |m| + if deprecation_disallowed?(message) + disallowed_behavior.each { |b| b.call(m, callstack, deprecation_horizon, gem_name) } + else + behavior.each { |b| b.call(m, callstack, deprecation_horizon, gem_name) } + end + end + end + + # Silence deprecation warnings within the block. + # + # ActiveSupport::Deprecation.warn('something broke!') + # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)" + # + # ActiveSupport::Deprecation.silence do + # ActiveSupport::Deprecation.warn('something broke!') + # end + # # => nil + def silence(&block) + @silenced_thread.bind(true, &block) + end + + # Allow previously disallowed deprecation warnings within the block. + # allowed_warnings can be an array containing strings, symbols, or regular + # expressions. (Symbols are treated as strings). These are compared against + # the text of deprecation warning messages generated within the block. + # Matching warnings will be exempt from the rules set by + # +ActiveSupport::Deprecation.disallowed_warnings+ + # + # The optional if: argument accepts a truthy/falsy value or an object that + # responds to .call. If truthy, then matching warnings will be allowed. + # If falsey then the method yields to the block without allowing the warning. + # + # ActiveSupport::Deprecation.disallowed_behavior = :raise + # ActiveSupport::Deprecation.disallowed_warnings = [ + # "something broke" + # ] + # + # ActiveSupport::Deprecation.warn('something broke!') + # # => ActiveSupport::DeprecationException + # + # ActiveSupport::Deprecation.allow ['something broke'] do + # ActiveSupport::Deprecation.warn('something broke!') + # end + # # => nil + # + # ActiveSupport::Deprecation.allow ['something broke'], if: Rails.env.production? do + # ActiveSupport::Deprecation.warn('something broke!') + # end + # # => ActiveSupport::DeprecationException for dev/test, nil for production + def allow(allowed_warnings = :all, if: true, &block) + conditional = binding.local_variable_get(:if) + conditional = conditional.call if conditional.respond_to?(:call) + if conditional + @explicitly_allowed_warnings.bind(allowed_warnings, &block) + else + yield + end + end + + def silenced + @silenced || @silenced_thread.value + end + + def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil) + caller_backtrace ||= caller_locations(2) + deprecated_method_warning(deprecated_method_name, message).tap do |msg| + warn(msg, caller_backtrace) + end + end + + private + # Outputs a deprecation warning message + # + # deprecated_method_warning(:method_name) + # # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon}" + # deprecated_method_warning(:method_name, :another_method) + # # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (use another_method instead)" + # deprecated_method_warning(:method_name, "Optional message") + # # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (Optional message)" + def deprecated_method_warning(method_name, message = nil) + warning = "#{method_name} is deprecated and will be removed from #{gem_name} #{deprecation_horizon}" + case message + when Symbol then "#{warning} (use #{message} instead)" + when String then "#{warning} (#{message})" + else warning + end + end + + def deprecation_message(callstack, message = nil) + message ||= "You are using deprecated behavior which will be removed from the next major or minor release." + "DEPRECATION WARNING: #{message} #{deprecation_caller_message(callstack)}" + end + + def deprecation_caller_message(callstack) + file, line, method = extract_callstack(callstack) + if file + if line && method + "(called from #{method} at #{file}:#{line})" + else + "(called from #{file}:#{line})" + end + end + end + + def extract_callstack(callstack) + return _extract_callstack(callstack) if callstack.first.is_a? String + + offending_line = callstack.find { |frame| + frame.absolute_path && !ignored_callstack(frame.absolute_path) + } || callstack.first + + [offending_line.path, offending_line.lineno, offending_line.label] + end + + def _extract_callstack(callstack) + warn "Please pass `caller_locations` to the deprecation API" if $VERBOSE + offending_line = callstack.find { |line| !ignored_callstack(line) } || callstack.first + + if offending_line + if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/) + md.captures + else + offending_line + end + end + end + + RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/" + + def ignored_callstack(path) + path.start_with?(RAILS_GEM_ROOT) || path.start_with?(RbConfig::CONFIG["rubylibdir"]) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/descendants_tracker.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/descendants_tracker.rb new file mode 100644 index 0000000..ea3ed1f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/descendants_tracker.rb @@ -0,0 +1,218 @@ +# frozen_string_literal: true + +require "weakref" +require "active_support/ruby_features" + +module ActiveSupport + # This module provides an internal implementation to track descendants + # which is faster than iterating through ObjectSpace. + module DescendantsTracker + class << self + def direct_descendants(klass) + ActiveSupport::Deprecation.warn(<<~MSG) + ActiveSupport::DescendantsTracker.direct_descendants is deprecated and will be removed in Rails 7.1. + Use ActiveSupport::DescendantsTracker.subclasses instead. + MSG + subclasses(klass) + end + end + + @clear_disabled = false + + if RubyFeatures::CLASS_SUBCLASSES + @@excluded_descendants = if RUBY_ENGINE == "ruby" + # On MRI `ObjectSpace::WeakMap` keys are weak references. + # So we can simply use WeakMap as a `Set`. + ObjectSpace::WeakMap.new + else + # On TruffleRuby `ObjectSpace::WeakMap` keys are strong references. + # So we use `object_id` as a key and the actual object as a value. + # + # JRuby for now doesn't have Class#descendant, but when it will, it will likely + # have the same WeakMap semantic than Truffle so we future proof this as much as possible. + class WeakSet # :nodoc: + def initialize + @map = ObjectSpace::WeakMap.new + end + + def [](object) + @map.key?(object.object_id) + end + + def []=(object, _present) + @map[object.object_id] = object + end + end + WeakSet.new + end + + class << self + def disable_clear! # :nodoc: + unless @clear_disabled + @clear_disabled = true + remove_method(:subclasses) + @@excluded_descendants = nil + end + end + + def subclasses(klass) + klass.subclasses + end + + def descendants(klass) + klass.descendants + end + + def clear(classes) # :nodoc: + raise "DescendantsTracker.clear was disabled because config.cache_classes = true" if @clear_disabled + + classes.each do |klass| + @@excluded_descendants[klass] = true + klass.descendants.each do |descendant| + @@excluded_descendants[descendant] = true + end + end + end + + def native? # :nodoc: + true + end + end + + def subclasses + subclasses = super + subclasses.reject! { |d| @@excluded_descendants[d] } + subclasses + end + + def descendants + subclasses.concat(subclasses.flat_map(&:descendants)) + end + + def direct_descendants + ActiveSupport::Deprecation.warn(<<~MSG) + ActiveSupport::DescendantsTracker#direct_descendants is deprecated and will be removed in Rails 7.1. + Use #subclasses instead. + MSG + subclasses + end + else + @@direct_descendants = {} + + class << self + def disable_clear! # :nodoc: + @clear_disabled = true + end + + def subclasses(klass) + descendants = @@direct_descendants[klass] + descendants ? descendants.to_a : [] + end + + def descendants(klass) + arr = [] + accumulate_descendants(klass, arr) + arr + end + + def clear(classes) # :nodoc: + raise "DescendantsTracker.clear was disabled because config.cache_classes = true" if @clear_disabled + + @@direct_descendants.each do |klass, direct_descendants_of_klass| + if classes.member?(klass) + @@direct_descendants.delete(klass) + else + direct_descendants_of_klass.reject! do |direct_descendant_of_class| + classes.member?(direct_descendant_of_class) + end + end + end + end + + def native? # :nodoc: + false + end + + # This is the only method that is not thread safe, but is only ever called + # during the eager loading phase. + def store_inherited(klass, descendant) + (@@direct_descendants[klass] ||= DescendantsArray.new) << descendant + end + + private + def accumulate_descendants(klass, acc) + if direct_descendants = @@direct_descendants[klass] + direct_descendants.each do |direct_descendant| + acc << direct_descendant + accumulate_descendants(direct_descendant, acc) + end + end + end + end + + def inherited(base) + DescendantsTracker.store_inherited(self, base) + super + end + + def direct_descendants + ActiveSupport::Deprecation.warn(<<~MSG) + ActiveSupport::DescendantsTracker#direct_descendants is deprecated and will be removed in Rails 7.1. + Use #subclasses instead. + MSG + DescendantsTracker.subclasses(self) + end + + def subclasses + DescendantsTracker.subclasses(self) + end + + def descendants + DescendantsTracker.descendants(self) + end + + # DescendantsArray is an array that contains weak references to classes. + class DescendantsArray # :nodoc: + include Enumerable + + def initialize + @refs = [] + end + + def initialize_copy(orig) + @refs = @refs.dup + end + + def <<(klass) + @refs << WeakRef.new(klass) + end + + def each + @refs.reject! do |ref| + yield ref.__getobj__ + false + rescue WeakRef::RefError + true + end + self + end + + def refs_size + @refs.size + end + + def cleanup! + @refs.delete_if { |ref| !ref.weakref_alive? } + end + + def reject! + @refs.reject! do |ref| + yield ref.__getobj__ + rescue WeakRef::RefError + true + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/digest.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/digest.rb new file mode 100644 index 0000000..a3d27be --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/digest.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require "openssl" + +module ActiveSupport + class Digest # :nodoc: + class << self + def hash_digest_class + @hash_digest_class ||= OpenSSL::Digest::MD5 + end + + def hash_digest_class=(klass) + raise ArgumentError, "#{klass} is expected to implement hexdigest class method" unless klass.respond_to?(:hexdigest) + @hash_digest_class = klass + end + + def hexdigest(arg) + hash_digest_class.hexdigest(arg)[0...32] + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/duration.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/duration.rb new file mode 100644 index 0000000..19986d1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/duration.rb @@ -0,0 +1,514 @@ +# frozen_string_literal: true + +require "active_support/core_ext/array/conversions" +require "active_support/core_ext/module/delegation" +require "active_support/core_ext/object/acts_like" +require "active_support/core_ext/string/filters" + +module ActiveSupport + # Provides accurate date and time measurements using Date#advance and + # Time#advance, respectively. It mainly supports the methods on Numeric. + # + # 1.month.ago # equivalent to Time.now.advance(months: -1) + class Duration + class Scalar < Numeric # :nodoc: + attr_reader :value + delegate :to_i, :to_f, :to_s, to: :value + + def initialize(value) + @value = value + end + + def coerce(other) + [Scalar.new(other), self] + end + + def -@ + Scalar.new(-value) + end + + def <=>(other) + if Scalar === other || Duration === other + value <=> other.value + elsif Numeric === other + value <=> other + else + nil + end + end + + def +(other) + if Duration === other + seconds = value + other._parts.fetch(:seconds, 0) + new_parts = other._parts.merge(seconds: seconds) + new_value = value + other.value + + Duration.new(new_value, new_parts, other.variable?) + else + calculate(:+, other) + end + end + + def -(other) + if Duration === other + seconds = value - other._parts.fetch(:seconds, 0) + new_parts = other._parts.transform_values(&:-@) + new_parts = new_parts.merge(seconds: seconds) + new_value = value - other.value + + Duration.new(new_value, new_parts, other.variable?) + else + calculate(:-, other) + end + end + + def *(other) + if Duration === other + new_parts = other._parts.transform_values { |other_value| value * other_value } + new_value = value * other.value + + Duration.new(new_value, new_parts, other.variable?) + else + calculate(:*, other) + end + end + + def /(other) + if Duration === other + value / other.value + else + calculate(:/, other) + end + end + + def %(other) + if Duration === other + Duration.build(value % other.value) + else + calculate(:%, other) + end + end + + def variable? # :nodoc: + false + end + + private + def calculate(op, other) + if Scalar === other + Scalar.new(value.public_send(op, other.value)) + elsif Numeric === other + Scalar.new(value.public_send(op, other)) + else + raise_type_error(other) + end + end + + def raise_type_error(other) + raise TypeError, "no implicit conversion of #{other.class} into #{self.class}" + end + end + + SECONDS_PER_MINUTE = 60 + SECONDS_PER_HOUR = 3600 + SECONDS_PER_DAY = 86400 + SECONDS_PER_WEEK = 604800 + SECONDS_PER_MONTH = 2629746 # 1/12 of a gregorian year + SECONDS_PER_YEAR = 31556952 # length of a gregorian year (365.2425 days) + + PARTS_IN_SECONDS = { + seconds: 1, + minutes: SECONDS_PER_MINUTE, + hours: SECONDS_PER_HOUR, + days: SECONDS_PER_DAY, + weeks: SECONDS_PER_WEEK, + months: SECONDS_PER_MONTH, + years: SECONDS_PER_YEAR + }.freeze + + PARTS = [:years, :months, :weeks, :days, :hours, :minutes, :seconds].freeze + VARIABLE_PARTS = [:years, :months, :weeks, :days].freeze + + attr_reader :value + + autoload :ISO8601Parser, "active_support/duration/iso8601_parser" + autoload :ISO8601Serializer, "active_support/duration/iso8601_serializer" + + class << self + # Creates a new Duration from string formatted according to ISO 8601 Duration. + # + # See {ISO 8601}[https://en.wikipedia.org/wiki/ISO_8601#Durations] for more information. + # This method allows negative parts to be present in pattern. + # If invalid string is provided, it will raise +ActiveSupport::Duration::ISO8601Parser::ParsingError+. + def parse(iso8601duration) + parts = ISO8601Parser.new(iso8601duration).parse! + new(calculate_total_seconds(parts), parts) + end + + def ===(other) # :nodoc: + other.is_a?(Duration) + rescue ::NoMethodError + false + end + + def seconds(value) # :nodoc: + new(value, { seconds: value }, false) + end + + def minutes(value) # :nodoc: + new(value * SECONDS_PER_MINUTE, { minutes: value }, false) + end + + def hours(value) # :nodoc: + new(value * SECONDS_PER_HOUR, { hours: value }, false) + end + + def days(value) # :nodoc: + new(value * SECONDS_PER_DAY, { days: value }, true) + end + + def weeks(value) # :nodoc: + new(value * SECONDS_PER_WEEK, { weeks: value }, true) + end + + def months(value) # :nodoc: + new(value * SECONDS_PER_MONTH, { months: value }, true) + end + + def years(value) # :nodoc: + new(value * SECONDS_PER_YEAR, { years: value }, true) + end + + # Creates a new Duration from a seconds value that is converted + # to the individual parts: + # + # ActiveSupport::Duration.build(31556952).parts # => {:years=>1} + # ActiveSupport::Duration.build(2716146).parts # => {:months=>1, :days=>1} + # + def build(value) + unless value.is_a?(::Numeric) + raise TypeError, "can't build an #{self.name} from a #{value.class.name}" + end + + parts = {} + remainder_sign = value <=> 0 + remainder = value.round(9).abs + variable = false + + PARTS.each do |part| + unless part == :seconds + part_in_seconds = PARTS_IN_SECONDS[part] + parts[part] = remainder.div(part_in_seconds) * remainder_sign + remainder %= part_in_seconds + + unless parts[part].zero? + variable ||= VARIABLE_PARTS.include?(part) + end + end + end unless value == 0 + + parts[:seconds] = remainder * remainder_sign + + new(value, parts, variable) + end + + private + def calculate_total_seconds(parts) + parts.inject(0) do |total, (part, value)| + total + value * PARTS_IN_SECONDS[part] + end + end + end + + def initialize(value, parts, variable = nil) # :nodoc: + @value, @parts = value, parts + @parts.reject! { |k, v| v.zero? } unless value == 0 + @parts.freeze + @variable = variable + + if @variable.nil? + @variable = @parts.any? { |part, _| VARIABLE_PARTS.include?(part) } + end + end + + # Returns a copy of the parts hash that defines the duration + def parts + @parts.dup + end + + def coerce(other) # :nodoc: + case other + when Scalar + [other, self] + when Duration + [Scalar.new(other.value), self] + else + [Scalar.new(other), self] + end + end + + # Compares one Duration with another or a Numeric to this Duration. + # Numeric values are treated as seconds. + def <=>(other) + if Duration === other + value <=> other.value + elsif Numeric === other + value <=> other + end + end + + # Adds another Duration or a Numeric to this Duration. Numeric values + # are treated as seconds. + def +(other) + if Duration === other + parts = @parts.merge(other._parts) do |_key, value, other_value| + value + other_value + end + Duration.new(value + other.value, parts, @variable || other.variable?) + else + seconds = @parts.fetch(:seconds, 0) + other + Duration.new(value + other, @parts.merge(seconds: seconds), @variable) + end + end + + # Subtracts another Duration or a Numeric from this Duration. Numeric + # values are treated as seconds. + def -(other) + self + (-other) + end + + # Multiplies this Duration by a Numeric and returns a new Duration. + def *(other) + if Scalar === other || Duration === other + Duration.new(value * other.value, @parts.transform_values { |number| number * other.value }, @variable || other.variable?) + elsif Numeric === other + Duration.new(value * other, @parts.transform_values { |number| number * other }, @variable) + else + raise_type_error(other) + end + end + + # Divides this Duration by a Numeric and returns a new Duration. + def /(other) + if Scalar === other + Duration.new(value / other.value, @parts.transform_values { |number| number / other.value }, @variable) + elsif Duration === other + value / other.value + elsif Numeric === other + Duration.new(value / other, @parts.transform_values { |number| number / other }, @variable) + else + raise_type_error(other) + end + end + + # Returns the modulo of this Duration by another Duration or Numeric. + # Numeric values are treated as seconds. + def %(other) + if Duration === other || Scalar === other + Duration.build(value % other.value) + elsif Numeric === other + Duration.build(value % other) + else + raise_type_error(other) + end + end + + def -@ # :nodoc: + Duration.new(-value, @parts.transform_values(&:-@), @variable) + end + + def +@ # :nodoc: + self + end + + def is_a?(klass) # :nodoc: + Duration == klass || value.is_a?(klass) + end + alias :kind_of? :is_a? + + def instance_of?(klass) # :nodoc: + Duration == klass || value.instance_of?(klass) + end + + # Returns +true+ if +other+ is also a Duration instance with the + # same +value+, or if other == value. + def ==(other) + if Duration === other + other.value == value + else + other == value + end + end + + # Returns the amount of seconds a duration covers as a string. + # For more information check to_i method. + # + # 1.day.to_s # => "86400" + def to_s + @value.to_s + end + + # Returns the number of seconds that this Duration represents. + # + # 1.minute.to_i # => 60 + # 1.hour.to_i # => 3600 + # 1.day.to_i # => 86400 + # + # Note that this conversion makes some assumptions about the + # duration of some periods, e.g. months are always 1/12 of year + # and years are 365.2425 days: + # + # # equivalent to (1.year / 12).to_i + # 1.month.to_i # => 2629746 + # + # # equivalent to 365.2425.days.to_i + # 1.year.to_i # => 31556952 + # + # In such cases, Ruby's core + # Date[https://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and + # Time[https://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision + # date and time arithmetic. + def to_i + @value.to_i + end + alias :in_seconds :to_i + + # Returns the amount of minutes a duration covers as a float + # + # 1.day.in_minutes # => 1440.0 + def in_minutes + in_seconds / SECONDS_PER_MINUTE.to_f + end + + # Returns the amount of hours a duration covers as a float + # + # 1.day.in_hours # => 24.0 + def in_hours + in_seconds / SECONDS_PER_HOUR.to_f + end + + # Returns the amount of days a duration covers as a float + # + # 12.hours.in_days # => 0.5 + def in_days + in_seconds / SECONDS_PER_DAY.to_f + end + + # Returns the amount of weeks a duration covers as a float + # + # 2.months.in_weeks # => 8.696 + def in_weeks + in_seconds / SECONDS_PER_WEEK.to_f + end + + # Returns the amount of months a duration covers as a float + # + # 9.weeks.in_months # => 2.07 + def in_months + in_seconds / SECONDS_PER_MONTH.to_f + end + + # Returns the amount of years a duration covers as a float + # + # 30.days.in_years # => 0.082 + def in_years + in_seconds / SECONDS_PER_YEAR.to_f + end + + # Returns +true+ if +other+ is also a Duration instance, which has the + # same parts as this one. + def eql?(other) + Duration === other && other.value.eql?(value) + end + + def hash + @value.hash + end + + # Calculates a new Time or Date that is as far in the future + # as this Duration represents. + def since(time = ::Time.current) + sum(1, time) + end + alias :from_now :since + alias :after :since + + # Calculates a new Time or Date that is as far in the past + # as this Duration represents. + def ago(time = ::Time.current) + sum(-1, time) + end + alias :until :ago + alias :before :ago + + def inspect # :nodoc: + return "#{value} seconds" if @parts.empty? + + @parts. + sort_by { |unit, _ | PARTS.index(unit) }. + map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }. + to_sentence(locale: false) + end + + def as_json(options = nil) # :nodoc: + to_i + end + + def init_with(coder) # :nodoc: + initialize(coder["value"], coder["parts"]) + end + + def encode_with(coder) # :nodoc: + coder.map = { "value" => @value, "parts" => @parts } + end + + # Build ISO 8601 Duration string for this duration. + # The +precision+ parameter can be used to limit seconds' precision of duration. + def iso8601(precision: nil) + ISO8601Serializer.new(self, precision: precision).serialize + end + + def variable? # :nodoc: + @variable + end + + def _parts # :nodoc: + @parts + end + + private + def sum(sign, time = ::Time.current) + unless time.acts_like?(:time) || time.acts_like?(:date) + raise ::ArgumentError, "expected a time or date, got #{time.inspect}" + end + + if @parts.empty? + time.since(sign * value) + else + @parts.inject(time) do |t, (type, number)| + if type == :seconds + t.since(sign * number) + elsif type == :minutes + t.since(sign * number * 60) + elsif type == :hours + t.since(sign * number * 3600) + else + t.advance(type => sign * number) + end + end + end + end + + def respond_to_missing?(method, _) + value.respond_to?(method) + end + + def method_missing(method, *args, &block) + value.public_send(method, *args, &block) + end + + def raise_type_error(other) + raise TypeError, "no implicit conversion of #{other.class} into #{self.class}" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/duration/iso8601_parser.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/duration/iso8601_parser.rb new file mode 100644 index 0000000..839caab --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/duration/iso8601_parser.rb @@ -0,0 +1,123 @@ +# frozen_string_literal: true + +require "strscan" + +module ActiveSupport + class Duration + # Parses a string formatted according to ISO 8601 Duration into the hash. + # + # See {ISO 8601}[https://en.wikipedia.org/wiki/ISO_8601#Durations] for more information. + # + # This parser allows negative parts to be present in pattern. + class ISO8601Parser # :nodoc: + class ParsingError < ::ArgumentError; end + + PERIOD_OR_COMMA = /\.|,/ + PERIOD = "." + COMMA = "," + + SIGN_MARKER = /\A-|\+|/ + DATE_MARKER = /P/ + TIME_MARKER = /T/ + DATE_COMPONENT = /(-?\d+(?:[.,]\d+)?)(Y|M|D|W)/ + TIME_COMPONENT = /(-?\d+(?:[.,]\d+)?)(H|M|S)/ + + DATE_TO_PART = { "Y" => :years, "M" => :months, "W" => :weeks, "D" => :days } + TIME_TO_PART = { "H" => :hours, "M" => :minutes, "S" => :seconds } + + DATE_COMPONENTS = [:years, :months, :days] + TIME_COMPONENTS = [:hours, :minutes, :seconds] + + attr_reader :parts, :scanner + attr_accessor :mode, :sign + + def initialize(string) + @scanner = StringScanner.new(string) + @parts = {} + @mode = :start + @sign = 1 + end + + def parse! + while !finished? + case mode + when :start + if scan(SIGN_MARKER) + self.sign = (scanner.matched == "-") ? -1 : 1 + self.mode = :sign + else + raise_parsing_error + end + + when :sign + if scan(DATE_MARKER) + self.mode = :date + else + raise_parsing_error + end + + when :date + if scan(TIME_MARKER) + self.mode = :time + elsif scan(DATE_COMPONENT) + parts[DATE_TO_PART[scanner[2]]] = number * sign + else + raise_parsing_error + end + + when :time + if scan(TIME_COMPONENT) + parts[TIME_TO_PART[scanner[2]]] = number * sign + else + raise_parsing_error + end + + end + end + + validate! + parts + end + + private + def finished? + scanner.eos? + end + + # Parses number which can be a float with either comma or period. + def number + PERIOD_OR_COMMA.match?(scanner[1]) ? scanner[1].tr(COMMA, PERIOD).to_f : scanner[1].to_i + end + + def scan(pattern) + scanner.scan(pattern) + end + + def raise_parsing_error(reason = nil) + raise ParsingError, "Invalid ISO 8601 duration: #{scanner.string.inspect} #{reason}".strip + end + + # Checks for various semantic errors as stated in ISO 8601 standard. + def validate! + raise_parsing_error("is empty duration") if parts.empty? + + # Mixing any of Y, M, D with W is invalid. + if parts.key?(:weeks) && (parts.keys & DATE_COMPONENTS).any? + raise_parsing_error("mixing weeks with other date parts not allowed") + end + + # Specifying an empty T part is invalid. + if mode == :time && (parts.keys & TIME_COMPONENTS).empty? + raise_parsing_error("time part marker is present but time part is empty") + end + + fractions = parts.values.reject(&:zero?).select { |a| (a % 1) != 0 } + unless fractions.empty? || (fractions.size == 1 && fractions.last == @parts.values.reject(&:zero?).last) + raise_parsing_error "(only last part can be fractional)" + end + + true + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/duration/iso8601_serializer.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/duration/iso8601_serializer.rb new file mode 100644 index 0000000..9353c64 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/duration/iso8601_serializer.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +require "active_support/core_ext/object/blank" + +module ActiveSupport + class Duration + # Serializes duration to string according to ISO 8601 Duration format. + class ISO8601Serializer # :nodoc: + DATE_COMPONENTS = %i(years months days) + + def initialize(duration, precision: nil) + @duration = duration + @precision = precision + end + + # Builds and returns output string. + def serialize + parts = normalize + return "PT0S" if parts.empty? + + output = +"P" + output << "#{parts[:years]}Y" if parts.key?(:years) + output << "#{parts[:months]}M" if parts.key?(:months) + output << "#{parts[:days]}D" if parts.key?(:days) + output << "#{parts[:weeks]}W" if parts.key?(:weeks) + time = +"" + time << "#{parts[:hours]}H" if parts.key?(:hours) + time << "#{parts[:minutes]}M" if parts.key?(:minutes) + if parts.key?(:seconds) + time << "#{format_seconds(parts[:seconds])}S" + end + output << "T#{time}" unless time.empty? + output + end + + private + # Return pair of duration's parts and whole duration sign. + # Parts are summarized (as they can become repetitive due to addition, etc). + # Zero parts are removed as not significant. + # If all parts are negative it will negate all of them and return minus as a sign. + def normalize + parts = @duration.parts.each_with_object(Hash.new(0)) do |(k, v), p| + p[k] += v unless v.zero? + end + + # Convert weeks to days and remove weeks if mixed with date parts + if week_mixed_with_date?(parts) + parts[:days] += parts.delete(:weeks) * SECONDS_PER_WEEK / SECONDS_PER_DAY + end + + parts + end + + def week_mixed_with_date?(parts) + parts.key?(:weeks) && (parts.keys & DATE_COMPONENTS).any? + end + + def format_seconds(seconds) + if @precision + sprintf("%0.0#{@precision}f", seconds) + else + seconds.to_s + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/encrypted_configuration.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/encrypted_configuration.rb new file mode 100644 index 0000000..3382fe7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/encrypted_configuration.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require "yaml" +require "active_support/encrypted_file" +require "active_support/ordered_options" +require "active_support/core_ext/object/inclusion" +require "active_support/core_ext/module/delegation" + +module ActiveSupport + class EncryptedConfiguration < EncryptedFile + delegate :[], :fetch, to: :config + delegate_missing_to :options + + def initialize(config_path:, key_path:, env_key:, raise_if_missing_key:) + super content_path: config_path, key_path: key_path, + env_key: env_key, raise_if_missing_key: raise_if_missing_key + end + + # Allow a config to be started without a file present + def read + super + rescue ActiveSupport::EncryptedFile::MissingContentError + "" + end + + def write(contents) + deserialize(contents) + + super + end + + def config + @config ||= deserialize(read).deep_symbolize_keys + end + + private + def deep_transform(hash) + return hash unless hash.is_a?(Hash) + + h = ActiveSupport::InheritableOptions.new + hash.each do |k, v| + h[k] = deep_transform(v) + end + h + end + + def options + @options ||= ActiveSupport::InheritableOptions.new(deep_transform(config)) + end + + def deserialize(config) + doc = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(config) : YAML.load(config) + doc.presence || {} + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/encrypted_file.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/encrypted_file.rb new file mode 100644 index 0000000..d2c9e62 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/encrypted_file.rb @@ -0,0 +1,129 @@ +# frozen_string_literal: true + +require "pathname" +require "tmpdir" +require "active_support/message_encryptor" + +module ActiveSupport + class EncryptedFile + class MissingContentError < RuntimeError + def initialize(content_path) + super "Missing encrypted content file in #{content_path}." + end + end + + class MissingKeyError < RuntimeError + def initialize(key_path:, env_key:) + super \ + "Missing encryption key to decrypt file with. " + + "Ask your team for your master key and write it to #{key_path} or put it in the ENV['#{env_key}']." + end + end + + class InvalidKeyLengthError < RuntimeError + def initialize + super "Encryption key must be exactly #{EncryptedFile.expected_key_length} characters." + end + end + + CIPHER = "aes-128-gcm" + + def self.generate_key + SecureRandom.hex(ActiveSupport::MessageEncryptor.key_len(CIPHER)) + end + + def self.expected_key_length # :nodoc: + @expected_key_length ||= generate_key.length + end + + + attr_reader :content_path, :key_path, :env_key, :raise_if_missing_key + + def initialize(content_path:, key_path:, env_key:, raise_if_missing_key:) + @content_path = Pathname.new(content_path).yield_self { |path| path.symlink? ? path.realpath : path } + @key_path = Pathname.new(key_path) + @env_key, @raise_if_missing_key = env_key, raise_if_missing_key + end + + # Returns the encryption key, first trying the environment variable + # specified by +env_key+, then trying the key file specified by +key_path+. + # If +raise_if_missing_key+ is true, raises MissingKeyError if the + # environment variable is not set and the key file does not exist. + def key + read_env_key || read_key_file || handle_missing_key + end + + # Reads the file and returns the decrypted content. + # + # Raises: + # - MissingKeyError if the key is missing and +raise_if_missing_key+ is true. + # - MissingContentError if the encrypted file does not exist or otherwise + # if the key is missing. + # - ActiveSupport::MessageEncryptor::InvalidMessage if the content cannot be + # decrypted or verified. + def read + if !key.nil? && content_path.exist? + decrypt content_path.binread + else + raise MissingContentError, content_path + end + end + + def write(contents) + IO.binwrite "#{content_path}.tmp", encrypt(contents) + FileUtils.mv "#{content_path}.tmp", content_path + end + + def change(&block) + writing read, &block + end + + + private + def writing(contents) + tmp_file = "#{Process.pid}.#{content_path.basename.to_s.chomp('.enc')}" + tmp_path = Pathname.new File.join(Dir.tmpdir, tmp_file) + tmp_path.binwrite contents + + yield tmp_path + + updated_contents = tmp_path.binread + + write(updated_contents) if updated_contents != contents + ensure + FileUtils.rm(tmp_path) if tmp_path&.exist? + end + + + def encrypt(contents) + check_key_length + encryptor.encrypt_and_sign contents + end + + def decrypt(contents) + encryptor.decrypt_and_verify contents + end + + def encryptor + @encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: CIPHER) + end + + + def read_env_key + ENV[env_key].presence + end + + def read_key_file + return @key_file_contents if defined?(@key_file_contents) + @key_file_contents = (key_path.binread.strip if key_path.exist?) + end + + def handle_missing_key + raise MissingKeyError.new(key_path: key_path, env_key: env_key) if raise_if_missing_key + end + + def check_key_length + raise InvalidKeyLengthError if key&.length != self.class.expected_key_length + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/environment_inquirer.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/environment_inquirer.rb new file mode 100644 index 0000000..770cddd --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/environment_inquirer.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require "active_support/string_inquirer" + +module ActiveSupport + class EnvironmentInquirer < StringInquirer # :nodoc: + DEFAULT_ENVIRONMENTS = ["development", "test", "production"] + def initialize(env) + super(env) + + DEFAULT_ENVIRONMENTS.each do |default| + instance_variable_set :"@#{default}", env == default + end + end + + DEFAULT_ENVIRONMENTS.each do |env| + class_eval "def #{env}?; @#{env}; end" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/error_reporter.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/error_reporter.rb new file mode 100644 index 0000000..8219a3e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/error_reporter.rb @@ -0,0 +1,117 @@ +# frozen_string_literal: true + +module ActiveSupport + # +ActiveSupport::ErrorReporter+ is a common interface for error reporting services. + # + # To rescue and report any unhandled error, you can use the +handle+ method: + # + # Rails.error.handle do + # do_something! + # end + # + # If an error is raised, it will be reported and swallowed. + # + # Alternatively if you want to report the error but not swallow it, you can use +record+ + # + # Rails.error.record do + # do_something! + # end + # + # Both methods can be restricted to only handle a specific exception class + # + # maybe_tags = Rails.error.handle(Redis::BaseError) { redis.get("tags") } + # + # You can also pass some extra context information that may be used by the error subscribers: + # + # Rails.error.handle(context: { section: "admin" }) do + # # ... + # end + # + # Additionally a +severity+ can be passed along to communicate how important the error report is. + # +severity+ can be one of +:error+, +:warning+, or +:info+. Handled errors default to the +:warning+ + # severity, and unhandled ones to +:error+. + # + # Both +handle+ and +record+ pass through the return value from the block. In the case of +handle+ + # rescuing an error, a fallback can be provided. The fallback must be a callable whose result will + # be returned when the block raises and is handled: + # + # user = Rails.error.handle(fallback: -> { User.anonymous }) do + # User.find_by(params) + # end + class ErrorReporter + SEVERITIES = %i(error warning info) + + attr_accessor :logger + + def initialize(*subscribers, logger: nil) + @subscribers = subscribers.flatten + @logger = logger + end + + # Report any unhandled exception, and swallow it. + # + # Rails.error.handle do + # 1 + '1' + # end + # + def handle(error_class = StandardError, severity: :warning, context: {}, fallback: nil) + yield + rescue error_class => error + report(error, handled: true, severity: severity, context: context) + fallback.call if fallback + end + + def record(error_class = StandardError, severity: :error, context: {}) + yield + rescue error_class => error + report(error, handled: false, severity: severity, context: context) + raise + end + + # Register a new error subscriber. The subscriber must respond to + # + # report(Exception, handled: Boolean, context: Hash) + # + # The +report+ method +should+ never raise an error. + def subscribe(subscriber) + unless subscriber.respond_to?(:report) + raise ArgumentError, "Error subscribers must respond to #report" + end + @subscribers << subscriber + end + + # Update the execution context that is accessible to error subscribers + # + # Rails.error.set_context(section: "checkout", user_id: @user.id) + # + # See +ActiveSupport::ExecutionContext.set+ + def set_context(...) + ActiveSupport::ExecutionContext.set(...) + end + + # When the block based +handle+ and +record+ methods are not suitable, you can directly use +report+ + # + # Rails.error.report(error, handled: true) + def report(error, handled:, severity: handled ? :warning : :error, context: {}) + unless SEVERITIES.include?(severity) + raise ArgumentError, "severity must be one of #{SEVERITIES.map(&:inspect).join(", ")}, got: #{severity.inspect}" + end + + full_context = ActiveSupport::ExecutionContext.to_h.merge(context) + @subscribers.each do |subscriber| + subscriber.report(error, handled: handled, severity: severity, context: full_context) + rescue => subscriber_error + if logger + logger.fatal( + "Error subscriber raised an error: #{subscriber_error.message} (#{subscriber_error.class})\n" + + subscriber_error.backtrace.join("\n") + ) + else + raise + end + end + + nil + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/evented_file_update_checker.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/evented_file_update_checker.rb new file mode 100644 index 0000000..bca31f4 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/evented_file_update_checker.rb @@ -0,0 +1,168 @@ +# frozen_string_literal: true + +require "set" +require "pathname" +require "concurrent/atomic/atomic_boolean" +require "listen" +require "active_support/fork_tracker" + +module ActiveSupport + # Allows you to "listen" to changes in a file system. + # The evented file updater does not hit disk when checking for updates. + # Instead, it uses platform-specific file system events to trigger a change + # in state. + # + # The file checker takes an array of files to watch or a hash specifying directories + # and file extensions to watch. It also takes a block that is called when + # EventedFileUpdateChecker#execute is run or when EventedFileUpdateChecker#execute_if_updated + # is run and there have been changes to the file system. + # + # Example: + # + # checker = ActiveSupport::EventedFileUpdateChecker.new(["/tmp/foo"]) { puts "changed" } + # checker.updated? + # # => false + # checker.execute_if_updated + # # => nil + # + # FileUtils.touch("/tmp/foo") + # + # checker.updated? + # # => true + # checker.execute_if_updated + # # => "changed" + # + class EventedFileUpdateChecker # :nodoc: all + def initialize(files, dirs = {}, &block) + unless block + raise ArgumentError, "A block is required to initialize an EventedFileUpdateChecker" + end + + @block = block + @core = Core.new(files, dirs) + ObjectSpace.define_finalizer(self, @core.finalizer) + end + + def updated? + if @core.restart? + @core.thread_safely(&:restart) + @core.updated.make_true + end + + @core.updated.true? + end + + def execute + @core.updated.make_false + @block.call + end + + def execute_if_updated + if updated? + yield if block_given? + execute + true + end + end + + class Core + attr_reader :updated + + def initialize(files, dirs) + @files = files.map { |file| Pathname(file).expand_path }.to_set + + @dirs = dirs.each_with_object({}) do |(dir, exts), hash| + hash[Pathname(dir).expand_path] = Array(exts).map { |ext| ext.to_s.sub(/\A\.?/, ".") }.to_set + end + + @common_path = common_path(@dirs.keys) + + @dtw = directories_to_watch + @missing = [] + + @updated = Concurrent::AtomicBoolean.new(false) + @mutex = Mutex.new + + start + @after_fork = ActiveSupport::ForkTracker.after_fork { start } + end + + def finalizer + proc do + stop + ActiveSupport::ForkTracker.unregister(@after_fork) + end + end + + def thread_safely + @mutex.synchronize do + yield self + end + end + + def start + normalize_dirs! + @dtw, @missing = [*@dtw, *@missing].partition(&:exist?) + @listener = @dtw.any? ? Listen.to(*@dtw, &method(:changed)) : nil + @listener&.start + end + + def stop + @listener&.stop + end + + def restart + stop + start + end + + def restart? + @missing.any?(&:exist?) + end + + def normalize_dirs! + @dirs.transform_keys! do |dir| + dir.exist? ? dir.realpath : dir + end + end + + def changed(modified, added, removed) + unless @updated.true? + @updated.make_true if (modified + added + removed).any? { |f| watching?(f) } + end + end + + def watching?(file) + file = Pathname(file) + + if @files.member?(file) + true + elsif file.directory? + false + else + ext = file.extname + + file.dirname.ascend do |dir| + matching = @dirs[dir] + + if matching && (matching.empty? || matching.include?(ext)) + break true + elsif dir == @common_path || dir.root? + break false + end + end + end + end + + def directories_to_watch + dtw = @dirs.keys | @files.map(&:dirname) + accounted_for = dtw.to_set + Gem.path.map { |path| Pathname(path) } + dtw.reject { |dir| dir.ascend.drop(1).any? { |parent| accounted_for.include?(parent) } } + end + + def common_path(paths) + paths.map { |path| path.ascend.to_a }.reduce(&:&)&.first + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/execution_context.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/execution_context.rb new file mode 100644 index 0000000..1c95188 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/execution_context.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +module ActiveSupport + module ExecutionContext # :nodoc: + @after_change_callbacks = [] + class << self + def after_change(&block) + @after_change_callbacks << block + end + + # Updates the execution context. If a block is given, it resets the provided keys to their + # previous value once the block exits. + def set(**options) + options.symbolize_keys! + keys = options.keys + + store = self.store + + previous_context = keys.zip(store.values_at(*keys)).to_h + + store.merge!(options) + @after_change_callbacks.each(&:call) + + if block_given? + begin + yield + ensure + store.merge!(previous_context) + @after_change_callbacks.each(&:call) + end + end + end + + def []=(key, value) + store[key.to_sym] = value + @after_change_callbacks.each(&:call) + end + + def to_h + store.dup + end + + def clear + store.clear + end + + private + def store + IsolatedExecutionState[:active_support_execution_context] ||= {} + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/execution_context/test_helper.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/execution_context/test_helper.rb new file mode 100644 index 0000000..ae8c43e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/execution_context/test_helper.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module ActiveSupport::ExecutionContext::TestHelper # :nodoc: + def before_setup + ActiveSupport::ExecutionContext.clear + super + end + + def after_teardown + super + ActiveSupport::ExecutionContext.clear + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/execution_wrapper.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/execution_wrapper.rb new file mode 100644 index 0000000..5a4a9b2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/execution_wrapper.rb @@ -0,0 +1,151 @@ +# frozen_string_literal: true + +require "active_support/error_reporter" +require "active_support/callbacks" +require "concurrent/hash" + +module ActiveSupport + class ExecutionWrapper + include ActiveSupport::Callbacks + + Null = Object.new # :nodoc: + def Null.complete! # :nodoc: + end + + define_callbacks :run + define_callbacks :complete + + def self.to_run(*args, &block) + set_callback(:run, *args, &block) + end + + def self.to_complete(*args, &block) + set_callback(:complete, *args, &block) + end + + RunHook = Struct.new(:hook) do # :nodoc: + def before(target) + hook_state = target.send(:hook_state) + hook_state[hook] = hook.run + end + end + + CompleteHook = Struct.new(:hook) do # :nodoc: + def before(target) + hook_state = target.send(:hook_state) + if hook_state.key?(hook) + hook.complete hook_state[hook] + end + end + alias after before + end + + # Register an object to be invoked during both the +run+ and + # +complete+ steps. + # + # +hook.complete+ will be passed the value returned from +hook.run+, + # and will only be invoked if +run+ has previously been called. + # (Mostly, this means it won't be invoked if an exception occurs in + # a preceding +to_run+ block; all ordinary +to_complete+ blocks are + # invoked in that situation.) + def self.register_hook(hook, outer: false) + if outer + to_run RunHook.new(hook), prepend: true + to_complete :after, CompleteHook.new(hook) + else + to_run RunHook.new(hook) + to_complete CompleteHook.new(hook) + end + end + + # Run this execution. + # + # Returns an instance, whose +complete!+ method *must* be invoked + # after the work has been performed. + # + # Where possible, prefer +wrap+. + def self.run!(reset: false) + if reset + lost_instance = IsolatedExecutionState.delete(active_key) + lost_instance&.complete! + else + return Null if active? + end + + new.tap do |instance| + success = nil + begin + instance.run! + success = true + ensure + instance.complete! unless success + end + end + end + + # Perform the work in the supplied block as an execution. + def self.wrap + return yield if active? + + instance = run! + begin + yield + rescue => error + error_reporter.report(error, handled: false) + raise + ensure + instance.complete! + end + end + + def self.perform # :nodoc: + instance = new + instance.run + begin + yield + ensure + instance.complete + end + end + + def self.error_reporter + @error_reporter ||= ActiveSupport::ErrorReporter.new + end + + def self.active_key # :nodoc: + @active_key ||= :"active_execution_wrapper_#{object_id}" + end + + def self.active? # :nodoc: + IsolatedExecutionState.key?(active_key) + end + + def run! # :nodoc: + IsolatedExecutionState[self.class.active_key] = self + run + end + + def run # :nodoc: + run_callbacks(:run) + end + + # Complete this in-flight execution. This method *must* be called + # exactly once on the result of any call to +run!+. + # + # Where possible, prefer +wrap+. + def complete! + complete + ensure + IsolatedExecutionState.delete(self.class.active_key) + end + + def complete # :nodoc: + run_callbacks(:complete) + end + + private + def hook_state + @_hook_state ||= {} + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/executor.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/executor.rb new file mode 100644 index 0000000..ce391b0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/executor.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +require "active_support/execution_wrapper" + +module ActiveSupport + class Executor < ExecutionWrapper + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/executor/test_helper.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/executor/test_helper.rb new file mode 100644 index 0000000..97f489d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/executor/test_helper.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module ActiveSupport::Executor::TestHelper # :nodoc: + def run(...) + Rails.application.executor.perform { super } + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/file_update_checker.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/file_update_checker.rb new file mode 100644 index 0000000..9b665e7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/file_update_checker.rb @@ -0,0 +1,162 @@ +# frozen_string_literal: true + +require "active_support/core_ext/time/calculations" + +module ActiveSupport + # FileUpdateChecker specifies the API used by Rails to watch files + # and control reloading. The API depends on four methods: + # + # * +initialize+ which expects two parameters and one block as + # described below. + # + # * +updated?+ which returns a boolean if there were updates in + # the filesystem or not. + # + # * +execute+ which executes the given block on initialization + # and updates the latest watched files and timestamp. + # + # * +execute_if_updated+ which just executes the block if it was updated. + # + # After initialization, a call to +execute_if_updated+ must execute + # the block only if there was really a change in the filesystem. + # + # This class is used by Rails to reload the I18n framework whenever + # they are changed upon a new request. + # + # i18n_reloader = ActiveSupport::FileUpdateChecker.new(paths) do + # I18n.reload! + # end + # + # ActiveSupport::Reloader.to_prepare do + # i18n_reloader.execute_if_updated + # end + class FileUpdateChecker + # It accepts two parameters on initialization. The first is an array + # of files and the second is an optional hash of directories. The hash must + # have directories as keys and the value is an array of extensions to be + # watched under that directory. + # + # This method must also receive a block that will be called once a path + # changes. The array of files and list of directories cannot be changed + # after FileUpdateChecker has been initialized. + def initialize(files, dirs = {}, &block) + unless block + raise ArgumentError, "A block is required to initialize a FileUpdateChecker" + end + + @files = files.freeze + @glob = compile_glob(dirs) + @block = block + + @watched = nil + @updated_at = nil + + @last_watched = watched + @last_update_at = updated_at(@last_watched) + end + + # Check if any of the entries were updated. If so, the watched and/or + # updated_at values are cached until the block is executed via +execute+ + # or +execute_if_updated+. + def updated? + current_watched = watched + if @last_watched.size != current_watched.size + @watched = current_watched + true + else + current_updated_at = updated_at(current_watched) + if @last_update_at < current_updated_at + @watched = current_watched + @updated_at = current_updated_at + true + else + false + end + end + end + + # Executes the given block and updates the latest watched files and + # timestamp. + def execute + @last_watched = watched + @last_update_at = updated_at(@last_watched) + @block.call + ensure + @watched = nil + @updated_at = nil + end + + # Execute the block given if updated. + def execute_if_updated + if updated? + yield if block_given? + execute + true + else + false + end + end + + private + def watched + @watched || begin + all = @files.select { |f| File.exist?(f) } + all.concat(Dir[@glob]) if @glob + all + end + end + + def updated_at(paths) + @updated_at || max_mtime(paths) || Time.at(0) + end + + # This method returns the maximum mtime of the files in +paths+, or +nil+ + # if the array is empty. + # + # Files with a mtime in the future are ignored. Such abnormal situation + # can happen for example if the user changes the clock by hand. It is + # healthy to consider this edge case because with mtimes in the future + # reloading is not triggered. + def max_mtime(paths) + time_now = Time.now + max_mtime = nil + + # Time comparisons are performed with #compare_without_coercion because + # AS redefines these operators in a way that is much slower and does not + # bring any benefit in this particular code. + # + # Read t1.compare_without_coercion(t2) < 0 as t1 < t2. + paths.each do |path| + mtime = File.mtime(path) + + next if time_now.compare_without_coercion(mtime) < 0 + + if max_mtime.nil? || max_mtime.compare_without_coercion(mtime) < 0 + max_mtime = mtime + end + end + + max_mtime + end + + def compile_glob(hash) + hash.freeze # Freeze so changes aren't accidentally pushed + return if hash.empty? + + globs = hash.map do |key, value| + "#{escape(key)}/**/*#{compile_ext(value)}" + end + "{#{globs.join(",")}}" + end + + def escape(key) + key.gsub(",", '\,') + end + + def compile_ext(array) + array = Array(array) + return if array.empty? + ".{#{array.join(",")}}" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/fork_tracker.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/fork_tracker.rb new file mode 100644 index 0000000..bc1d32b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/fork_tracker.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +module ActiveSupport + module ForkTracker # :nodoc: + module ModernCoreExt + def _fork + pid = super + if pid == 0 + ForkTracker.check! + end + pid + end + end + + module CoreExt + def fork(...) + if block_given? + super do + ForkTracker.check! + yield + end + else + unless pid = super + ForkTracker.check! + end + pid + end + end + end + + module CoreExtPrivate + include CoreExt + private :fork + end + + @pid = Process.pid + @callbacks = [] + + class << self + def check! + new_pid = Process.pid + if @pid != new_pid + @callbacks.each(&:call) + @pid = new_pid + end + end + + def hook! + if Process.respond_to?(:_fork) # Ruby 3.1+ + ::Process.singleton_class.prepend(ModernCoreExt) + elsif Process.respond_to?(:fork) + ::Object.prepend(CoreExtPrivate) if RUBY_VERSION < "3.0" + ::Kernel.prepend(CoreExtPrivate) + ::Kernel.singleton_class.prepend(CoreExt) + ::Process.singleton_class.prepend(CoreExt) + end + end + + def after_fork(&block) + @callbacks << block + block + end + + def unregister(callback) + @callbacks.delete(callback) + end + end + end +end + +ActiveSupport::ForkTracker.hook! diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/gem_version.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/gem_version.rb new file mode 100644 index 0000000..421f95c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/gem_version.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module ActiveSupport + # Returns the currently loaded version of Active Support as a Gem::Version. + def self.gem_version + Gem::Version.new VERSION::STRING + end + + module VERSION + MAJOR = 7 + MINOR = 0 + TINY = 4 + PRE = nil + + STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".") + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/gzip.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/gzip.rb new file mode 100644 index 0000000..7ffa6d9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/gzip.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require "zlib" +require "stringio" + +module ActiveSupport + # A convenient wrapper for the zlib standard library that allows + # compression/decompression of strings with gzip. + # + # gzip = ActiveSupport::Gzip.compress('compress me!') + # # => "\x1F\x8B\b\x00o\x8D\xCDO\x00\x03K\xCE\xCF-(J-.V\xC8MU\x04\x00R>n\x83\f\x00\x00\x00" + # + # ActiveSupport::Gzip.decompress(gzip) + # # => "compress me!" + module Gzip + class Stream < StringIO + def initialize(*) + super + set_encoding "BINARY" + end + def close; rewind; end + end + + # Decompresses a gzipped string. + def self.decompress(source) + Zlib::GzipReader.wrap(StringIO.new(source), &:read) + end + + # Compresses a string using gzip. + def self.compress(source, level = Zlib::DEFAULT_COMPRESSION, strategy = Zlib::DEFAULT_STRATEGY) + output = Stream.new + gz = Zlib::GzipWriter.new(output, level, strategy) + gz.write(source) + gz.close + output.string + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/hash_with_indifferent_access.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/hash_with_indifferent_access.rb new file mode 100644 index 0000000..aed884f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/hash_with_indifferent_access.rb @@ -0,0 +1,425 @@ +# frozen_string_literal: true + +require "active_support/core_ext/hash/keys" +require "active_support/core_ext/hash/reverse_merge" +require "active_support/core_ext/hash/except" +require "active_support/core_ext/hash/slice" + +module ActiveSupport + # Implements a hash where keys :foo and "foo" are considered + # to be the same. + # + # rgb = ActiveSupport::HashWithIndifferentAccess.new + # + # rgb[:black] = '#000000' + # rgb[:black] # => '#000000' + # rgb['black'] # => '#000000' + # + # rgb['white'] = '#FFFFFF' + # rgb[:white] # => '#FFFFFF' + # rgb['white'] # => '#FFFFFF' + # + # Internally symbols are mapped to strings when used as keys in the entire + # writing interface (calling []=, merge, etc). This + # mapping belongs to the public interface. For example, given: + # + # hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1) + # + # You are guaranteed that the key is returned as a string: + # + # hash.keys # => ["a"] + # + # Technically other types of keys are accepted: + # + # hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1) + # hash[0] = 0 + # hash # => {"a"=>1, 0=>0} + # + # but this class is intended for use cases where strings or symbols are the + # expected keys and it is convenient to understand both as the same. For + # example the +params+ hash in Ruby on Rails. + # + # Note that core extensions define Hash#with_indifferent_access: + # + # rgb = { black: '#000000', white: '#FFFFFF' }.with_indifferent_access + # + # which may be handy. + # + # To access this class outside of Rails, require the core extension with: + # + # require "active_support/core_ext/hash/indifferent_access" + # + # which will, in turn, require this file. + class HashWithIndifferentAccess < Hash + # Returns +true+ so that Array#extract_options! finds members of + # this class. + def extractable_options? + true + end + + def with_indifferent_access + dup + end + + def nested_under_indifferent_access + self + end + + def initialize(constructor = nil) + if constructor.respond_to?(:to_hash) + super() + update(constructor) + + hash = constructor.is_a?(Hash) ? constructor : constructor.to_hash + self.default = hash.default if hash.default + self.default_proc = hash.default_proc if hash.default_proc + elsif constructor.nil? + super() + else + super(constructor) + end + end + + def self.[](*args) + new.merge!(Hash[*args]) + end + + alias_method :regular_writer, :[]= unless method_defined?(:regular_writer) + alias_method :regular_update, :update unless method_defined?(:regular_update) + + # Assigns a new value to the hash: + # + # hash = ActiveSupport::HashWithIndifferentAccess.new + # hash[:key] = 'value' + # + # This value can be later fetched using either +:key+ or 'key'. + def []=(key, value) + regular_writer(convert_key(key), convert_value(value, conversion: :assignment)) + end + + alias_method :store, :[]= + + # Updates the receiver in-place, merging in the hashes passed as arguments: + # + # hash_1 = ActiveSupport::HashWithIndifferentAccess.new + # hash_1[:key] = 'value' + # + # hash_2 = ActiveSupport::HashWithIndifferentAccess.new + # hash_2[:key] = 'New Value!' + # + # hash_1.update(hash_2) # => {"key"=>"New Value!"} + # + # hash = ActiveSupport::HashWithIndifferentAccess.new + # hash.update({ "a" => 1 }, { "b" => 2 }) # => { "a" => 1, "b" => 2 } + # + # The arguments can be either an + # ActiveSupport::HashWithIndifferentAccess or a regular +Hash+. + # In either case the merge respects the semantics of indifferent access. + # + # If the argument is a regular hash with keys +:key+ and "key" only one + # of the values end up in the receiver, but which one is unspecified. + # + # When given a block, the value for duplicated keys will be determined + # by the result of invoking the block with the duplicated key, the value + # in the receiver, and the value in +other_hash+. The rules for duplicated + # keys follow the semantics of indifferent access: + # + # hash_1[:key] = 10 + # hash_2['key'] = 12 + # hash_1.update(hash_2) { |key, old, new| old + new } # => {"key"=>22} + def update(*other_hashes, &block) + if other_hashes.size == 1 + update_with_single_argument(other_hashes.first, block) + else + other_hashes.each do |other_hash| + update_with_single_argument(other_hash, block) + end + end + self + end + + alias_method :merge!, :update + + # Checks the hash for a key matching the argument passed in: + # + # hash = ActiveSupport::HashWithIndifferentAccess.new + # hash['key'] = 'value' + # hash.key?(:key) # => true + # hash.key?('key') # => true + def key?(key) + super(convert_key(key)) + end + + alias_method :include?, :key? + alias_method :has_key?, :key? + alias_method :member?, :key? + + # Same as Hash#[] where the key passed as argument can be + # either a string or a symbol: + # + # counters = ActiveSupport::HashWithIndifferentAccess.new + # counters[:foo] = 1 + # + # counters['foo'] # => 1 + # counters[:foo] # => 1 + # counters[:zoo] # => nil + def [](key) + super(convert_key(key)) + end + + # Same as Hash#assoc where the key passed as argument can be + # either a string or a symbol: + # + # counters = ActiveSupport::HashWithIndifferentAccess.new + # counters[:foo] = 1 + # + # counters.assoc('foo') # => ["foo", 1] + # counters.assoc(:foo) # => ["foo", 1] + # counters.assoc(:zoo) # => nil + def assoc(key) + super(convert_key(key)) + end + + # Same as Hash#fetch where the key passed as argument can be + # either a string or a symbol: + # + # counters = ActiveSupport::HashWithIndifferentAccess.new + # counters[:foo] = 1 + # + # counters.fetch('foo') # => 1 + # counters.fetch(:bar, 0) # => 0 + # counters.fetch(:bar) { |key| 0 } # => 0 + # counters.fetch(:zoo) # => KeyError: key not found: "zoo" + def fetch(key, *extras) + super(convert_key(key), *extras) + end + + # Same as Hash#dig where the key passed as argument can be + # either a string or a symbol: + # + # counters = ActiveSupport::HashWithIndifferentAccess.new + # counters[:foo] = { bar: 1 } + # + # counters.dig('foo', 'bar') # => 1 + # counters.dig(:foo, :bar) # => 1 + # counters.dig(:zoo) # => nil + def dig(*args) + args[0] = convert_key(args[0]) if args.size > 0 + super(*args) + end + + # Same as Hash#default where the key passed as argument can be + # either a string or a symbol: + # + # hash = ActiveSupport::HashWithIndifferentAccess.new(1) + # hash.default # => 1 + # + # hash = ActiveSupport::HashWithIndifferentAccess.new { |hash, key| key } + # hash.default # => nil + # hash.default('foo') # => 'foo' + # hash.default(:foo) # => 'foo' + def default(*args) + super(*args.map { |arg| convert_key(arg) }) + end + + # Returns an array of the values at the specified indices: + # + # hash = ActiveSupport::HashWithIndifferentAccess.new + # hash[:a] = 'x' + # hash[:b] = 'y' + # hash.values_at('a', 'b') # => ["x", "y"] + def values_at(*keys) + super(*keys.map { |key| convert_key(key) }) + end + + # Returns an array of the values at the specified indices, but also + # raises an exception when one of the keys can't be found. + # + # hash = ActiveSupport::HashWithIndifferentAccess.new + # hash[:a] = 'x' + # hash[:b] = 'y' + # hash.fetch_values('a', 'b') # => ["x", "y"] + # hash.fetch_values('a', 'c') { |key| 'z' } # => ["x", "z"] + # hash.fetch_values('a', 'c') # => KeyError: key not found: "c" + def fetch_values(*indices, &block) + super(*indices.map { |key| convert_key(key) }, &block) + end + + # Returns a shallow copy of the hash. + # + # hash = ActiveSupport::HashWithIndifferentAccess.new({ a: { b: 'b' } }) + # dup = hash.dup + # dup[:a][:c] = 'c' + # + # hash[:a][:c] # => "c" + # dup[:a][:c] # => "c" + def dup + self.class.new(self).tap do |new_hash| + set_defaults(new_hash) + end + end + + # This method has the same semantics of +update+, except it does not + # modify the receiver but rather returns a new hash with indifferent + # access with the result of the merge. + def merge(*hashes, &block) + dup.update(*hashes, &block) + end + + # Like +merge+ but the other way around: Merges the receiver into the + # argument and returns a new hash with indifferent access as result: + # + # hash = ActiveSupport::HashWithIndifferentAccess.new + # hash['a'] = nil + # hash.reverse_merge(a: 0, b: 1) # => {"a"=>nil, "b"=>1} + def reverse_merge(other_hash) + super(self.class.new(other_hash)) + end + alias_method :with_defaults, :reverse_merge + + # Same semantics as +reverse_merge+ but modifies the receiver in-place. + def reverse_merge!(other_hash) + super(self.class.new(other_hash)) + end + alias_method :with_defaults!, :reverse_merge! + + # Replaces the contents of this hash with other_hash. + # + # h = { "a" => 100, "b" => 200 } + # h.replace({ "c" => 300, "d" => 400 }) # => {"c"=>300, "d"=>400} + def replace(other_hash) + super(self.class.new(other_hash)) + end + + # Removes the specified key from the hash. + def delete(key) + super(convert_key(key)) + end + + # Returns a hash with indifferent access that includes everything except given keys. + # hash = { a: "x", b: "y", c: 10 }.with_indifferent_access + # hash.except(:a, "b") # => {c: 10}.with_indifferent_access + # hash # => { a: "x", b: "y", c: 10 }.with_indifferent_access + def except(*keys) + slice(*self.keys - keys.map { |key| convert_key(key) }) + end + alias_method :without, :except + + def stringify_keys!; self end + def deep_stringify_keys!; self end + def stringify_keys; dup end + def deep_stringify_keys; dup end + undef :symbolize_keys! + undef :deep_symbolize_keys! + def symbolize_keys; to_hash.symbolize_keys! end + alias_method :to_options, :symbolize_keys + def deep_symbolize_keys; to_hash.deep_symbolize_keys! end + def to_options!; self end + + def select(*args, &block) + return to_enum(:select) unless block_given? + dup.tap { |hash| hash.select!(*args, &block) } + end + + def reject(*args, &block) + return to_enum(:reject) unless block_given? + dup.tap { |hash| hash.reject!(*args, &block) } + end + + def transform_values(*args, &block) + return to_enum(:transform_values) unless block_given? + dup.tap { |hash| hash.transform_values!(*args, &block) } + end + + def transform_keys(*args, &block) + return to_enum(:transform_keys) unless block_given? + dup.tap { |hash| hash.transform_keys!(*args, &block) } + end + + def transform_keys! + return enum_for(:transform_keys!) { size } unless block_given? + keys.each do |key| + self[yield(key)] = delete(key) + end + self + end + + def slice(*keys) + keys.map! { |key| convert_key(key) } + self.class.new(super) + end + + def slice!(*keys) + keys.map! { |key| convert_key(key) } + super + end + + def compact + dup.tap(&:compact!) + end + + # Convert to a regular hash with string keys. + def to_hash + _new_hash = Hash.new + set_defaults(_new_hash) + + each do |key, value| + _new_hash[key] = convert_value(value, conversion: :to_hash) + end + _new_hash + end + + private + if Symbol.method_defined?(:name) + def convert_key(key) + key.kind_of?(Symbol) ? key.name : key + end + else + def convert_key(key) + key.kind_of?(Symbol) ? key.to_s : key + end + end + + def convert_value(value, conversion: nil) + if value.is_a? Hash + if conversion == :to_hash + value.to_hash + else + value.nested_under_indifferent_access + end + elsif value.is_a?(Array) + if conversion != :assignment || value.frozen? + value = value.dup + end + value.map! { |e| convert_value(e, conversion: conversion) } + else + value + end + end + + def set_defaults(target) + if default_proc + target.default_proc = default_proc.dup + else + target.default = default + end + end + + def update_with_single_argument(other_hash, block) + if other_hash.is_a? HashWithIndifferentAccess + regular_update(other_hash, &block) + else + other_hash.to_hash.each_pair do |key, value| + if block && key?(key) + value = block.call(convert_key(key), self[key], value) + end + regular_writer(convert_key(key), convert_value(value)) + end + end + end + end +end + +# :stopdoc: + +HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/html_safe_translation.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/html_safe_translation.rb new file mode 100644 index 0000000..2d06a0d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/html_safe_translation.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module ActiveSupport + module HtmlSafeTranslation # :nodoc: + extend self + + def translate(key, **options) + if html_safe_translation_key?(key) + html_safe_options = html_escape_translation_options(options) + translation = I18n.translate(key, **html_safe_options) + html_safe_translation(translation) + else + I18n.translate(key, **options) + end + end + + private + def html_safe_translation_key?(key) + /(?:_|\b)html\z/.match?(key) + end + + def html_escape_translation_options(options) + options.each do |name, value| + unless i18n_option?(name) || (name == :count && value.is_a?(Numeric)) + options[name] = ERB::Util.html_escape(value.to_s) + end + end + end + + def i18n_option?(name) + (@i18n_option_names ||= I18n::RESERVED_KEYS.to_set).include?(name) + end + + + def html_safe_translation(translation) + if translation.respond_to?(:map) + translation.map { |element| element.respond_to?(:html_safe) ? element.html_safe : element } + else + translation.respond_to?(:html_safe) ? translation.html_safe : translation + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/i18n.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/i18n.rb new file mode 100644 index 0000000..832a9fa --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/i18n.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require "active_support/core_ext/hash/deep_merge" +require "active_support/core_ext/hash/except" +require "active_support/core_ext/hash/slice" +begin + require "i18n" + require "i18n/backend/fallbacks" +rescue LoadError => e + $stderr.puts "The i18n gem is not available. Please add it to your Gemfile and run bundle install" + raise e +end +require "active_support/lazy_load_hooks" + +ActiveSupport.run_load_hooks(:i18n) +I18n.load_path << File.expand_path("locale/en.yml", __dir__) +I18n.load_path << File.expand_path("locale/en.rb", __dir__) diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/i18n_railtie.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/i18n_railtie.rb new file mode 100644 index 0000000..1e7185e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/i18n_railtie.rb @@ -0,0 +1,125 @@ +# frozen_string_literal: true + +require "active_support" +require "active_support/core_ext/array/wrap" + +# :enddoc: + +module I18n + class Railtie < Rails::Railtie + config.i18n = ActiveSupport::OrderedOptions.new + config.i18n.railties_load_path = [] + config.i18n.load_path = [] + config.i18n.fallbacks = ActiveSupport::OrderedOptions.new + + config.eager_load_namespaces << I18n + + # Set the i18n configuration after initialization since a lot of + # configuration is still usually done in application initializers. + config.after_initialize do |app| + I18n::Railtie.initialize_i18n(app) + end + + # Trigger i18n config before any eager loading has happened + # so it's ready if any classes require it when eager loaded. + config.before_eager_load do |app| + I18n::Railtie.initialize_i18n(app) + end + + @i18n_inited = false + + # Setup i18n configuration. + def self.initialize_i18n(app) + return if @i18n_inited + + fallbacks = app.config.i18n.delete(:fallbacks) + + # Avoid issues with setting the default_locale by disabling available locales + # check while configuring. + enforce_available_locales = app.config.i18n.delete(:enforce_available_locales) + enforce_available_locales = I18n.enforce_available_locales if enforce_available_locales.nil? + I18n.enforce_available_locales = false + + reloadable_paths = [] + app.config.i18n.each do |setting, value| + case setting + when :railties_load_path + reloadable_paths = value + app.config.i18n.load_path.unshift(*value.flat_map(&:existent)) + when :load_path + I18n.load_path += value + when :raise_on_missing_translations + forward_raise_on_missing_translations_config(app) + else + I18n.public_send("#{setting}=", value) + end + end + + init_fallbacks(fallbacks) if fallbacks && validate_fallbacks(fallbacks) + + # Restore available locales check so it will take place from now on. + I18n.enforce_available_locales = enforce_available_locales + + directories = watched_dirs_with_extensions(reloadable_paths) + reloader = app.config.file_watcher.new(I18n.load_path.dup, directories) do + I18n.load_path.keep_if { |p| File.exist?(p) } + I18n.load_path |= reloadable_paths.flat_map(&:existent) + end + + app.reloaders << reloader + app.reloader.to_run do + reloader.execute_if_updated { require_unload_lock! } + end + reloader.execute + + @i18n_inited = true + end + + def self.forward_raise_on_missing_translations_config(app) + ActiveSupport.on_load(:action_view) do + ActionView::Helpers::TranslationHelper.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations + end + + ActiveSupport.on_load(:action_controller) do + AbstractController::Translation.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations + end + end + + def self.include_fallbacks_module + I18n.backend.class.include(I18n::Backend::Fallbacks) + end + + def self.init_fallbacks(fallbacks) + include_fallbacks_module + + args = \ + case fallbacks + when ActiveSupport::OrderedOptions + [*(fallbacks[:defaults] || []) << fallbacks[:map]].compact + when Hash, Array + Array.wrap(fallbacks) + else # TrueClass + [I18n.default_locale] + end + + I18n.fallbacks = I18n::Locale::Fallbacks.new(*args) + end + + def self.validate_fallbacks(fallbacks) + case fallbacks + when ActiveSupport::OrderedOptions + !fallbacks.empty? + when TrueClass, Array, Hash + true + else + raise "Unexpected fallback type #{fallbacks.inspect}" + end + end + + def self.watched_dirs_with_extensions(paths) + paths.each_with_object({}) do |path, result| + result[path.absolute_current] = path.extensions + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/inflections.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/inflections.rb new file mode 100644 index 0000000..baf1cb3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/inflections.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require "active_support/inflector/inflections" + +#-- +# Defines the standard inflection rules. These are the starting point for +# new projects and are not considered complete. The current set of inflection +# rules is frozen. This means, we do not change them to become more complete. +# This is a safety measure to keep existing applications from breaking. +#++ +module ActiveSupport + Inflector.inflections(:en) do |inflect| + inflect.plural(/$/, "s") + inflect.plural(/s$/i, "s") + inflect.plural(/^(ax|test)is$/i, '\1es') + inflect.plural(/(octop|vir)us$/i, '\1i') + inflect.plural(/(octop|vir)i$/i, '\1i') + inflect.plural(/(alias|status)$/i, '\1es') + inflect.plural(/(bu)s$/i, '\1ses') + inflect.plural(/(buffal|tomat)o$/i, '\1oes') + inflect.plural(/([ti])um$/i, '\1a') + inflect.plural(/([ti])a$/i, '\1a') + inflect.plural(/sis$/i, "ses") + inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves') + inflect.plural(/(hive)$/i, '\1s') + inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies') + inflect.plural(/(x|ch|ss|sh)$/i, '\1es') + inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices') + inflect.plural(/^(m|l)ouse$/i, '\1ice') + inflect.plural(/^(m|l)ice$/i, '\1ice') + inflect.plural(/^(ox)$/i, '\1en') + inflect.plural(/^(oxen)$/i, '\1') + inflect.plural(/(quiz)$/i, '\1zes') + + inflect.singular(/s$/i, "") + inflect.singular(/(ss)$/i, '\1') + inflect.singular(/(n)ews$/i, '\1ews') + inflect.singular(/([ti])a$/i, '\1um') + inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '\1sis') + inflect.singular(/(^analy)(sis|ses)$/i, '\1sis') + inflect.singular(/([^f])ves$/i, '\1fe') + inflect.singular(/(hive)s$/i, '\1') + inflect.singular(/(tive)s$/i, '\1') + inflect.singular(/([lr])ves$/i, '\1f') + inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y') + inflect.singular(/(s)eries$/i, '\1eries') + inflect.singular(/(m)ovies$/i, '\1ovie') + inflect.singular(/(x|ch|ss|sh)es$/i, '\1') + inflect.singular(/^(m|l)ice$/i, '\1ouse') + inflect.singular(/(bus)(es)?$/i, '\1') + inflect.singular(/(o)es$/i, '\1') + inflect.singular(/(shoe)s$/i, '\1') + inflect.singular(/(cris|test)(is|es)$/i, '\1is') + inflect.singular(/^(a)x[ie]s$/i, '\1xis') + inflect.singular(/(octop|vir)(us|i)$/i, '\1us') + inflect.singular(/(alias|status)(es)?$/i, '\1') + inflect.singular(/^(ox)en/i, '\1') + inflect.singular(/(vert|ind)ices$/i, '\1ex') + inflect.singular(/(matr)ices$/i, '\1ix') + inflect.singular(/(quiz)zes$/i, '\1') + inflect.singular(/(database)s$/i, '\1') + + inflect.irregular("person", "people") + inflect.irregular("man", "men") + inflect.irregular("child", "children") + inflect.irregular("sex", "sexes") + inflect.irregular("move", "moves") + inflect.irregular("zombie", "zombies") + + inflect.uncountable(%w(equipment information rice money species series fish sheep jeans police)) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/inflector.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/inflector.rb new file mode 100644 index 0000000..d77f04c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/inflector.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +# in case active_support/inflector is required without the rest of active_support +require "active_support/inflector/inflections" +require "active_support/inflector/transliterate" +require "active_support/inflector/methods" + +require "active_support/inflections" +require "active_support/core_ext/string/inflections" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/inflector/inflections.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/inflector/inflections.rb new file mode 100644 index 0000000..a9943a8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/inflector/inflections.rb @@ -0,0 +1,271 @@ +# frozen_string_literal: true + +require "concurrent/map" +require "active_support/i18n" + +module ActiveSupport + module Inflector + extend self + + # A singleton instance of this class is yielded by Inflector.inflections, + # which can then be used to specify additional inflection rules. If passed + # an optional locale, rules for other languages can be specified. The + # default locale is :en. Only rules for English are provided. + # + # ActiveSupport::Inflector.inflections(:en) do |inflect| + # inflect.plural /^(ox)$/i, '\1\2en' + # inflect.singular /^(ox)en/i, '\1' + # + # inflect.irregular 'cactus', 'cacti' + # + # inflect.uncountable 'equipment' + # end + # + # New rules are added at the top. So in the example above, the irregular + # rule for cactus will now be the first of the pluralization and + # singularization rules that is runs. This guarantees that your rules run + # before any of the rules that may already have been loaded. + class Inflections + @__instance__ = Concurrent::Map.new + + class Uncountables < Array + def initialize + @regex_array = [] + super + end + + def delete(entry) + super entry + @regex_array.delete(to_regex(entry)) + end + + def <<(*word) + add(word) + end + + def add(words) + words = words.flatten.map(&:downcase) + concat(words) + @regex_array += words.map { |word| to_regex(word) } + self + end + + def uncountable?(str) + @regex_array.any? { |regex| regex.match? str } + end + + private + def to_regex(string) + /\b#{::Regexp.escape(string)}\Z/i + end + end + + def self.instance(locale = :en) + @__instance__[locale] ||= new + end + + def self.instance_or_fallback(locale) + I18n.fallbacks[locale].each do |k| + return @__instance__[k] if @__instance__.key?(k) + end + instance(locale) + end + + attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms + + attr_reader :acronyms_camelize_regex, :acronyms_underscore_regex # :nodoc: + + def initialize + @plurals, @singulars, @uncountables, @humans, @acronyms = [], [], Uncountables.new, [], {} + define_acronym_regex_patterns + end + + # Private, for the test suite. + def initialize_dup(orig) # :nodoc: + %w(plurals singulars uncountables humans acronyms).each do |scope| + instance_variable_set("@#{scope}", orig.public_send(scope).dup) + end + define_acronym_regex_patterns + end + + # Specifies a new acronym. An acronym must be specified as it will appear + # in a camelized string. An underscore string that contains the acronym + # will retain the acronym when passed to +camelize+, +humanize+, or + # +titleize+. A camelized string that contains the acronym will maintain + # the acronym when titleized or humanized, and will convert the acronym + # into a non-delimited single lowercase word when passed to +underscore+. + # + # acronym 'HTML' + # titleize 'html' # => 'HTML' + # camelize 'html' # => 'HTML' + # underscore 'MyHTML' # => 'my_html' + # + # The acronym, however, must occur as a delimited unit and not be part of + # another word for conversions to recognize it: + # + # acronym 'HTTP' + # camelize 'my_http_delimited' # => 'MyHTTPDelimited' + # camelize 'https' # => 'Https', not 'HTTPs' + # underscore 'HTTPS' # => 'http_s', not 'https' + # + # acronym 'HTTPS' + # camelize 'https' # => 'HTTPS' + # underscore 'HTTPS' # => 'https' + # + # Note: Acronyms that are passed to +pluralize+ will no longer be + # recognized, since the acronym will not occur as a delimited unit in the + # pluralized result. To work around this, you must specify the pluralized + # form as an acronym as well: + # + # acronym 'API' + # camelize(pluralize('api')) # => 'Apis' + # + # acronym 'APIs' + # camelize(pluralize('api')) # => 'APIs' + # + # +acronym+ may be used to specify any word that contains an acronym or + # otherwise needs to maintain a non-standard capitalization. The only + # restriction is that the word must begin with a capital letter. + # + # acronym 'RESTful' + # underscore 'RESTful' # => 'restful' + # underscore 'RESTfulController' # => 'restful_controller' + # titleize 'RESTfulController' # => 'RESTful Controller' + # camelize 'restful' # => 'RESTful' + # camelize 'restful_controller' # => 'RESTfulController' + # + # acronym 'McDonald' + # underscore 'McDonald' # => 'mcdonald' + # camelize 'mcdonald' # => 'McDonald' + def acronym(word) + @acronyms[word.downcase] = word + define_acronym_regex_patterns + end + + # Specifies a new pluralization rule and its replacement. The rule can + # either be a string or a regular expression. The replacement should + # always be a string that may include references to the matched data from + # the rule. + def plural(rule, replacement) + @uncountables.delete(rule) if rule.is_a?(String) + @uncountables.delete(replacement) + @plurals.prepend([rule, replacement]) + end + + # Specifies a new singularization rule and its replacement. The rule can + # either be a string or a regular expression. The replacement should + # always be a string that may include references to the matched data from + # the rule. + def singular(rule, replacement) + @uncountables.delete(rule) if rule.is_a?(String) + @uncountables.delete(replacement) + @singulars.prepend([rule, replacement]) + end + + # Specifies a new irregular that applies to both pluralization and + # singularization at the same time. This can only be used for strings, not + # regular expressions. You simply pass the irregular in singular and + # plural form. + # + # irregular 'cactus', 'cacti' + # irregular 'person', 'people' + def irregular(singular, plural) + @uncountables.delete(singular) + @uncountables.delete(plural) + + s0 = singular[0] + srest = singular[1..-1] + + p0 = plural[0] + prest = plural[1..-1] + + if s0.upcase == p0.upcase + plural(/(#{s0})#{srest}$/i, '\1' + prest) + plural(/(#{p0})#{prest}$/i, '\1' + prest) + + singular(/(#{s0})#{srest}$/i, '\1' + srest) + singular(/(#{p0})#{prest}$/i, '\1' + srest) + else + plural(/#{s0.upcase}(?i)#{srest}$/, p0.upcase + prest) + plural(/#{s0.downcase}(?i)#{srest}$/, p0.downcase + prest) + plural(/#{p0.upcase}(?i)#{prest}$/, p0.upcase + prest) + plural(/#{p0.downcase}(?i)#{prest}$/, p0.downcase + prest) + + singular(/#{s0.upcase}(?i)#{srest}$/, s0.upcase + srest) + singular(/#{s0.downcase}(?i)#{srest}$/, s0.downcase + srest) + singular(/#{p0.upcase}(?i)#{prest}$/, s0.upcase + srest) + singular(/#{p0.downcase}(?i)#{prest}$/, s0.downcase + srest) + end + end + + # Specifies words that are uncountable and should not be inflected. + # + # uncountable 'money' + # uncountable 'money', 'information' + # uncountable %w( money information rice ) + def uncountable(*words) + @uncountables.add(words) + end + + # Specifies a humanized form of a string by a regular expression rule or + # by a string mapping. When using a regular expression based replacement, + # the normal humanize formatting is called after the replacement. When a + # string is used, the human form should be specified as desired (example: + # 'The name', not 'the_name'). + # + # human /_cnt$/i, '\1_count' + # human 'legacy_col_person_name', 'Name' + def human(rule, replacement) + @humans.prepend([rule, replacement]) + end + + # Clears the loaded inflections within a given scope (default is + # :all). Give the scope as a symbol of the inflection type, the + # options are: :plurals, :singulars, :uncountables, + # :humans, :acronyms. + # + # clear :all + # clear :plurals + def clear(scope = :all) + case scope + when :all + clear(:acronyms) + clear(:plurals) + clear(:singulars) + clear(:uncountables) + clear(:humans) + when :acronyms + @acronyms = {} + define_acronym_regex_patterns + when :uncountables + @uncountables = Uncountables.new + when :plurals, :singulars, :humans + instance_variable_set "@#{scope}", [] + end + end + + private + def define_acronym_regex_patterns + @acronym_regex = @acronyms.empty? ? /(?=a)b/ : /#{@acronyms.values.join("|")}/ + @acronyms_camelize_regex = /^(?:#{@acronym_regex}(?=\b|[A-Z_])|\w)/ + @acronyms_underscore_regex = /(?:(?<=([A-Za-z\d]))|\b)(#{@acronym_regex})(?=\b|[^a-z])/ + end + end + + # Yields a singleton instance of Inflector::Inflections so you can specify + # additional inflector rules. If passed an optional locale, rules for other + # languages can be specified. If not specified, defaults to :en. + # Only rules for English are provided. + # + # ActiveSupport::Inflector.inflections(:en) do |inflect| + # inflect.uncountable 'rails' + # end + def inflections(locale = :en) + if block_given? + yield Inflections.instance(locale) + else + Inflections.instance_or_fallback(locale) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/inflector/methods.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/inflector/methods.rb new file mode 100644 index 0000000..68a00d7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/inflector/methods.rb @@ -0,0 +1,377 @@ +# frozen_string_literal: true + +require "active_support/inflections" +require "active_support/core_ext/object/blank" + +module ActiveSupport + # The Inflector transforms words from singular to plural, class names to table + # names, modularized class names to ones without, and class names to foreign + # keys. The default inflections for pluralization, singularization, and + # uncountable words are kept in inflections.rb. + # + # The Rails core team has stated patches for the inflections library will not + # be accepted in order to avoid breaking legacy applications which may be + # relying on errant inflections. If you discover an incorrect inflection and + # require it for your application or wish to define rules for languages other + # than English, please correct or add them yourself (explained below). + module Inflector + extend self + + # Returns the plural form of the word in the string. + # + # If passed an optional +locale+ parameter, the word will be + # pluralized using rules defined for that language. By default, + # this parameter is set to :en. + # + # pluralize('post') # => "posts" + # pluralize('octopus') # => "octopi" + # pluralize('sheep') # => "sheep" + # pluralize('words') # => "words" + # pluralize('CamelOctopus') # => "CamelOctopi" + # pluralize('ley', :es) # => "leyes" + def pluralize(word, locale = :en) + apply_inflections(word, inflections(locale).plurals, locale) + end + + # The reverse of #pluralize, returns the singular form of a word in a + # string. + # + # If passed an optional +locale+ parameter, the word will be + # singularized using rules defined for that language. By default, + # this parameter is set to :en. + # + # singularize('posts') # => "post" + # singularize('octopi') # => "octopus" + # singularize('sheep') # => "sheep" + # singularize('word') # => "word" + # singularize('CamelOctopi') # => "CamelOctopus" + # singularize('leyes', :es) # => "ley" + def singularize(word, locale = :en) + apply_inflections(word, inflections(locale).singulars, locale) + end + + # Converts strings to UpperCamelCase. + # If the +uppercase_first_letter+ parameter is set to false, then produces + # lowerCamelCase. + # + # Also converts '/' to '::' which is useful for converting + # paths to namespaces. + # + # camelize('active_model') # => "ActiveModel" + # camelize('active_model', false) # => "activeModel" + # camelize('active_model/errors') # => "ActiveModel::Errors" + # camelize('active_model/errors', false) # => "activeModel::Errors" + # + # As a rule of thumb you can think of +camelize+ as the inverse of + # #underscore, though there are cases where that does not hold: + # + # camelize(underscore('SSLError')) # => "SslError" + def camelize(term, uppercase_first_letter = true) + string = term.to_s + # String#camelize takes a symbol (:upper or :lower), so here we also support :lower to keep the methods consistent. + if !uppercase_first_letter || uppercase_first_letter == :lower + string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase! || match } + else + string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize! || match } + end + string.gsub!(/(?:_|(\/))([a-z\d]*)/i) do + word = $2 + substituted = inflections.acronyms[word] || word.capitalize! || word + $1 ? "::#{substituted}" : substituted + end + string + end + + # Makes an underscored, lowercase form from the expression in the string. + # + # Changes '::' to '/' to convert namespaces to paths. + # + # underscore('ActiveModel') # => "active_model" + # underscore('ActiveModel::Errors') # => "active_model/errors" + # + # As a rule of thumb you can think of +underscore+ as the inverse of + # #camelize, though there are cases where that does not hold: + # + # camelize(underscore('SSLError')) # => "SslError" + def underscore(camel_cased_word) + return camel_cased_word.to_s unless /[A-Z-]|::/.match?(camel_cased_word) + word = camel_cased_word.to_s.gsub("::", "/") + word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_' }#{$2.downcase}" } + word.gsub!(/([A-Z]+)(?=[A-Z][a-z])|([a-z\d])(?=[A-Z])/) { ($1 || $2) << "_" } + word.tr!("-", "_") + word.downcase! + word + end + + # Tweaks an attribute name for display to end users. + # + # Specifically, performs these transformations: + # + # * Applies human inflection rules to the argument. + # * Deletes leading underscores, if any. + # * Removes an "_id" suffix if present. + # * Replaces underscores with spaces, if any. + # * Downcases all words except acronyms. + # * Capitalizes the first word. + # The capitalization of the first word can be turned off by setting the + # +:capitalize+ option to false (default is true). + # + # The trailing '_id' can be kept and capitalized by setting the + # optional parameter +keep_id_suffix+ to true (default is false). + # + # humanize('employee_salary') # => "Employee salary" + # humanize('author_id') # => "Author" + # humanize('author_id', capitalize: false) # => "author" + # humanize('_id') # => "Id" + # humanize('author_id', keep_id_suffix: true) # => "Author id" + # + # If "SSL" was defined to be an acronym: + # + # humanize('ssl_error') # => "SSL error" + # + def humanize(lower_case_and_underscored_word, capitalize: true, keep_id_suffix: false) + result = lower_case_and_underscored_word.to_s.dup + + inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) } + + result.tr!("_", " ") + result.lstrip! + unless keep_id_suffix + result.delete_suffix!(" id") + end + + result.gsub!(/([a-z\d]+)/i) do |match| + match.downcase! + inflections.acronyms[match] || match + end + + if capitalize + result.sub!(/\A\w/) do |match| + match.upcase! + match + end + end + + result + end + + # Converts just the first character to uppercase. + # + # upcase_first('what a Lovely Day') # => "What a Lovely Day" + # upcase_first('w') # => "W" + # upcase_first('') # => "" + def upcase_first(string) + string.length > 0 ? string[0].upcase.concat(string[1..-1]) : "" + end + + # Capitalizes all the words and replaces some characters in the string to + # create a nicer looking title. +titleize+ is meant for creating pretty + # output. It is not used in the Rails internals. + # + # The trailing '_id','Id'.. can be kept and capitalized by setting the + # optional parameter +keep_id_suffix+ to true. + # By default, this parameter is false. + # + # +titleize+ is also aliased as +titlecase+. + # + # titleize('man from the boondocks') # => "Man From The Boondocks" + # titleize('x-men: the last stand') # => "X Men: The Last Stand" + # titleize('TheManWithoutAPast') # => "The Man Without A Past" + # titleize('raiders_of_the_lost_ark') # => "Raiders Of The Lost Ark" + # titleize('string_ending_with_id', keep_id_suffix: true) # => "String Ending With Id" + def titleize(word, keep_id_suffix: false) + humanize(underscore(word), keep_id_suffix: keep_id_suffix).gsub(/\b(? "raw_scaled_scorers" + # tableize('ham_and_egg') # => "ham_and_eggs" + # tableize('fancyCategory') # => "fancy_categories" + def tableize(class_name) + pluralize(underscore(class_name)) + end + + # Creates a class name from a plural table name like Rails does for table + # names to models. Note that this returns a string and not a Class (To + # convert to an actual class follow +classify+ with #constantize). + # + # classify('ham_and_eggs') # => "HamAndEgg" + # classify('posts') # => "Post" + # + # Singular names are not handled correctly: + # + # classify('calculus') # => "Calculu" + def classify(table_name) + # strip out any leading schema name + camelize(singularize(table_name.to_s.sub(/.*\./, ""))) + end + + # Replaces underscores with dashes in the string. + # + # dasherize('puni_puni') # => "puni-puni" + def dasherize(underscored_word) + underscored_word.tr("_", "-") + end + + # Removes the module part from the expression in the string. + # + # demodulize('ActiveSupport::Inflector::Inflections') # => "Inflections" + # demodulize('Inflections') # => "Inflections" + # demodulize('::Inflections') # => "Inflections" + # demodulize('') # => "" + # + # See also #deconstantize. + def demodulize(path) + path = path.to_s + if i = path.rindex("::") + path[(i + 2)..-1] + else + path + end + end + + # Removes the rightmost segment from the constant expression in the string. + # + # deconstantize('Net::HTTP') # => "Net" + # deconstantize('::Net::HTTP') # => "::Net" + # deconstantize('String') # => "" + # deconstantize('::String') # => "" + # deconstantize('') # => "" + # + # See also #demodulize. + def deconstantize(path) + path.to_s[0, path.rindex("::") || 0] # implementation based on the one in facets' Module#spacename + end + + # Creates a foreign key name from a class name. + # +separate_class_name_and_id_with_underscore+ sets whether + # the method should put '_' between the name and 'id'. + # + # foreign_key('Message') # => "message_id" + # foreign_key('Message', false) # => "messageid" + # foreign_key('Admin::Post') # => "post_id" + def foreign_key(class_name, separate_class_name_and_id_with_underscore = true) + underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id") + end + + # Tries to find a constant with the name specified in the argument string. + # + # constantize('Module') # => Module + # constantize('Foo::Bar') # => Foo::Bar + # + # The name is assumed to be the one of a top-level constant, no matter + # whether it starts with "::" or not. No lexical context is taken into + # account: + # + # C = 'outside' + # module M + # C = 'inside' + # C # => 'inside' + # constantize('C') # => 'outside', same as ::C + # end + # + # NameError is raised when the name is not in CamelCase or the constant is + # unknown. + def constantize(camel_cased_word) + Object.const_get(camel_cased_word) + end + + # Tries to find a constant with the name specified in the argument string. + # + # safe_constantize('Module') # => Module + # safe_constantize('Foo::Bar') # => Foo::Bar + # + # The name is assumed to be the one of a top-level constant, no matter + # whether it starts with "::" or not. No lexical context is taken into + # account: + # + # C = 'outside' + # module M + # C = 'inside' + # C # => 'inside' + # safe_constantize('C') # => 'outside', same as ::C + # end + # + # +nil+ is returned when the name is not in CamelCase or the constant (or + # part of it) is unknown. + # + # safe_constantize('blargle') # => nil + # safe_constantize('UnknownModule') # => nil + # safe_constantize('UnknownModule::Foo::Bar') # => nil + def safe_constantize(camel_cased_word) + constantize(camel_cased_word) + rescue NameError => e + raise if e.name && !(camel_cased_word.to_s.split("::").include?(e.name.to_s) || + e.name.to_s == camel_cased_word.to_s) + rescue LoadError => e + message = e.respond_to?(:original_message) ? e.original_message : e.message + raise unless /Unable to autoload constant #{const_regexp(camel_cased_word)}/.match?(message) + end + + # Returns the suffix that should be added to a number to denote the position + # in an ordered sequence such as 1st, 2nd, 3rd, 4th. + # + # ordinal(1) # => "st" + # ordinal(2) # => "nd" + # ordinal(1002) # => "nd" + # ordinal(1003) # => "rd" + # ordinal(-11) # => "th" + # ordinal(-1021) # => "st" + def ordinal(number) + I18n.translate("number.nth.ordinals", number: number) + end + + # Turns a number into an ordinal string used to denote the position in an + # ordered sequence such as 1st, 2nd, 3rd, 4th. + # + # ordinalize(1) # => "1st" + # ordinalize(2) # => "2nd" + # ordinalize(1002) # => "1002nd" + # ordinalize(1003) # => "1003rd" + # ordinalize(-11) # => "-11th" + # ordinalize(-1021) # => "-1021st" + def ordinalize(number) + I18n.translate("number.nth.ordinalized", number: number) + end + + private + # Mounts a regular expression, returned as a string to ease interpolation, + # that will match part by part the given constant. + # + # const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?" + # const_regexp("::") # => "::" + def const_regexp(camel_cased_word) + parts = camel_cased_word.split("::") + + return Regexp.escape(camel_cased_word) if parts.blank? + + last = parts.pop + + parts.reverse!.inject(last) do |acc, part| + part.empty? ? acc : "#{part}(::#{acc})?" + end + end + + # Applies inflection rules for +singularize+ and +pluralize+. + # + # If passed an optional +locale+ parameter, the uncountables will be + # found for that locale. + # + # apply_inflections('post', inflections.plurals, :en) # => "posts" + # apply_inflections('posts', inflections.singulars, :en) # => "post" + def apply_inflections(word, rules, locale = :en) + result = word.to_s.dup + + if word.empty? || inflections(locale).uncountables.uncountable?(result) + result + else + rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) } + result + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/inflector/transliterate.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/inflector/transliterate.rb new file mode 100644 index 0000000..e2a4189 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/inflector/transliterate.rb @@ -0,0 +1,147 @@ +# frozen_string_literal: true + +require "active_support/core_ext/string/multibyte" +require "active_support/i18n" + +module ActiveSupport + module Inflector + ALLOWED_ENCODINGS_FOR_TRANSLITERATE = [Encoding::UTF_8, Encoding::US_ASCII, Encoding::GB18030].freeze + + # Replaces non-ASCII characters with an ASCII approximation, or if none + # exists, a replacement character which defaults to "?". + # + # transliterate('Ærøskøbing') + # # => "AEroskobing" + # + # Default approximations are provided for Western/Latin characters, + # e.g, "ø", "ñ", "é", "ß", etc. + # + # This method is I18n aware, so you can set up custom approximations for a + # locale. This can be useful, for example, to transliterate German's "ü" + # and "ö" to "ue" and "oe", or to add support for transliterating Russian + # to ASCII. + # + # In order to make your custom transliterations available, you must set + # them as the i18n.transliterate.rule i18n key: + # + # # Store the transliterations in locales/de.yml + # i18n: + # transliterate: + # rule: + # ü: "ue" + # ö: "oe" + # + # # Or set them using Ruby + # I18n.backend.store_translations(:de, i18n: { + # transliterate: { + # rule: { + # 'ü' => 'ue', + # 'ö' => 'oe' + # } + # } + # }) + # + # The value for i18n.transliterate.rule can be a simple Hash that + # maps characters to ASCII approximations as shown above, or, for more + # complex requirements, a Proc: + # + # I18n.backend.store_translations(:de, i18n: { + # transliterate: { + # rule: ->(string) { MyTransliterator.transliterate(string) } + # } + # }) + # + # Now you can have different transliterations for each locale: + # + # transliterate('Jürgen', locale: :en) + # # => "Jurgen" + # + # transliterate('Jürgen', locale: :de) + # # => "Juergen" + # + # Transliteration is restricted to UTF-8, US-ASCII, and GB18030 strings. + # Other encodings will raise an ArgumentError. + def transliterate(string, replacement = "?", locale: nil) + string = string.dup if string.frozen? + raise ArgumentError, "Can only transliterate strings. Received #{string.class.name}" unless string.is_a?(String) + raise ArgumentError, "Cannot transliterate strings with #{string.encoding} encoding" unless ALLOWED_ENCODINGS_FOR_TRANSLITERATE.include?(string.encoding) + + input_encoding = string.encoding + + # US-ASCII is a subset of UTF-8 so we'll force encoding as UTF-8 if + # US-ASCII is given. This way we can let tidy_bytes handle the string + # in the same way as we do for UTF-8 + string.force_encoding(Encoding::UTF_8) if string.encoding == Encoding::US_ASCII + + # GB18030 is Unicode compatible but is not a direct mapping so needs to be + # transcoded. Using invalid/undef :replace will result in loss of data in + # the event of invalid characters, but since tidy_bytes will replace + # invalid/undef with a "?" we're safe to do the same beforehand + string.encode!(Encoding::UTF_8, invalid: :replace, undef: :replace) if string.encoding == Encoding::GB18030 + + transliterated = I18n.transliterate( + ActiveSupport::Multibyte::Unicode.tidy_bytes(string).unicode_normalize(:nfc), + replacement: replacement, + locale: locale + ) + + # Restore the string encoding of the input if it was not UTF-8. + # Apply invalid/undef :replace as tidy_bytes does + transliterated.encode!(input_encoding, invalid: :replace, undef: :replace) if input_encoding != transliterated.encoding + + transliterated + end + + # Replaces special characters in a string so that it may be used as part of + # a 'pretty' URL. + # + # parameterize("Donald E. Knuth") # => "donald-e-knuth" + # parameterize("^très|Jolie-- ") # => "tres-jolie" + # + # To use a custom separator, override the +separator+ argument. + # + # parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth" + # parameterize("^très|Jolie__ ", separator: '_') # => "tres_jolie" + # + # To preserve the case of the characters in a string, use the +preserve_case+ argument. + # + # parameterize("Donald E. Knuth", preserve_case: true) # => "Donald-E-Knuth" + # parameterize("^très|Jolie-- ", preserve_case: true) # => "tres-Jolie" + # + # It preserves dashes and underscores unless they are used as separators: + # + # parameterize("^très|Jolie__ ") # => "tres-jolie__" + # parameterize("^très|Jolie-- ", separator: "_") # => "tres_jolie--" + # parameterize("^très_Jolie-- ", separator: ".") # => "tres_jolie--" + # + # If the optional parameter +locale+ is specified, + # the word will be parameterized as a word of that language. + # By default, this parameter is set to nil and it will use + # the configured I18n.locale. + def parameterize(string, separator: "-", preserve_case: false, locale: nil) + # Replace accented chars with their ASCII equivalents. + parameterized_string = transliterate(string, locale: locale) + + # Turn unwanted chars into the separator. + parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator) + + unless separator.nil? || separator.empty? + if separator == "-" + re_duplicate_separator = /-{2,}/ + re_leading_trailing_separator = /^-|-$/i + else + re_sep = Regexp.escape(separator) + re_duplicate_separator = /#{re_sep}{2,}/ + re_leading_trailing_separator = /^#{re_sep}|#{re_sep}$/i + end + # No more than one of the separator in a row. + parameterized_string.gsub!(re_duplicate_separator, separator) + # Remove leading/trailing separator. + parameterized_string.gsub!(re_leading_trailing_separator, "") + end + + parameterized_string.downcase! unless preserve_case + parameterized_string + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/isolated_execution_state.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/isolated_execution_state.rb new file mode 100644 index 0000000..2b8b06f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/isolated_execution_state.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require "fiber" + +module ActiveSupport + module IsolatedExecutionState # :nodoc: + @isolation_level = :thread + + Thread.attr_accessor :active_support_execution_state + Fiber.attr_accessor :active_support_execution_state + + class << self + attr_reader :isolation_level + + def isolation_level=(level) + unless %i(thread fiber).include?(level) + raise ArgumentError, "isolation_level must be `:thread` or `:fiber`, got: `#{level.inspect}`" + end + + if level != isolation_level + clear + singleton_class.alias_method(:current, "current_#{level}") + singleton_class.send(:private, :current) + @isolation_level = level + end + end + + def unique_id + self[:__id__] ||= Object.new + end + + def [](key) + current[key] + end + + def []=(key, value) + current[key] = value + end + + def key?(key) + current.key?(key) + end + + def delete(key) + current.delete(key) + end + + def clear + current.clear + end + + def share_with(other) + # Action Controller streaming spawns a new thread and copy thread locals. + # We do the same here for backward compatibility, but this is very much a hack + # and streaming should be rethought. + context = @isolation_level == :thread ? Thread.current : Fiber.current + context.active_support_execution_state = other.active_support_execution_state.dup + end + + private + def current_thread + Thread.current.active_support_execution_state ||= {} + end + + def current_fiber + Fiber.current.active_support_execution_state ||= {} + end + + alias_method :current, :current_thread + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/json.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/json.rb new file mode 100644 index 0000000..d788717 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/json.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +require "active_support/json/decoding" +require "active_support/json/encoding" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/json/decoding.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/json/decoding.rb new file mode 100644 index 0000000..e40957e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/json/decoding.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +require "active_support/core_ext/module/attribute_accessors" +require "active_support/core_ext/module/delegation" +require "json" + +module ActiveSupport + # Look for and parse json strings that look like ISO 8601 times. + mattr_accessor :parse_json_times + + module JSON + # matches YAML-formatted dates + DATE_REGEX = /\A\d{4}-\d{2}-\d{2}\z/ + DATETIME_REGEX = /\A(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)?)\z/ + + class << self + # Parses a JSON string (JavaScript Object Notation) into a hash. + # See http://www.json.org for more info. + # + # ActiveSupport::JSON.decode("{\"team\":\"rails\",\"players\":\"36\"}") + # => {"team" => "rails", "players" => "36"} + def decode(json) + data = ::JSON.parse(json, quirks_mode: true) + + if ActiveSupport.parse_json_times + convert_dates_from(data) + else + data + end + end + + # Returns the class of the error that will be raised when there is an + # error in decoding JSON. Using this method means you won't directly + # depend on the ActiveSupport's JSON implementation, in case it changes + # in the future. + # + # begin + # obj = ActiveSupport::JSON.decode(some_string) + # rescue ActiveSupport::JSON.parse_error + # Rails.logger.warn("Attempted to decode invalid JSON: #{some_string}") + # end + def parse_error + ::JSON::ParserError + end + + private + def convert_dates_from(data) + case data + when nil + nil + when DATE_REGEX + begin + Date.parse(data) + rescue ArgumentError + data + end + when DATETIME_REGEX + begin + Time.zone.parse(data) + rescue ArgumentError + data + end + when Array + data.map! { |d| convert_dates_from(d) } + when Hash + data.transform_values! do |value| + convert_dates_from(value) + end + else + data + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/json/encoding.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/json/encoding.rb new file mode 100644 index 0000000..8e08b24 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/json/encoding.rb @@ -0,0 +1,138 @@ +# frozen_string_literal: true + +require "active_support/core_ext/object/json" +require "active_support/core_ext/module/delegation" + +module ActiveSupport + class << self + delegate :use_standard_json_time_format, :use_standard_json_time_format=, + :time_precision, :time_precision=, + :escape_html_entities_in_json, :escape_html_entities_in_json=, + :json_encoder, :json_encoder=, + to: :'ActiveSupport::JSON::Encoding' + end + + module JSON + # Dumps objects in JSON (JavaScript Object Notation). + # See http://www.json.org for more info. + # + # ActiveSupport::JSON.encode({ team: 'rails', players: '36' }) + # # => "{\"team\":\"rails\",\"players\":\"36\"}" + def self.encode(value, options = nil) + Encoding.json_encoder.new(options).encode(value) + end + + module Encoding # :nodoc: + class JSONGemEncoder # :nodoc: + attr_reader :options + + def initialize(options = nil) + @options = options || {} + end + + # Encode the given object into a JSON string + def encode(value) + stringify jsonify value.as_json(options.dup) + end + + private + # Rails does more escaping than the JSON gem natively does (we + # escape \u2028 and \u2029 and optionally >, <, & to work around + # certain browser problems). + ESCAPED_CHARS = { + "\u2028" => '\u2028', + "\u2029" => '\u2029', + ">" => '\u003e', + "<" => '\u003c', + "&" => '\u0026', + } + + ESCAPE_REGEX_WITH_HTML_ENTITIES = /[\u2028\u2029><&]/u + ESCAPE_REGEX_WITHOUT_HTML_ENTITIES = /[\u2028\u2029]/u + + # This class wraps all the strings we see and does the extra escaping + class EscapedString < String # :nodoc: + def to_json(*) + if Encoding.escape_html_entities_in_json + s = super + s.gsub! ESCAPE_REGEX_WITH_HTML_ENTITIES, ESCAPED_CHARS + s + else + s = super + s.gsub! ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS + s + end + end + + def to_s + self + end + end + + # Mark these as private so we don't leak encoding-specific constructs + private_constant :ESCAPED_CHARS, :ESCAPE_REGEX_WITH_HTML_ENTITIES, + :ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, :EscapedString + + # Convert an object into a "JSON-ready" representation composed of + # primitives like Hash, Array, String, Numeric, + # and +true+/+false+/+nil+. + # Recursively calls #as_json to the object to recursively build a + # fully JSON-ready object. + # + # This allows developers to implement #as_json without having to + # worry about what base types of objects they are allowed to return + # or having to remember to call #as_json recursively. + # + # Note: the +options+ hash passed to +object.to_json+ is only passed + # to +object.as_json+, not any of this method's recursive +#as_json+ + # calls. + def jsonify(value) + case value + when String + EscapedString.new(value) + when Numeric, NilClass, TrueClass, FalseClass + value.as_json + when Hash + result = {} + value.each do |k, v| + result[jsonify(k)] = jsonify(v) + end + result + when Array + value.map { |v| jsonify(v) } + else + jsonify value.as_json + end + end + + # Encode a "jsonified" Ruby data structure using the JSON gem + def stringify(jsonified) + ::JSON.generate(jsonified, quirks_mode: true, max_nesting: false) + end + end + + class << self + # If true, use ISO 8601 format for dates and times. Otherwise, fall back + # to the Active Support legacy format. + attr_accessor :use_standard_json_time_format + + # If true, encode >, <, & as escaped unicode sequences (e.g. > as \u003e) + # as a safety measure. + attr_accessor :escape_html_entities_in_json + + # Sets the precision of encoded time values. + # Defaults to 3 (equivalent to millisecond precision) + attr_accessor :time_precision + + # Sets the encoder used by Rails to encode Ruby objects into JSON strings + # in +Object#to_json+ and +ActiveSupport::JSON.encode+. + attr_accessor :json_encoder + end + + self.use_standard_json_time_format = true + self.escape_html_entities_in_json = true + self.json_encoder = JSONGemEncoder + self.time_precision = 3 + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/key_generator.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/key_generator.rb new file mode 100644 index 0000000..660bffd --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/key_generator.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require "concurrent/map" +require "openssl" + +module ActiveSupport + # KeyGenerator is a simple wrapper around OpenSSL's implementation of PBKDF2. + # It can be used to derive a number of keys for various purposes from a given secret. + # This lets Rails applications have a single secure secret, but avoid reusing that + # key in multiple incompatible contexts. + class KeyGenerator + class << self + def hash_digest_class=(klass) + if klass.kind_of?(Class) && klass < OpenSSL::Digest + @hash_digest_class = klass + else + raise ArgumentError, "#{klass} is expected to be an OpenSSL::Digest subclass" + end + end + + def hash_digest_class + @hash_digest_class ||= OpenSSL::Digest::SHA1 + end + end + + def initialize(secret, options = {}) + @secret = secret + # The default iterations are higher than required for our key derivation uses + # on the off chance someone uses this for password storage + @iterations = options[:iterations] || 2**16 + # Also allow configuration here so people can use this to build a rotation + # scheme when switching the digest class. + @hash_digest_class = options[:hash_digest_class] || self.class.hash_digest_class + end + + # Returns a derived key suitable for use. The default +key_size+ is chosen + # to be compatible with the default settings of ActiveSupport::MessageVerifier. + # i.e. OpenSSL::Digest::SHA1#block_length + def generate_key(salt, key_size = 64) + OpenSSL::PKCS5.pbkdf2_hmac(@secret, salt, @iterations, key_size, @hash_digest_class.new) + end + end + + # CachingKeyGenerator is a wrapper around KeyGenerator which allows users to avoid + # re-executing the key generation process when it's called using the same +salt+ and + # +key_size+. + class CachingKeyGenerator + def initialize(key_generator) + @key_generator = key_generator + @cache_keys = Concurrent::Map.new + end + + # Returns a derived key suitable for use. + def generate_key(*args) + @cache_keys[args.join("|")] ||= @key_generator.generate_key(*args) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/lazy_load_hooks.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/lazy_load_hooks.rb new file mode 100644 index 0000000..8ab6cf7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/lazy_load_hooks.rb @@ -0,0 +1,105 @@ +# frozen_string_literal: true + +module ActiveSupport + # LazyLoadHooks allows Rails to lazily load a lot of components and thus + # making the app boot faster. Because of this feature now there is no need to + # require ActiveRecord::Base at boot time purely to apply + # configuration. Instead a hook is registered that applies configuration once + # ActiveRecord::Base is loaded. Here ActiveRecord::Base is + # used as example but this feature can be applied elsewhere too. + # + # Here is an example where on_load method is called to register a hook. + # + # initializer 'active_record.initialize_timezone' do + # ActiveSupport.on_load(:active_record) do + # self.time_zone_aware_attributes = true + # self.default_timezone = :utc + # end + # end + # + # When the entirety of +ActiveRecord::Base+ has been + # evaluated then run_load_hooks is invoked. The very last line of + # +ActiveRecord::Base+ is: + # + # ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base) + # + # run_load_hooks will then execute all the hooks that were registered + # with the on_load method. In the case of the above example, it will + # execute the block of code that is in the +initializer+. + # + # Registering a hook that has already run results in that hook executing + # immediately. This allows hooks to be nested for code that relies on + # multiple lazily loaded components: + # + # initializer "action_text.renderer" do + # ActiveSupport.on_load(:action_controller_base) do + # ActiveSupport.on_load(:action_text_content) do + # self.default_renderer = Class.new(ActionController::Base).renderer + # end + # end + # end + module LazyLoadHooks + def self.extended(base) # :nodoc: + base.class_eval do + @load_hooks = Hash.new { |h, k| h[k] = [] } + @loaded = Hash.new { |h, k| h[k] = [] } + @run_once = Hash.new { |h, k| h[k] = [] } + end + end + + # Declares a block that will be executed when a Rails component is fully + # loaded. If the component has already loaded, the block is executed + # immediately. + # + # Options: + # + # * :yield - Yields the object that run_load_hooks to +block+. + # * :run_once - Given +block+ will run only once. + def on_load(name, options = {}, &block) + @loaded[name].each do |base| + execute_hook(name, base, options, block) + end + + @load_hooks[name] << [block, options] + end + + # Executes all blocks registered to +name+ via on_load, using +base+ as the + # evaluation context. + # + # ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base) + # + # In the case of the above example, it will execute all hooks registered + # for +:active_record+ within the class +ActiveRecord::Base+. + def run_load_hooks(name, base = Object) + @loaded[name] << base + @load_hooks[name].each do |hook, options| + execute_hook(name, base, options, hook) + end + end + + private + def with_execution_control(name, block, once) + unless @run_once[name].include?(block) + @run_once[name] << block if once + + yield + end + end + + def execute_hook(name, base, options, block) + with_execution_control(name, block, options[:run_once]) do + if options[:yield] + block.call(base) + else + if base.is_a?(Module) + base.class_eval(&block) + else + base.instance_eval(&block) + end + end + end + end + end + + extend LazyLoadHooks +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/locale/en.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/locale/en.rb new file mode 100644 index 0000000..29eb9de --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/locale/en.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +{ + en: { + number: { + nth: { + ordinals: lambda do |_key, options| + number = options[:number] + case number + when 1; "st" + when 2; "nd" + when 3; "rd" + when 4, 5, 6, 7, 8, 9, 10, 11, 12, 13; "th" + else + num_modulo = number.to_i.abs % 100 + num_modulo %= 10 if num_modulo > 13 + case num_modulo + when 1; "st" + when 2; "nd" + when 3; "rd" + else "th" + end + end + end, + + ordinalized: lambda do |_key, options| + number = options[:number] + "#{number}#{ActiveSupport::Inflector.ordinal(number)}" + end + } + } + } +} diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/locale/en.yml b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/locale/en.yml new file mode 100644 index 0000000..0453883 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/locale/en.yml @@ -0,0 +1,139 @@ +en: + date: + formats: + # Use the strftime parameters for formats. + # When no format has been given, it uses default. + # You can provide other formats here if you like! + default: "%Y-%m-%d" + short: "%b %d" + long: "%B %d, %Y" + + day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday] + abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat] + + # Don't forget the nil at the beginning; there's no such thing as a 0th month + month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December] + abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec] + # Used in date_select and datetime_select. + order: + - year + - month + - day + + time: + formats: + default: "%a, %d %b %Y %H:%M:%S %z" + short: "%d %b %H:%M" + long: "%B %d, %Y %H:%M" + am: "am" + pm: "pm" + +# Used in array.to_sentence. + support: + array: + words_connector: ", " + two_words_connector: " and " + last_word_connector: ", and " + number: + # Used in NumberHelper.number_to_delimited() + # These are also the defaults for 'currency', 'percentage', 'precision', and 'human' + format: + # Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5) + separator: "." + # Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three) + delimiter: "," + # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00) + precision: 3 + # Determine how rounding is performed (see BigDecimal::mode) + round_mode: default + # If set to true, precision will mean the number of significant digits instead + # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2) + significant: false + # If set, the zeros after the decimal separator will always be stripped (e.g.: 1.200 will be 1.2) + strip_insignificant_zeros: false + + # Used in NumberHelper.number_to_currency() + currency: + format: + # Where is the currency sign? %u is the currency unit, %n is the number (default: $5.00) + format: "%u%n" + unit: "$" + # These six are to override number.format and are optional + separator: "." + delimiter: "," + precision: 2 + # round_mode: + significant: false + strip_insignificant_zeros: false + + # Used in NumberHelper.number_to_percentage() + percentage: + format: + # These five are to override number.format and are optional + # separator: + delimiter: "" + # precision: + # significant: false + # strip_insignificant_zeros: false + format: "%n%" + + # Used in NumberHelper.number_to_rounded() + precision: + format: + # These five are to override number.format and are optional + # separator: + delimiter: "" + # precision: + # significant: false + # strip_insignificant_zeros: false + + # Used in NumberHelper.number_to_human_size() and NumberHelper.number_to_human() + human: + format: + # These six are to override number.format and are optional + # separator: + delimiter: "" + precision: 3 + # round_mode: + significant: true + strip_insignificant_zeros: true + # Used in number_to_human_size() + storage_units: + # Storage units output formatting. + # %u is the storage unit, %n is the number (default: 2 MB) + format: "%n %u" + units: + byte: + one: "Byte" + other: "Bytes" + kb: "KB" + mb: "MB" + gb: "GB" + tb: "TB" + pb: "PB" + eb: "EB" + # Used in NumberHelper.number_to_human() + decimal_units: + format: "%n %u" + # Decimal units output formatting + # By default we will only quantify some of the exponents + # but the commented ones might be defined or overridden + # by the user. + units: + # femto: Quadrillionth + # pico: Trillionth + # nano: Billionth + # micro: Millionth + # mili: Thousandth + # centi: Hundredth + # deci: Tenth + unit: "" + # ten: + # one: Ten + # other: Tens + # hundred: Hundred + thousand: Thousand + million: Million + billion: Billion + trillion: Trillion + quadrillion: Quadrillion diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/log_subscriber.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/log_subscriber.rb new file mode 100644 index 0000000..ddeaff0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/log_subscriber.rb @@ -0,0 +1,152 @@ +# frozen_string_literal: true + +require "active_support/core_ext/module/attribute_accessors" +require "active_support/core_ext/class/attribute" +require "active_support/subscriber" + +module ActiveSupport + # ActiveSupport::LogSubscriber is an object set to consume + # ActiveSupport::Notifications with the sole purpose of logging them. + # The log subscriber dispatches notifications to a registered object based + # on its given namespace. + # + # An example would be Active Record log subscriber responsible for logging + # queries: + # + # module ActiveRecord + # class LogSubscriber < ActiveSupport::LogSubscriber + # def sql(event) + # info "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}" + # end + # end + # end + # + # And it's finally registered as: + # + # ActiveRecord::LogSubscriber.attach_to :active_record + # + # Since we need to know all instance methods before attaching the log + # subscriber, the line above should be called after your + # ActiveRecord::LogSubscriber definition. + # + # A logger also needs to be set with ActiveRecord::LogSubscriber.logger=. + # This is assigned automatically in a Rails environment. + # + # After configured, whenever a "sql.active_record" notification is published, + # it will properly dispatch the event + # (ActiveSupport::Notifications::Event) to the sql method. + # + # Being an ActiveSupport::Notifications consumer, + # ActiveSupport::LogSubscriber exposes a simple interface to check if + # instrumented code raises an exception. It is common to log a different + # message in case of an error, and this can be achieved by extending + # the previous example: + # + # module ActiveRecord + # class LogSubscriber < ActiveSupport::LogSubscriber + # def sql(event) + # exception = event.payload[:exception] + # + # if exception + # exception_object = event.payload[:exception_object] + # + # error "[ERROR] #{event.payload[:name]}: #{exception.join(', ')} " \ + # "(#{exception_object.backtrace.first})" + # else + # # standard logger code + # end + # end + # end + # end + # + # Log subscriber also has some helpers to deal with logging and automatically + # flushes all logs when the request finishes + # (via action_dispatch.callback notification) in a Rails environment. + class LogSubscriber < Subscriber + # Embed in a String to clear all previous ANSI sequences. + CLEAR = "\e[0m" + BOLD = "\e[1m" + + # Colors + BLACK = "\e[30m" + RED = "\e[31m" + GREEN = "\e[32m" + YELLOW = "\e[33m" + BLUE = "\e[34m" + MAGENTA = "\e[35m" + CYAN = "\e[36m" + WHITE = "\e[37m" + + mattr_accessor :colorize_logging, default: true + + class << self + def logger + @logger ||= if defined?(Rails) && Rails.respond_to?(:logger) + Rails.logger + end + end + + attr_writer :logger + + def log_subscribers + subscribers + end + + # Flush all log_subscribers' logger. + def flush_all! + logger.flush if logger.respond_to?(:flush) + end + + private + def fetch_public_methods(subscriber, inherit_all) + subscriber.public_methods(inherit_all) - LogSubscriber.public_instance_methods(true) + end + end + + def logger + LogSubscriber.logger + end + + def start(name, id, payload) + super if logger + end + + def finish(name, id, payload) + super if logger + rescue => e + log_exception(name, e) + end + + def publish_event(event) + super if logger + rescue => e + log_exception(event.name, e) + end + + private + %w(info debug warn error fatal unknown).each do |level| + class_eval <<-METHOD, __FILE__, __LINE__ + 1 + def #{level}(progname = nil, &block) + logger.#{level}(progname, &block) if logger + end + METHOD + end + + # Set color by using a symbol or one of the defined constants. If a third + # option is set to +true+, it also adds bold to the string. This is based + # on the Highline implementation and will automatically append CLEAR to the + # end of the returned String. + def color(text, color, bold = false) # :doc: + return text unless colorize_logging + color = self.class.const_get(color.upcase) if color.is_a?(Symbol) + bold = bold ? BOLD : "" + "#{bold}#{color}#{text}#{CLEAR}" + end + + def log_exception(name, e) + if logger + logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}" + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/log_subscriber/test_helper.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/log_subscriber/test_helper.rb new file mode 100644 index 0000000..b528a7f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/log_subscriber/test_helper.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +require "active_support/log_subscriber" +require "active_support/logger" +require "active_support/notifications" + +module ActiveSupport + class LogSubscriber + # Provides some helpers to deal with testing log subscribers by setting up + # notifications. Take for instance Active Record subscriber tests: + # + # class SyncLogSubscriberTest < ActiveSupport::TestCase + # include ActiveSupport::LogSubscriber::TestHelper + # + # setup do + # ActiveRecord::LogSubscriber.attach_to(:active_record) + # end + # + # def test_basic_query_logging + # Developer.all.to_a + # wait + # assert_equal 1, @logger.logged(:debug).size + # assert_match(/Developer Load/, @logger.logged(:debug).last) + # assert_match(/SELECT \* FROM "developers"/, @logger.logged(:debug).last) + # end + # end + # + # All you need to do is to ensure that your log subscriber is added to + # Rails::Subscriber, as in the second line of the code above. The test + # helpers are responsible for setting up the queue and subscriptions, and + # turning colors in logs off. + # + # The messages are available in the @logger instance, which is a logger with + # limited powers (it actually does not send anything to your output), and + # you can collect them doing @logger.logged(level), where level is the level + # used in logging, like info, debug, warn, and so on. + module TestHelper + def setup # :nodoc: + @logger = MockLogger.new + @notifier = ActiveSupport::Notifications::Fanout.new + + ActiveSupport::LogSubscriber.colorize_logging = false + + @old_notifier = ActiveSupport::Notifications.notifier + set_logger(@logger) + ActiveSupport::Notifications.notifier = @notifier + end + + def teardown # :nodoc: + set_logger(nil) + ActiveSupport::Notifications.notifier = @old_notifier + end + + class MockLogger + include ActiveSupport::Logger::Severity + + attr_reader :flush_count + attr_accessor :level + + def initialize(level = DEBUG) + @flush_count = 0 + @level = level + @logged = Hash.new { |h, k| h[k] = [] } + end + + def method_missing(level, message = nil) + if block_given? + @logged[level] << yield + else + @logged[level] << message + end + end + + def logged(level) + @logged[level].compact.map { |l| l.to_s.strip } + end + + def flush + @flush_count += 1 + end + + ActiveSupport::Logger::Severity.constants.each do |severity| + class_eval <<-EOT, __FILE__, __LINE__ + 1 + def #{severity.downcase}? + #{severity} >= @level + end + EOT + end + end + + # Wait notifications to be published. + def wait + @notifier.wait + end + + # Overwrite if you use another logger in your log subscriber. + # + # def logger + # ActiveRecord::Base.logger = @logger + # end + def set_logger(logger) + ActiveSupport::LogSubscriber.logger = logger + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/logger.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/logger.rb new file mode 100644 index 0000000..1e241c1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/logger.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +require "active_support/logger_silence" +require "active_support/logger_thread_safe_level" +require "logger" + +module ActiveSupport + class Logger < ::Logger + include LoggerSilence + + # Returns true if the logger destination matches one of the sources + # + # logger = Logger.new(STDOUT) + # ActiveSupport::Logger.logger_outputs_to?(logger, STDOUT) + # # => true + def self.logger_outputs_to?(logger, *sources) + logdev = logger.instance_variable_get(:@logdev) + logger_source = logdev.dev if logdev.respond_to?(:dev) + sources.any? { |source| source == logger_source } + end + + # Broadcasts logs to multiple loggers. + def self.broadcast(logger) # :nodoc: + Module.new do + define_method(:add) do |*args, &block| + logger.add(*args, &block) + super(*args, &block) + end + + define_method(:<<) do |x| + logger << x + super(x) + end + + define_method(:close) do + logger.close + super() + end + + define_method(:progname=) do |name| + logger.progname = name + super(name) + end + + define_method(:formatter=) do |formatter| + logger.formatter = formatter + super(formatter) + end + + define_method(:level=) do |level| + logger.level = level + super(level) + end + + define_method(:local_level=) do |level| + logger.local_level = level if logger.respond_to?(:local_level=) + super(level) if respond_to?(:local_level=) + end + + define_method(:silence) do |level = Logger::ERROR, &block| + if logger.respond_to?(:silence) + logger.silence(level) do + if defined?(super) + super(level, &block) + else + block.call(self) + end + end + else + if defined?(super) + super(level, &block) + else + block.call(self) + end + end + end + end + end + + def initialize(*args, **kwargs) + super + @formatter = SimpleFormatter.new + end + + # Simple formatter which only displays the message. + class SimpleFormatter < ::Logger::Formatter + # This method is invoked when a log event occurs + def call(severity, timestamp, progname, msg) + "#{String === msg ? msg : msg.inspect}\n" + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/logger_silence.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/logger_silence.rb new file mode 100644 index 0000000..8567eff --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/logger_silence.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require "active_support/concern" +require "active_support/core_ext/module/attribute_accessors" +require "active_support/logger_thread_safe_level" + +module ActiveSupport + module LoggerSilence + extend ActiveSupport::Concern + + included do + cattr_accessor :silencer, default: true + include ActiveSupport::LoggerThreadSafeLevel + end + + # Silences the logger for the duration of the block. + def silence(severity = Logger::ERROR) + silencer ? log_at(severity) { yield self } : yield(self) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/logger_thread_safe_level.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/logger_thread_safe_level.rb new file mode 100644 index 0000000..042f484 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/logger_thread_safe_level.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require "active_support/concern" +require "active_support/core_ext/module/attribute_accessors" +require "concurrent" +require "fiber" + +module ActiveSupport + module LoggerThreadSafeLevel # :nodoc: + extend ActiveSupport::Concern + + Logger::Severity.constants.each do |severity| + class_eval(<<-EOT, __FILE__, __LINE__ + 1) + def #{severity.downcase}? # def debug? + Logger::#{severity} >= level # DEBUG >= level + end # end + EOT + end + + def local_level + IsolatedExecutionState[:logger_thread_safe_level] + end + + def local_level=(level) + case level + when Integer + when Symbol + level = Logger::Severity.const_get(level.to_s.upcase) + when nil + else + raise ArgumentError, "Invalid log level: #{level.inspect}" + end + IsolatedExecutionState[:logger_thread_safe_level] = level + end + + def level + local_level || super + end + + # Change the thread-local level for the duration of the given block. + def log_at(level) + old_local_level, self.local_level = local_level, level + yield + ensure + self.local_level = old_local_level + end + + # Redefined to check severity against #level, and thus the thread-local level, rather than +@level+. + # FIXME: Remove when the minimum Ruby version supports overriding Logger#level. + def add(severity, message = nil, progname = nil, &block) # :nodoc: + severity ||= UNKNOWN + progname ||= @progname + + return true if @logdev.nil? || severity < level + + if message.nil? + if block_given? + message = yield + else + message = progname + progname = @progname + end + end + + @logdev.write \ + format_message(format_severity(severity), Time.now, progname, message) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/message_encryptor.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/message_encryptor.rb new file mode 100644 index 0000000..6528d1e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/message_encryptor.rb @@ -0,0 +1,230 @@ +# frozen_string_literal: true + +require "openssl" +require "base64" +require "active_support/core_ext/module/attribute_accessors" +require "active_support/message_verifier" +require "active_support/messages/metadata" + +module ActiveSupport + # MessageEncryptor is a simple way to encrypt values which get stored + # somewhere you don't trust. + # + # The cipher text and initialization vector are base64 encoded and returned + # to you. + # + # This can be used in situations similar to the MessageVerifier, but + # where you don't want users to be able to determine the value of the payload. + # + # len = ActiveSupport::MessageEncryptor.key_len + # salt = SecureRandom.random_bytes(len) + # key = ActiveSupport::KeyGenerator.new('password').generate_key(salt, len) # => "\x89\xE0\x156\xAC..." + # crypt = ActiveSupport::MessageEncryptor.new(key) # => # + # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..." + # crypt.decrypt_and_verify(encrypted_data) # => "my secret data" + # + # The +decrypt_and_verify+ method will raise an + # ActiveSupport::MessageEncryptor::InvalidMessage exception if the data + # provided cannot be decrypted or verified. + # + # crypt.decrypt_and_verify('not encrypted data') # => ActiveSupport::MessageEncryptor::InvalidMessage + # + # === Confining messages to a specific purpose + # + # By default any message can be used throughout your app. But they can also be + # confined to a specific +:purpose+. + # + # token = crypt.encrypt_and_sign("this is the chair", purpose: :login) + # + # Then that same purpose must be passed when verifying to get the data back out: + # + # crypt.decrypt_and_verify(token, purpose: :login) # => "this is the chair" + # crypt.decrypt_and_verify(token, purpose: :shipping) # => nil + # crypt.decrypt_and_verify(token) # => nil + # + # Likewise, if a message has no purpose it won't be returned when verifying with + # a specific purpose. + # + # token = crypt.encrypt_and_sign("the conversation is lively") + # crypt.decrypt_and_verify(token, purpose: :scare_tactics) # => nil + # crypt.decrypt_and_verify(token) # => "the conversation is lively" + # + # === Making messages expire + # + # By default messages last forever and verifying one year from now will still + # return the original value. But messages can be set to expire at a given + # time with +:expires_in+ or +:expires_at+. + # + # crypt.encrypt_and_sign(parcel, expires_in: 1.month) + # crypt.encrypt_and_sign(doowad, expires_at: Time.now.end_of_year) + # + # Then the messages can be verified and returned up to the expire time. + # Thereafter, verifying returns +nil+. + # + # === Rotating keys + # + # MessageEncryptor also supports rotating out old configurations by falling + # back to a stack of encryptors. Call +rotate+ to build and add an encryptor + # so +decrypt_and_verify+ will also try the fallback. + # + # By default any rotated encryptors use the values of the primary + # encryptor unless specified otherwise. + # + # You'd give your encryptor the new defaults: + # + # crypt = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm") + # + # Then gradually rotate the old values out by adding them as fallbacks. Any message + # generated with the old values will then work until the rotation is removed. + # + # crypt.rotate old_secret # Fallback to an old secret instead of @secret. + # crypt.rotate cipher: "aes-256-cbc" # Fallback to an old cipher instead of aes-256-gcm. + # + # Though if both the secret and the cipher was changed at the same time, + # the above should be combined into: + # + # crypt.rotate old_secret, cipher: "aes-256-cbc" + class MessageEncryptor + prepend Messages::Rotator::Encryptor + + cattr_accessor :use_authenticated_message_encryption, instance_accessor: false, default: false + + class << self + def default_cipher # :nodoc: + if use_authenticated_message_encryption + "aes-256-gcm" + else + "aes-256-cbc" + end + end + end + + module NullSerializer # :nodoc: + def self.load(value) + value + end + + def self.dump(value) + value + end + end + + module NullVerifier # :nodoc: + def self.verify(value) + value + end + + def self.generate(value) + value + end + end + + class InvalidMessage < StandardError; end + OpenSSLCipherError = OpenSSL::Cipher::CipherError + + # Initialize a new MessageEncryptor. +secret+ must be at least as long as + # the cipher key size. For the default 'aes-256-gcm' cipher, this is 256 + # bits. If you are using a user-entered secret, you can generate a suitable + # key by using ActiveSupport::KeyGenerator or a similar key + # derivation function. + # + # First additional parameter is used as the signature key for MessageVerifier. + # This allows you to specify keys to encrypt and sign data. + # + # ActiveSupport::MessageEncryptor.new('secret', 'signature_secret') + # + # Options: + # * :cipher - Cipher to use. Can be any cipher returned by + # OpenSSL::Cipher.ciphers. Default is 'aes-256-gcm'. + # * :digest - String of digest to use for signing. Default is + # +SHA1+. Ignored when using an AEAD cipher like 'aes-256-gcm'. + # * :serializer - Object serializer to use. Default is +Marshal+. + def initialize(secret, sign_secret = nil, cipher: nil, digest: nil, serializer: nil) + @secret = secret + @sign_secret = sign_secret + @cipher = cipher || self.class.default_cipher + @digest = digest || "SHA1" unless aead_mode? + @verifier = resolve_verifier + @serializer = serializer || Marshal + end + + # Encrypt and sign a message. We need to sign the message in order to avoid + # padding attacks. Reference: https://www.limited-entropy.com/padding-oracle-attacks/. + def encrypt_and_sign(value, expires_at: nil, expires_in: nil, purpose: nil) + verifier.generate(_encrypt(value, expires_at: expires_at, expires_in: expires_in, purpose: purpose)) + end + + # Decrypt and verify a message. We need to verify the message in order to + # avoid padding attacks. Reference: https://www.limited-entropy.com/padding-oracle-attacks/. + def decrypt_and_verify(data, purpose: nil, **) + _decrypt(verifier.verify(data), purpose) + end + + # Given a cipher, returns the key length of the cipher to help generate the key of desired size + def self.key_len(cipher = default_cipher) + OpenSSL::Cipher.new(cipher).key_len + end + + private + def _encrypt(value, **metadata_options) + cipher = new_cipher + cipher.encrypt + cipher.key = @secret + + # Rely on OpenSSL for the initialization vector + iv = cipher.random_iv + cipher.auth_data = "" if aead_mode? + + encrypted_data = cipher.update(Messages::Metadata.wrap(@serializer.dump(value), **metadata_options)) + encrypted_data << cipher.final + + blob = "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}" + blob = "#{blob}--#{::Base64.strict_encode64 cipher.auth_tag}" if aead_mode? + blob + end + + def _decrypt(encrypted_message, purpose) + cipher = new_cipher + encrypted_data, iv, auth_tag = encrypted_message.split("--").map { |v| ::Base64.strict_decode64(v) } + + # Currently the OpenSSL bindings do not raise an error if auth_tag is + # truncated, which would allow an attacker to easily forge it. See + # https://github.com/ruby/openssl/issues/63 + raise InvalidMessage if aead_mode? && (auth_tag.nil? || auth_tag.bytes.length != 16) + + cipher.decrypt + cipher.key = @secret + cipher.iv = iv + if aead_mode? + cipher.auth_tag = auth_tag + cipher.auth_data = "" + end + + decrypted_data = cipher.update(encrypted_data) + decrypted_data << cipher.final + + message = Messages::Metadata.verify(decrypted_data, purpose) + @serializer.load(message) if message + rescue OpenSSLCipherError, TypeError, ArgumentError + raise InvalidMessage + end + + def new_cipher + OpenSSL::Cipher.new(@cipher) + end + + attr_reader :verifier + + def aead_mode? + @aead_mode ||= new_cipher.authenticated? + end + + def resolve_verifier + if aead_mode? + NullVerifier + else + MessageVerifier.new(@sign_secret || @secret, digest: @digest, serializer: NullSerializer) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/message_verifier.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/message_verifier.rb new file mode 100644 index 0000000..c224bdc --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/message_verifier.rb @@ -0,0 +1,237 @@ +# frozen_string_literal: true + +require "openssl" +require "base64" +require "active_support/core_ext/object/blank" +require "active_support/security_utils" +require "active_support/messages/metadata" +require "active_support/messages/rotator" + +module ActiveSupport + # +MessageVerifier+ makes it easy to generate and verify messages which are + # signed to prevent tampering. + # + # This is useful for cases like remember-me tokens and auto-unsubscribe links + # where the session store isn't suitable or available. + # + # Remember Me: + # cookies[:remember_me] = @verifier.generate([@user.id, 2.weeks.from_now]) + # + # In the authentication filter: + # + # id, time = @verifier.verify(cookies[:remember_me]) + # if Time.now < time + # self.current_user = User.find(id) + # end + # + # By default it uses Marshal to serialize the message. If you want to use + # another serialization method, you can set the serializer in the options + # hash upon initialization: + # + # @verifier = ActiveSupport::MessageVerifier.new('s3Krit', serializer: YAML) + # + # +MessageVerifier+ creates HMAC signatures using SHA1 hash algorithm by default. + # If you want to use a different hash algorithm, you can change it by providing + # +:digest+ key as an option while initializing the verifier: + # + # @verifier = ActiveSupport::MessageVerifier.new('s3Krit', digest: 'SHA256') + # + # === Confining messages to a specific purpose + # + # By default any message can be used throughout your app. But they can also be + # confined to a specific +:purpose+. + # + # token = @verifier.generate("this is the chair", purpose: :login) + # + # Then that same purpose must be passed when verifying to get the data back out: + # + # @verifier.verified(token, purpose: :login) # => "this is the chair" + # @verifier.verified(token, purpose: :shipping) # => nil + # @verifier.verified(token) # => nil + # + # @verifier.verify(token, purpose: :login) # => "this is the chair" + # @verifier.verify(token, purpose: :shipping) # => ActiveSupport::MessageVerifier::InvalidSignature + # @verifier.verify(token) # => ActiveSupport::MessageVerifier::InvalidSignature + # + # Likewise, if a message has no purpose it won't be returned when verifying with + # a specific purpose. + # + # token = @verifier.generate("the conversation is lively") + # @verifier.verified(token, purpose: :scare_tactics) # => nil + # @verifier.verified(token) # => "the conversation is lively" + # + # @verifier.verify(token, purpose: :scare_tactics) # => ActiveSupport::MessageVerifier::InvalidSignature + # @verifier.verify(token) # => "the conversation is lively" + # + # === Making messages expire + # + # By default messages last forever and verifying one year from now will still + # return the original value. But messages can be set to expire at a given + # time with +:expires_in+ or +:expires_at+. + # + # @verifier.generate("parcel", expires_in: 1.month) + # @verifier.generate("doowad", expires_at: Time.now.end_of_year) + # + # Then the messages can be verified and returned up to the expire time. + # Thereafter, the +verified+ method returns +nil+ while +verify+ raises + # ActiveSupport::MessageVerifier::InvalidSignature. + # + # === Rotating keys + # + # MessageVerifier also supports rotating out old configurations by falling + # back to a stack of verifiers. Call +rotate+ to build and add a verifier so + # either +verified+ or +verify+ will also try verifying with the fallback. + # + # By default any rotated verifiers use the values of the primary + # verifier unless specified otherwise. + # + # You'd give your verifier the new defaults: + # + # verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA512", serializer: JSON) + # + # Then gradually rotate the old values out by adding them as fallbacks. Any message + # generated with the old values will then work until the rotation is removed. + # + # verifier.rotate old_secret # Fallback to an old secret instead of @secret. + # verifier.rotate digest: "SHA256" # Fallback to an old digest instead of SHA512. + # verifier.rotate serializer: Marshal # Fallback to an old serializer instead of JSON. + # + # Though the above would most likely be combined into one rotation: + # + # verifier.rotate old_secret, digest: "SHA256", serializer: Marshal + class MessageVerifier + prepend Messages::Rotator::Verifier + + class InvalidSignature < StandardError; end + + SEPARATOR = "--" # :nodoc: + SEPARATOR_LENGTH = SEPARATOR.length # :nodoc: + + def initialize(secret, digest: nil, serializer: nil) + raise ArgumentError, "Secret should not be nil." unless secret + @secret = secret + @digest = digest&.to_s || "SHA1" + @serializer = serializer || Marshal + end + + # Checks if a signed message could have been generated by signing an object + # with the +MessageVerifier+'s secret. + # + # verifier = ActiveSupport::MessageVerifier.new 's3Krit' + # signed_message = verifier.generate 'a private message' + # verifier.valid_message?(signed_message) # => true + # + # tampered_message = signed_message.chop # editing the message invalidates the signature + # verifier.valid_message?(tampered_message) # => false + def valid_message?(signed_message) + data, digest = get_data_and_digest_from(signed_message) + digest_matches_data?(digest, data) + end + + # Decodes the signed message using the +MessageVerifier+'s secret. + # + # verifier = ActiveSupport::MessageVerifier.new 's3Krit' + # + # signed_message = verifier.generate 'a private message' + # verifier.verified(signed_message) # => 'a private message' + # + # Returns +nil+ if the message was not signed with the same secret. + # + # other_verifier = ActiveSupport::MessageVerifier.new 'd1ff3r3nt-s3Krit' + # other_verifier.verified(signed_message) # => nil + # + # Returns +nil+ if the message is not Base64-encoded. + # + # invalid_message = "f--46a0120593880c733a53b6dad75b42ddc1c8996d" + # verifier.verified(invalid_message) # => nil + # + # Raises any error raised while decoding the signed message. + # + # incompatible_message = "test--dad7b06c94abba8d46a15fafaef56c327665d5ff" + # verifier.verified(incompatible_message) # => TypeError: incompatible marshal file format + def verified(signed_message, purpose: nil, **) + data, digest = get_data_and_digest_from(signed_message) + if digest_matches_data?(digest, data) + begin + message = Messages::Metadata.verify(decode(data), purpose) + @serializer.load(message) if message + rescue ArgumentError => argument_error + return if argument_error.message.include?("invalid base64") + raise + end + end + end + + # Decodes the signed message using the +MessageVerifier+'s secret. + # + # verifier = ActiveSupport::MessageVerifier.new 's3Krit' + # signed_message = verifier.generate 'a private message' + # + # verifier.verify(signed_message) # => 'a private message' + # + # Raises +InvalidSignature+ if the message was not signed with the same + # secret or was not Base64-encoded. + # + # other_verifier = ActiveSupport::MessageVerifier.new 'd1ff3r3nt-s3Krit' + # other_verifier.verify(signed_message) # => ActiveSupport::MessageVerifier::InvalidSignature + def verify(*args, **options) + verified(*args, **options) || raise(InvalidSignature) + end + + # Generates a signed message for the provided value. + # + # The message is signed with the +MessageVerifier+'s secret. + # Returns Base64-encoded message joined with the generated signature. + # + # verifier = ActiveSupport::MessageVerifier.new 's3Krit' + # verifier.generate 'a private message' # => "BAhJIhRwcml2YXRlLW1lc3NhZ2UGOgZFVA==--e2d724331ebdee96a10fb99b089508d1c72bd772" + def generate(value, expires_at: nil, expires_in: nil, purpose: nil) + data = encode(Messages::Metadata.wrap(@serializer.dump(value), expires_at: expires_at, expires_in: expires_in, purpose: purpose)) + "#{data}#{SEPARATOR}#{generate_digest(data)}" + end + + private + def encode(data) + ::Base64.strict_encode64(data) + end + + def decode(data) + ::Base64.strict_decode64(data) + end + + def generate_digest(data) + OpenSSL::HMAC.hexdigest(@digest, @secret, data) + end + + def digest_length_in_hex + # In hexadecimal (AKA base16) it takes 4 bits to represent a character, + # hence we multiply the digest's length (in bytes) by 8 to get it in + # bits and divide by 4 to get its number of characters it hex. Well, 8 + # divided by 4 is 2. + @digest_length_in_hex ||= OpenSSL::Digest.new(@digest).digest_length * 2 + end + + def separator_index_for(signed_message) + index = signed_message.length - digest_length_in_hex - SEPARATOR_LENGTH + return if index.negative? || signed_message[index, SEPARATOR_LENGTH] != SEPARATOR + + index + end + + def get_data_and_digest_from(signed_message) + return if signed_message.nil? || !signed_message.valid_encoding? || signed_message.empty? + + separator_index = separator_index_for(signed_message) + return if separator_index.nil? + + data = signed_message[0...separator_index] + digest = signed_message[separator_index + SEPARATOR_LENGTH..-1] + + [data, digest] + end + + def digest_matches_data?(digest, data) + data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data)) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/messages/metadata.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/messages/metadata.rb new file mode 100644 index 0000000..4719d8e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/messages/metadata.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +require "time" + +module ActiveSupport + module Messages # :nodoc: + class Metadata # :nodoc: + def initialize(message, expires_at = nil, purpose = nil) + @message, @purpose = message, purpose + @expires_at = expires_at.is_a?(String) ? parse_expires_at(expires_at) : expires_at + end + + def as_json(options = {}) + { _rails: { message: @message, exp: @expires_at, pur: @purpose } } + end + + class << self + def wrap(message, expires_at: nil, expires_in: nil, purpose: nil) + if expires_at || expires_in || purpose + JSON.encode new(encode(message), pick_expiry(expires_at, expires_in), purpose) + else + message + end + end + + def verify(message, purpose) + extract_metadata(message).verify(purpose) + end + + private + def pick_expiry(expires_at, expires_in) + if expires_at + expires_at.utc.iso8601(3) + elsif expires_in + Time.now.utc.advance(seconds: expires_in).iso8601(3) + end + end + + def extract_metadata(message) + data = JSON.decode(message) rescue nil + + if data.is_a?(Hash) && data.key?("_rails") + new(decode(data["_rails"]["message"]), data["_rails"]["exp"], data["_rails"]["pur"]) + else + new(message) + end + end + + def encode(message) + ::Base64.strict_encode64(message) + end + + def decode(message) + ::Base64.strict_decode64(message) + end + end + + def verify(purpose) + @message if match?(purpose) && fresh? + end + + private + def match?(purpose) + @purpose.to_s == purpose.to_s + end + + def fresh? + @expires_at.nil? || Time.now.utc < @expires_at + end + + def parse_expires_at(expires_at) + if ActiveSupport.use_standard_json_time_format + Time.iso8601(expires_at) + else + Time.parse(expires_at) + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/messages/rotation_configuration.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/messages/rotation_configuration.rb new file mode 100644 index 0000000..eef05fe --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/messages/rotation_configuration.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module ActiveSupport + module Messages + class RotationConfiguration # :nodoc: + attr_reader :signed, :encrypted + + def initialize + @signed, @encrypted = [], [] + end + + def rotate(kind, *args, **options) + args << options unless options.empty? + case kind + when :signed + @signed << args + when :encrypted + @encrypted << args + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/messages/rotator.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/messages/rotator.rb new file mode 100644 index 0000000..b19e185 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/messages/rotator.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module ActiveSupport + module Messages + module Rotator # :nodoc: + def initialize(*secrets, on_rotation: nil, **options) + super(*secrets, **options) + + @options = options + @rotations = [] + @on_rotation = on_rotation + end + + def rotate(*secrets, **options) + @rotations << build_rotation(*secrets, @options.merge(options)) + end + + module Encryptor + include Rotator + + def decrypt_and_verify(*args, on_rotation: @on_rotation, **options) + super + rescue MessageEncryptor::InvalidMessage, MessageVerifier::InvalidSignature + run_rotations(on_rotation) { |encryptor| encryptor.decrypt_and_verify(*args, **options) } || raise + end + + private + def build_rotation(secret = @secret, sign_secret = @sign_secret, options) + self.class.new(secret, sign_secret, **options) + end + end + + module Verifier + include Rotator + + def verified(*args, on_rotation: @on_rotation, **options) + super || run_rotations(on_rotation) { |verifier| verifier.verified(*args, **options) } + end + + private + def build_rotation(secret = @secret, options) + self.class.new(secret, **options) + end + end + + private + def run_rotations(on_rotation) + @rotations.find do |rotation| + if message = yield(rotation) rescue next + on_rotation&.call + return message + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/multibyte.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/multibyte.rb new file mode 100644 index 0000000..0366350 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/multibyte.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module ActiveSupport # :nodoc: + module Multibyte + autoload :Chars, "active_support/multibyte/chars" + autoload :Unicode, "active_support/multibyte/unicode" + + # The proxy class returned when calling mb_chars. You can use this accessor + # to configure your own proxy class so you can support other encodings. See + # the ActiveSupport::Multibyte::Chars implementation for an example how to + # do this. + # + # ActiveSupport::Multibyte.proxy_class = CharsForUTF32 + def self.proxy_class=(klass) + @proxy_class = klass + end + + # Returns the current proxy class. + def self.proxy_class + @proxy_class ||= ActiveSupport::Multibyte::Chars + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/multibyte/chars.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/multibyte/chars.rb new file mode 100644 index 0000000..79dda27 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/multibyte/chars.rb @@ -0,0 +1,176 @@ +# frozen_string_literal: true + +require "active_support/json" +require "active_support/core_ext/string/access" +require "active_support/core_ext/string/behavior" +require "active_support/core_ext/module/delegation" + +module ActiveSupport # :nodoc: + module Multibyte # :nodoc: + # Chars enables you to work transparently with UTF-8 encoding in the Ruby + # String class without having extensive knowledge about the encoding. A + # Chars object accepts a string upon initialization and proxies String + # methods in an encoding safe manner. All the normal String methods are also + # implemented on the proxy. + # + # String methods are proxied through the Chars object, and can be accessed + # through the +mb_chars+ method. Methods which would normally return a + # String object now return a Chars object so methods can be chained. + # + # 'The Perfect String '.mb_chars.downcase.strip + # # => # + # + # Chars objects are perfectly interchangeable with String objects as long as + # no explicit class checks are made. If certain methods do explicitly check + # the class, call +to_s+ before you pass chars objects to them. + # + # bad.explicit_checking_method 'T'.mb_chars.downcase.to_s + # + # The default Chars implementation assumes that the encoding of the string + # is UTF-8, if you want to handle different encodings you can write your own + # multibyte string handler and configure it through + # ActiveSupport::Multibyte.proxy_class. + # + # class CharsForUTF32 + # def size + # @wrapped_string.size / 4 + # end + # + # def self.accepts?(string) + # string.length % 4 == 0 + # end + # end + # + # ActiveSupport::Multibyte.proxy_class = CharsForUTF32 + class Chars + include Comparable + attr_reader :wrapped_string + alias to_s wrapped_string + alias to_str wrapped_string + + delegate :<=>, :=~, :match?, :acts_like_string?, to: :wrapped_string + + # Creates a new Chars instance by wrapping _string_. + def initialize(string) + @wrapped_string = string + @wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen? + end + + # Forward all undefined methods to the wrapped string. + def method_missing(method, *args, &block) + result = @wrapped_string.__send__(method, *args, &block) + if method.end_with?("!") + self if result + else + result.kind_of?(String) ? chars(result) : result + end + end + + # Returns +true+ if _obj_ responds to the given method. Private methods + # are included in the search only if the optional second parameter + # evaluates to +true+. + def respond_to_missing?(method, include_private) + @wrapped_string.respond_to?(method, include_private) + end + + # Works just like String#split, with the exception that the items + # in the resulting list are Chars instances instead of String. This makes + # chaining methods easier. + # + # 'Café périferôl'.mb_chars.split(/é/).map { |part| part.upcase.to_s } # => ["CAF", " P", "RIFERÔL"] + def split(*args) + @wrapped_string.split(*args).map { |i| self.class.new(i) } + end + + # Works like String#slice!, but returns an instance of + # Chars, or +nil+ if the string was not modified. The string will not be + # modified if the range given is out of bounds + # + # string = 'Welcome' + # string.mb_chars.slice!(3) # => # + # string # => 'Welome' + # string.mb_chars.slice!(0..3) # => # + # string # => 'me' + def slice!(*args) + string_sliced = @wrapped_string.slice!(*args) + if string_sliced + chars(string_sliced) + end + end + + # Reverses all characters in the string. + # + # 'Café'.mb_chars.reverse.to_s # => 'éfaC' + def reverse + chars(@wrapped_string.grapheme_clusters.reverse.join) + end + + # Limits the byte size of the string to a number of bytes without breaking + # characters. Usable when the storage for a string is limited for some + # reason. + # + # 'こんにちは'.mb_chars.limit(7).to_s # => "こん" + def limit(limit) + chars(@wrapped_string.truncate_bytes(limit, omission: nil)) + end + + # Capitalizes the first letter of every word, when possible. + # + # "ÉL QUE SE ENTERÓ".mb_chars.titleize.to_s # => "Él Que Se Enteró" + # "日本語".mb_chars.titleize.to_s # => "日本語" + def titleize + chars(downcase.to_s.gsub(/\b('?\S)/u) { $1.upcase }) + end + alias_method :titlecase, :titleize + + # Performs canonical decomposition on all the characters. + # + # 'é'.length # => 1 + # 'é'.mb_chars.decompose.to_s.length # => 2 + def decompose + chars(Unicode.decompose(:canonical, @wrapped_string.codepoints.to_a).pack("U*")) + end + + # Performs composition on all the characters. + # + # 'é'.length # => 1 + # 'é'.mb_chars.compose.to_s.length # => 1 + def compose + chars(Unicode.compose(@wrapped_string.codepoints.to_a).pack("U*")) + end + + # Returns the number of grapheme clusters in the string. + # + # 'क्षि'.mb_chars.length # => 4 + # 'क्षि'.mb_chars.grapheme_length # => 2 + def grapheme_length + @wrapped_string.grapheme_clusters.length + end + + # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent + # resulting in a valid UTF-8 string. + # + # Passing +true+ will forcibly tidy all bytes, assuming that the string's + # encoding is entirely CP1252 or ISO-8859-1. + def tidy_bytes(force = false) + chars(Unicode.tidy_bytes(@wrapped_string, force)) + end + + def as_json(options = nil) # :nodoc: + to_s.as_json(options) + end + + %w(reverse tidy_bytes).each do |method| + define_method("#{method}!") do |*args| + @wrapped_string = public_send(method, *args).to_s + self + end + end + + private + def chars(string) + self.class.new(string) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/multibyte/unicode.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/multibyte/unicode.rb new file mode 100644 index 0000000..1c3e98b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/multibyte/unicode.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +module ActiveSupport + module Multibyte + module Unicode + extend self + + # The Unicode version that is supported by the implementation + UNICODE_VERSION = RbConfig::CONFIG["UNICODE_VERSION"] + + # Decompose composed characters to the decomposed form. + def decompose(type, codepoints) + if type == :compatibility + codepoints.pack("U*").unicode_normalize(:nfkd).codepoints + else + codepoints.pack("U*").unicode_normalize(:nfd).codepoints + end + end + + # Compose decomposed characters to the composed form. + def compose(codepoints) + codepoints.pack("U*").unicode_normalize(:nfc).codepoints + end + + # Rubinius' String#scrub, however, doesn't support ASCII-incompatible chars. + if !defined?(Rubinius) + # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent + # resulting in a valid UTF-8 string. + # + # Passing +true+ will forcibly tidy all bytes, assuming that the string's + # encoding is entirely CP1252 or ISO-8859-1. + def tidy_bytes(string, force = false) + return string if string.empty? || string.ascii_only? + return recode_windows1252_chars(string) if force + string.scrub { |bad| recode_windows1252_chars(bad) } + end + else + def tidy_bytes(string, force = false) + return string if string.empty? + return recode_windows1252_chars(string) if force + + # We can't transcode to the same format, so we choose a nearly-identical encoding. + # We're going to 'transcode' bytes from UTF-8 when possible, then fall back to + # CP1252 when we get errors. The final string will be 'converted' back to UTF-8 + # before returning. + reader = Encoding::Converter.new(Encoding::UTF_8, Encoding::UTF_16LE) + + source = string.dup + out = "".force_encoding(Encoding::UTF_16LE) + + loop do + reader.primitive_convert(source, out) + _, _, _, error_bytes, _ = reader.primitive_errinfo + break if error_bytes.nil? + out << error_bytes.encode(Encoding::UTF_16LE, Encoding::Windows_1252, invalid: :replace, undef: :replace) + end + + reader.finish + + out.encode!(Encoding::UTF_8) + end + end + + private + def recode_windows1252_chars(string) + string.encode(Encoding::UTF_8, Encoding::Windows_1252, invalid: :replace, undef: :replace) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/notifications.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/notifications.rb new file mode 100644 index 0000000..f2bcf82 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/notifications.rb @@ -0,0 +1,280 @@ +# frozen_string_literal: true + +require "active_support/notifications/instrumenter" +require "active_support/notifications/fanout" + +module ActiveSupport + # = \Notifications + # + # ActiveSupport::Notifications provides an instrumentation API for + # Ruby. + # + # == Instrumenters + # + # To instrument an event you just need to do: + # + # ActiveSupport::Notifications.instrument('render', extra: :information) do + # render plain: 'Foo' + # end + # + # That first executes the block and then notifies all subscribers once done. + # + # In the example above +render+ is the name of the event, and the rest is called + # the _payload_. The payload is a mechanism that allows instrumenters to pass + # extra information to subscribers. Payloads consist of a hash whose contents + # are arbitrary and generally depend on the event. + # + # == Subscribers + # + # You can consume those events and the information they provide by registering + # a subscriber. + # + # ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload| + # name # => String, name of the event (such as 'render' from above) + # start # => Time, when the instrumented block started execution + # finish # => Time, when the instrumented block ended execution + # id # => String, unique ID for the instrumenter that fired the event + # payload # => Hash, the payload + # end + # + # Here, the +start+ and +finish+ values represent wall-clock time. If you are + # concerned about accuracy, you can register a monotonic subscriber. + # + # ActiveSupport::Notifications.monotonic_subscribe('render') do |name, start, finish, id, payload| + # name # => String, name of the event (such as 'render' from above) + # start # => Monotonic time, when the instrumented block started execution + # finish # => Monotonic time, when the instrumented block ended execution + # id # => String, unique ID for the instrumenter that fired the event + # payload # => Hash, the payload + # end + # + # The +start+ and +finish+ values above represent monotonic time. + # + # For instance, let's store all "render" events in an array: + # + # events = [] + # + # ActiveSupport::Notifications.subscribe('render') do |*args| + # events << ActiveSupport::Notifications::Event.new(*args) + # end + # + # That code returns right away, you are just subscribing to "render" events. + # The block is saved and will be called whenever someone instruments "render": + # + # ActiveSupport::Notifications.instrument('render', extra: :information) do + # render plain: 'Foo' + # end + # + # event = events.first + # event.name # => "render" + # event.duration # => 10 (in milliseconds) + # event.payload # => { extra: :information } + # + # The block in the subscribe call gets the name of the event, start + # timestamp, end timestamp, a string with a unique identifier for that event's instrumenter + # (something like "535801666f04d0298cd6"), and a hash with the payload, in + # that order. + # + # If an exception happens during that particular instrumentation the payload will + # have a key :exception with an array of two elements as value: a string with + # the name of the exception class, and the exception message. + # The :exception_object key of the payload will have the exception + # itself as the value: + # + # event.payload[:exception] # => ["ArgumentError", "Invalid value"] + # event.payload[:exception_object] # => # + # + # As the earlier example depicts, the class ActiveSupport::Notifications::Event + # is able to take the arguments as they come and provide an object-oriented + # interface to that data. + # + # It is also possible to pass an object which responds to call method + # as the second parameter to the subscribe method instead of a block: + # + # module ActionController + # class PageRequest + # def call(name, started, finished, unique_id, payload) + # Rails.logger.debug ['notification:', name, started, finished, unique_id, payload].join(' ') + # end + # end + # end + # + # ActiveSupport::Notifications.subscribe('process_action.action_controller', ActionController::PageRequest.new) + # + # resulting in the following output within the logs including a hash with the payload: + # + # notification: process_action.action_controller 2012-04-13 01:08:35 +0300 2012-04-13 01:08:35 +0300 af358ed7fab884532ec7 { + # controller: "Devise::SessionsController", + # action: "new", + # params: {"action"=>"new", "controller"=>"devise/sessions"}, + # format: :html, + # method: "GET", + # path: "/login/sign_in", + # status: 200, + # view_runtime: 279.3080806732178, + # db_runtime: 40.053 + # } + # + # You can also subscribe to all events whose name matches a certain regexp: + # + # ActiveSupport::Notifications.subscribe(/render/) do |*args| + # ... + # end + # + # and even pass no argument to subscribe, in which case you are subscribing + # to all events. + # + # == Temporary Subscriptions + # + # Sometimes you do not want to subscribe to an event for the entire life of + # the application. There are two ways to unsubscribe. + # + # WARNING: The instrumentation framework is designed for long-running subscribers, + # use this feature sparingly because it wipes some internal caches and that has + # a negative impact on performance. + # + # === Subscribe While a Block Runs + # + # You can subscribe to some event temporarily while some block runs. For + # example, in + # + # callback = lambda {|*args| ... } + # ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do + # ... + # end + # + # the callback will be called for all "sql.active_record" events instrumented + # during the execution of the block. The callback is unsubscribed automatically + # after that. + # + # To record +started+ and +finished+ values with monotonic time, + # specify the optional :monotonic option to the + # subscribed method. The :monotonic option is set + # to +false+ by default. + # + # callback = lambda {|name, started, finished, unique_id, payload| ... } + # ActiveSupport::Notifications.subscribed(callback, "sql.active_record", monotonic: true) do + # ... + # end + # + # === Manual Unsubscription + # + # The +subscribe+ method returns a subscriber object: + # + # subscriber = ActiveSupport::Notifications.subscribe("render") do |*args| + # ... + # end + # + # To prevent that block from being called anymore, just unsubscribe passing + # that reference: + # + # ActiveSupport::Notifications.unsubscribe(subscriber) + # + # You can also unsubscribe by passing the name of the subscriber object. Note + # that this will unsubscribe all subscriptions with the given name: + # + # ActiveSupport::Notifications.unsubscribe("render") + # + # Subscribers using a regexp or other pattern-matching object will remain subscribed + # to all events that match their original pattern, unless those events match a string + # passed to +unsubscribe+: + # + # subscriber = ActiveSupport::Notifications.subscribe(/render/) { } + # ActiveSupport::Notifications.unsubscribe('render_template.action_view') + # subscriber.matches?('render_template.action_view') # => false + # subscriber.matches?('render_partial.action_view') # => true + # + # == Default Queue + # + # Notifications ships with a queue implementation that consumes and publishes events + # to all log subscribers. You can use any queue implementation you want. + # + module Notifications + class << self + attr_accessor :notifier + + def publish(name, *args) + notifier.publish(name, *args) + end + + def publish_event(event) # :nodoc: + notifier.publish_event(event) + end + + def instrument(name, payload = {}) + if notifier.listening?(name) + instrumenter.instrument(name, payload) { yield payload if block_given? } + else + yield payload if block_given? + end + end + + # Subscribe to a given event name with the passed +block+. + # + # You can subscribe to events by passing a String to match exact event + # names, or by passing a Regexp to match all events that match a pattern. + # + # ActiveSupport::Notifications.subscribe(/render/) do |*args| + # @event = ActiveSupport::Notifications::Event.new(*args) + # end + # + # The +block+ will receive five parameters with information about the event: + # + # ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload| + # name # => String, name of the event (such as 'render' from above) + # start # => Time, when the instrumented block started execution + # finish # => Time, when the instrumented block ended execution + # id # => String, unique ID for the instrumenter that fired the event + # payload # => Hash, the payload + # end + # + # If the block passed to the method only takes one parameter, + # it will yield an event object to the block: + # + # ActiveSupport::Notifications.subscribe(/render/) do |event| + # @event = event + # end + # + # Raises an error if invalid event name type is passed: + # + # ActiveSupport::Notifications.subscribe(:render) {|*args| ...} + # #=> ArgumentError (pattern must be specified as a String, Regexp or empty) + # + def subscribe(pattern = nil, callback = nil, &block) + notifier.subscribe(pattern, callback, monotonic: false, &block) + end + + # Performs the same functionality as #subscribe, but the +start+ and + # +finish+ block arguments are in monotonic time instead of wall-clock + # time. Monotonic time will not jump forward or backward (due to NTP or + # Daylights Savings). Use +monotonic_subscribe+ when accuracy of time + # duration is important. For example, computing elapsed time between + # two events. + def monotonic_subscribe(pattern = nil, callback = nil, &block) + notifier.subscribe(pattern, callback, monotonic: true, &block) + end + + def subscribed(callback, pattern = nil, monotonic: false, &block) + subscriber = notifier.subscribe(pattern, callback, monotonic: monotonic) + yield + ensure + unsubscribe(subscriber) + end + + def unsubscribe(subscriber_or_name) + notifier.unsubscribe(subscriber_or_name) + end + + def instrumenter + registry[notifier] ||= Instrumenter.new(notifier) + end + + private + def registry + ActiveSupport::IsolatedExecutionState[:active_support_notifications_registry] ||= {} + end + end + + self.notifier = Fanout.new + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/notifications/fanout.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/notifications/fanout.rb new file mode 100644 index 0000000..0759d3a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/notifications/fanout.rb @@ -0,0 +1,285 @@ +# frozen_string_literal: true + +require "mutex_m" +require "concurrent/map" +require "set" +require "active_support/core_ext/object/try" + +module ActiveSupport + module Notifications + class InstrumentationSubscriberError < RuntimeError + attr_reader :exceptions + + def initialize(exceptions) + @exceptions = exceptions + exception_class_names = exceptions.map { |e| e.class.name } + super "Exception(s) occurred within instrumentation subscribers: #{exception_class_names.join(', ')}" + end + end + + # This is a default queue implementation that ships with Notifications. + # It just pushes events to all registered log subscribers. + # + # This class is thread safe. All methods are reentrant. + class Fanout + include Mutex_m + + def initialize + @string_subscribers = Hash.new { |h, k| h[k] = [] } + @other_subscribers = [] + @listeners_for = Concurrent::Map.new + super + end + + def subscribe(pattern = nil, callable = nil, monotonic: false, &block) + subscriber = Subscribers.new(pattern, callable || block, monotonic) + synchronize do + case pattern + when String + @string_subscribers[pattern] << subscriber + @listeners_for.delete(pattern) + when NilClass, Regexp + @other_subscribers << subscriber + @listeners_for.clear + else + raise ArgumentError, "pattern must be specified as a String, Regexp or empty" + end + end + subscriber + end + + def unsubscribe(subscriber_or_name) + synchronize do + case subscriber_or_name + when String + @string_subscribers[subscriber_or_name].clear + @listeners_for.delete(subscriber_or_name) + @other_subscribers.each { |sub| sub.unsubscribe!(subscriber_or_name) } + else + pattern = subscriber_or_name.try(:pattern) + if String === pattern + @string_subscribers[pattern].delete(subscriber_or_name) + @listeners_for.delete(pattern) + else + @other_subscribers.delete(subscriber_or_name) + @listeners_for.clear + end + end + end + end + + def start(name, id, payload) + iterate_guarding_exceptions(listeners_for(name)) { |s| s.start(name, id, payload) } + end + + def finish(name, id, payload, listeners = listeners_for(name)) + iterate_guarding_exceptions(listeners) { |s| s.finish(name, id, payload) } + end + + def publish(name, *args) + iterate_guarding_exceptions(listeners_for(name)) { |s| s.publish(name, *args) } + end + + def publish_event(event) + iterate_guarding_exceptions(listeners_for(event.name)) { |s| s.publish_event(event) } + end + + def iterate_guarding_exceptions(listeners) + exceptions = nil + + listeners.each do |s| + yield s + rescue Exception => e + exceptions ||= [] + exceptions << e + end + + if exceptions + if exceptions.size == 1 + raise exceptions.first + else + raise InstrumentationSubscriberError.new(exceptions), cause: exceptions.first + end + end + + listeners + end + + def listeners_for(name) + # this is correctly done double-checked locking (Concurrent::Map's lookups have volatile semantics) + @listeners_for[name] || synchronize do + # use synchronisation when accessing @subscribers + @listeners_for[name] ||= + @string_subscribers[name] + @other_subscribers.select { |s| s.subscribed_to?(name) } + end + end + + def listening?(name) + listeners_for(name).any? + end + + # This is a sync queue, so there is no waiting. + def wait + end + + module Subscribers # :nodoc: + def self.new(pattern, listener, monotonic) + subscriber_class = monotonic ? MonotonicTimed : Timed + + if listener.respond_to?(:start) && listener.respond_to?(:finish) + subscriber_class = Evented + else + # Doing this to detect a single argument block or callable + # like `proc { |x| }` vs `proc { |*x| }`, `proc { |**x| }`, + # or `proc { |x, **y| }` + procish = listener.respond_to?(:parameters) ? listener : listener.method(:call) + + if procish.arity == 1 && procish.parameters.length == 1 + subscriber_class = EventObject + end + end + + subscriber_class.new(pattern, listener) + end + + class Matcher # :nodoc: + attr_reader :pattern, :exclusions + + def self.wrap(pattern) + if String === pattern + pattern + elsif pattern.nil? + AllMessages.new + else + new(pattern) + end + end + + def initialize(pattern) + @pattern = pattern + @exclusions = Set.new + end + + def unsubscribe!(name) + exclusions << -name if pattern === name + end + + def ===(name) + pattern === name && !exclusions.include?(name) + end + + class AllMessages + def ===(name) + true + end + + def unsubscribe!(*) + false + end + end + end + + class Evented # :nodoc: + attr_reader :pattern + + def initialize(pattern, delegate) + @pattern = Matcher.wrap(pattern) + @delegate = delegate + @can_publish = delegate.respond_to?(:publish) + @can_publish_event = delegate.respond_to?(:publish_event) + end + + def publish(name, *args) + if @can_publish + @delegate.publish name, *args + end + end + + def publish_event(event) + if @can_publish_event + @delegate.publish_event event + else + publish(event.name, event.time, event.end, event.transaction_id, event.payload) + end + end + + def start(name, id, payload) + @delegate.start name, id, payload + end + + def finish(name, id, payload) + @delegate.finish name, id, payload + end + + def subscribed_to?(name) + pattern === name + end + + def unsubscribe!(name) + pattern.unsubscribe!(name) + end + end + + class Timed < Evented # :nodoc: + def publish(name, *args) + @delegate.call name, *args + end + + def start(name, id, payload) + timestack = IsolatedExecutionState[:_timestack] ||= [] + timestack.push Time.now + end + + def finish(name, id, payload) + timestack = IsolatedExecutionState[:_timestack] + started = timestack.pop + @delegate.call(name, started, Time.now, id, payload) + end + end + + class MonotonicTimed < Evented # :nodoc: + def publish(name, *args) + @delegate.call name, *args + end + + def start(name, id, payload) + timestack = IsolatedExecutionState[:_timestack_monotonic] ||= [] + timestack.push Process.clock_gettime(Process::CLOCK_MONOTONIC) + end + + def finish(name, id, payload) + timestack = IsolatedExecutionState[:_timestack_monotonic] + started = timestack.pop + @delegate.call(name, started, Process.clock_gettime(Process::CLOCK_MONOTONIC), id, payload) + end + end + + class EventObject < Evented + def start(name, id, payload) + stack = IsolatedExecutionState[:_event_stack] ||= [] + event = build_event name, id, payload + event.start! + stack.push event + end + + def finish(name, id, payload) + stack = IsolatedExecutionState[:_event_stack] + event = stack.pop + event.payload = payload + event.finish! + @delegate.call event + end + + def publish_event(event) + @delegate.call event + end + + private + def build_event(name, id, payload) + ActiveSupport::Notifications::Event.new name, nil, nil, id, payload + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/notifications/instrumenter.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/notifications/instrumenter.rb new file mode 100644 index 0000000..c69e8cd --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/notifications/instrumenter.rb @@ -0,0 +1,172 @@ +# frozen_string_literal: true + +require "securerandom" + +module ActiveSupport + module Notifications + # Instrumenters are stored in a thread local. + class Instrumenter + attr_reader :id + + def initialize(notifier) + @id = unique_id + @notifier = notifier + end + + # Given a block, instrument it by measuring the time taken to execute + # and publish it. Without a block, simply send a message via the + # notifier. Notice that events get sent even if an error occurs in the + # passed-in block. + def instrument(name, payload = {}) + # some of the listeners might have state + listeners_state = start name, payload + begin + yield payload if block_given? + rescue Exception => e + payload[:exception] = [e.class.name, e.message] + payload[:exception_object] = e + raise e + ensure + finish_with_state listeners_state, name, payload + end + end + + def new_event(name, payload = {}) # :nodoc: + Event.new(name, nil, nil, @id, payload) + end + + # Send a start notification with +name+ and +payload+. + def start(name, payload) + @notifier.start name, @id, payload + end + + # Send a finish notification with +name+ and +payload+. + def finish(name, payload) + @notifier.finish name, @id, payload + end + + def finish_with_state(listeners_state, name, payload) + @notifier.finish name, @id, payload, listeners_state + end + + private + def unique_id + SecureRandom.hex(10) + end + end + + class Event + attr_reader :name, :time, :end, :transaction_id, :children + attr_accessor :payload + + def initialize(name, start, ending, transaction_id, payload) + @name = name + @payload = payload.dup + @time = start ? start.to_f * 1_000.0 : start + @transaction_id = transaction_id + @end = ending ? ending.to_f * 1_000.0 : ending + @children = [] + @cpu_time_start = 0.0 + @cpu_time_finish = 0.0 + @allocation_count_start = 0 + @allocation_count_finish = 0 + end + + def record + start! + begin + yield payload if block_given? + rescue Exception => e + payload[:exception] = [e.class.name, e.message] + payload[:exception_object] = e + raise e + ensure + finish! + end + end + + # Record information at the time this event starts + def start! + @time = now + @cpu_time_start = now_cpu + @allocation_count_start = now_allocations + end + + # Record information at the time this event finishes + def finish! + @cpu_time_finish = now_cpu + @end = now + @allocation_count_finish = now_allocations + end + + # Returns the CPU time (in milliseconds) passed since the call to + # +start!+ and the call to +finish!+ + def cpu_time + @cpu_time_finish - @cpu_time_start + end + + # Returns the idle time time (in milliseconds) passed since the call to + # +start!+ and the call to +finish!+ + def idle_time + duration - cpu_time + end + + # Returns the number of allocations made since the call to +start!+ and + # the call to +finish!+ + def allocations + @allocation_count_finish - @allocation_count_start + end + + # Returns the difference in milliseconds between when the execution of the + # event started and when it ended. + # + # ActiveSupport::Notifications.subscribe('wait') do |*args| + # @event = ActiveSupport::Notifications::Event.new(*args) + # end + # + # ActiveSupport::Notifications.instrument('wait') do + # sleep 1 + # end + # + # @event.duration # => 1000.138 + def duration + self.end - time + end + + def <<(event) + @children << event + end + + def parent_of?(event) + @children.include? event + end + + private + def now + Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) + end + + begin + Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_millisecond) + + def now_cpu + Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_millisecond) + end + rescue + def now_cpu # rubocop:disable Lint/DuplicateMethods + 0.0 + end + end + + if GC.stat.key?(:total_allocated_objects) + def now_allocations + GC.stat(:total_allocated_objects) + end + else # Likely on JRuby, TruffleRuby + def now_allocations + 0 + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper.rb new file mode 100644 index 0000000..8b66e3c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper.rb @@ -0,0 +1,395 @@ +# frozen_string_literal: true + +module ActiveSupport + module NumberHelper + extend ActiveSupport::Autoload + + eager_autoload do + autoload :NumberConverter + autoload :RoundingHelper + autoload :NumberToRoundedConverter + autoload :NumberToDelimitedConverter + autoload :NumberToHumanConverter + autoload :NumberToHumanSizeConverter + autoload :NumberToPhoneConverter + autoload :NumberToCurrencyConverter + autoload :NumberToPercentageConverter + end + + extend self + + # Formats a +number+ into a phone number (US by default e.g., (555) + # 123-9876). You can customize the format in the +options+ hash. + # + # ==== Options + # + # * :area_code - Adds parentheses around the area code. + # * :delimiter - Specifies the delimiter to use + # (defaults to "-"). + # * :extension - Specifies an extension to add to the + # end of the generated number. + # * :country_code - Sets the country code for the phone + # number. + # * :pattern - Specifies how the number is divided into three + # groups with the custom regexp to override the default format. + # ==== Examples + # + # number_to_phone(5551234) # => "555-1234" + # number_to_phone('5551234') # => "555-1234" + # number_to_phone(1235551234) # => "123-555-1234" + # number_to_phone(1235551234, area_code: true) # => "(123) 555-1234" + # number_to_phone(1235551234, delimiter: ' ') # => "123 555 1234" + # number_to_phone(1235551234, area_code: true, extension: 555) # => "(123) 555-1234 x 555" + # number_to_phone(1235551234, country_code: 1) # => "+1-123-555-1234" + # number_to_phone('123a456') # => "123a456" + # + # number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: '.') + # # => "+1.123.555.1234 x 1343" + # + # number_to_phone(75561234567, pattern: /(\d{1,4})(\d{4})(\d{4})$/, area_code: true) + # # => "(755) 6123-4567" + # number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})$/) + # # => "133-1234-5678" + def number_to_phone(number, options = {}) + NumberToPhoneConverter.convert(number, options) + end + + # Formats a +number+ into a currency string (e.g., $13.65). You + # can customize the format in the +options+ hash. + # + # The currency unit and number formatting of the current locale will be used + # unless otherwise specified in the provided options. No currency conversion + # is performed. If the user is given a way to change their locale, they will + # also be able to change the relative value of the currency displayed with + # this helper. If your application will ever support multiple locales, you + # may want to specify a constant :locale option or consider + # using a library capable of currency conversion. + # + # ==== Options + # + # * :locale - Sets the locale to be used for formatting + # (defaults to current locale). + # * :precision - Sets the level of precision (defaults + # to 2). + # * :round_mode - Determine how rounding is performed + # (defaults to :default. See BigDecimal::mode) + # * :unit - Sets the denomination of the currency + # (defaults to "$"). + # * :separator - Sets the separator between the units + # (defaults to "."). + # * :delimiter - Sets the thousands delimiter (defaults + # to ","). + # * :format - Sets the format for non-negative numbers + # (defaults to "%u%n"). Fields are %u for the + # currency, and %n for the number. + # * :negative_format - Sets the format for negative + # numbers (defaults to prepending a hyphen to the formatted + # number given by :format). Accepts the same fields + # than :format, except %n is here the + # absolute value of the number. + # * :strip_insignificant_zeros - If +true+ removes + # insignificant zeros after the decimal separator (defaults to + # +false+). + # + # ==== Examples + # + # number_to_currency(1234567890.50) # => "$1,234,567,890.50" + # number_to_currency(1234567890.506) # => "$1,234,567,890.51" + # number_to_currency(1234567890.506, precision: 3) # => "$1,234,567,890.506" + # number_to_currency(1234567890.506, locale: :fr) # => "1 234 567 890,51 €" + # number_to_currency('123a456') # => "$123a456" + # + # number_to_currency(-0.456789, precision: 0) + # # => "$0" + # number_to_currency(-1234567890.50, negative_format: '(%u%n)') + # # => "($1,234,567,890.50)" + # number_to_currency(1234567890.50, unit: '£', separator: ',', delimiter: '') + # # => "£1234567890,50" + # number_to_currency(1234567890.50, unit: '£', separator: ',', delimiter: '', format: '%n %u') + # # => "1234567890,50 £" + # number_to_currency(1234567890.50, strip_insignificant_zeros: true) + # # => "$1,234,567,890.5" + # number_to_currency(1234567890.50, precision: 0, round_mode: :up) + # # => "$1,234,567,891" + def number_to_currency(number, options = {}) + NumberToCurrencyConverter.convert(number, options) + end + + # Formats a +number+ as a percentage string (e.g., 65%). You can + # customize the format in the +options+ hash. + # + # ==== Options + # + # * :locale - Sets the locale to be used for formatting + # (defaults to current locale). + # * :precision - Sets the precision of the number + # (defaults to 3). Keeps the number's precision if +nil+. + # * :round_mode - Determine how rounding is performed + # (defaults to :default. See BigDecimal::mode) + # * :significant - If +true+, precision will be the number + # of significant_digits. If +false+, the number of fractional + # digits (defaults to +false+). + # * :separator - Sets the separator between the + # fractional and integer digits (defaults to "."). + # * :delimiter - Sets the thousands delimiter (defaults + # to ""). + # * :strip_insignificant_zeros - If +true+ removes + # insignificant zeros after the decimal separator (defaults to + # +false+). + # * :format - Specifies the format of the percentage + # string The number field is %n (defaults to "%n%"). + # + # ==== Examples + # + # number_to_percentage(100) # => "100.000%" + # number_to_percentage('98') # => "98.000%" + # number_to_percentage(100, precision: 0) # => "100%" + # number_to_percentage(1000, delimiter: '.', separator: ',') # => "1.000,000%" + # number_to_percentage(302.24398923423, precision: 5) # => "302.24399%" + # number_to_percentage(1000, locale: :fr) # => "1000,000%" + # number_to_percentage(1000, precision: nil) # => "1000%" + # number_to_percentage('98a') # => "98a%" + # number_to_percentage(100, format: '%n %') # => "100.000 %" + # number_to_percentage(302.24398923423, precision: 5, round_mode: :down) # => "302.24398%" + def number_to_percentage(number, options = {}) + NumberToPercentageConverter.convert(number, options) + end + + # Formats a +number+ with grouped thousands using +delimiter+ + # (e.g., 12,324). You can customize the format in the +options+ + # hash. + # + # ==== Options + # + # * :locale - Sets the locale to be used for formatting + # (defaults to current locale). + # * :delimiter - Sets the thousands delimiter (defaults + # to ","). + # * :separator - Sets the separator between the + # fractional and integer digits (defaults to "."). + # * :delimiter_pattern - Sets a custom regular expression used for + # deriving the placement of delimiter. Helpful when using currency formats + # like INR. + # + # ==== Examples + # + # number_to_delimited(12345678) # => "12,345,678" + # number_to_delimited('123456') # => "123,456" + # number_to_delimited(12345678.05) # => "12,345,678.05" + # number_to_delimited(12345678, delimiter: '.') # => "12.345.678" + # number_to_delimited(12345678, delimiter: ',') # => "12,345,678" + # number_to_delimited(12345678.05, separator: ' ') # => "12,345,678 05" + # number_to_delimited(12345678.05, locale: :fr) # => "12 345 678,05" + # number_to_delimited('112a') # => "112a" + # number_to_delimited(98765432.98, delimiter: ' ', separator: ',') + # # => "98 765 432,98" + # number_to_delimited("123456.78", + # delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/) + # # => "1,23,456.78" + def number_to_delimited(number, options = {}) + NumberToDelimitedConverter.convert(number, options) + end + + # Formats a +number+ with the specified level of + # :precision (e.g., 112.32 has a precision of 2 if + # +:significant+ is +false+, and 5 if +:significant+ is +true+). + # You can customize the format in the +options+ hash. + # + # ==== Options + # + # * :locale - Sets the locale to be used for formatting + # (defaults to current locale). + # * :precision - Sets the precision of the number + # (defaults to 3). Keeps the number's precision if +nil+. + # * :round_mode - Determine how rounding is performed + # (defaults to :default. See BigDecimal::mode) + # * :significant - If +true+, precision will be the number + # of significant_digits. If +false+, the number of fractional + # digits (defaults to +false+). + # * :separator - Sets the separator between the + # fractional and integer digits (defaults to "."). + # * :delimiter - Sets the thousands delimiter (defaults + # to ""). + # * :strip_insignificant_zeros - If +true+ removes + # insignificant zeros after the decimal separator (defaults to + # +false+). + # + # ==== Examples + # + # number_to_rounded(111.2345) # => "111.235" + # number_to_rounded(111.2345, precision: 2) # => "111.23" + # number_to_rounded(13, precision: 5) # => "13.00000" + # number_to_rounded(389.32314, precision: 0) # => "389" + # number_to_rounded(111.2345, significant: true) # => "111" + # number_to_rounded(111.2345, precision: 1, significant: true) # => "100" + # number_to_rounded(13, precision: 5, significant: true) # => "13.000" + # number_to_rounded(13, precision: nil) # => "13" + # number_to_rounded(389.32314, precision: 0, round_mode: :up) # => "390" + # number_to_rounded(111.234, locale: :fr) # => "111,234" + # + # number_to_rounded(13, precision: 5, significant: true, strip_insignificant_zeros: true) + # # => "13" + # + # number_to_rounded(389.32314, precision: 4, significant: true) # => "389.3" + # number_to_rounded(1111.2345, precision: 2, separator: ',', delimiter: '.') + # # => "1.111,23" + def number_to_rounded(number, options = {}) + NumberToRoundedConverter.convert(number, options) + end + + # Formats the bytes in +number+ into a more understandable + # representation (e.g., giving it 1500 yields 1.46 KB). This + # method is useful for reporting file sizes to users. You can + # customize the format in the +options+ hash. + # + # See number_to_human if you want to pretty-print a + # generic number. + # + # ==== Options + # + # * :locale - Sets the locale to be used for formatting + # (defaults to current locale). + # * :precision - Sets the precision of the number + # (defaults to 3). + # * :round_mode - Determine how rounding is performed + # (defaults to :default. See BigDecimal::mode) + # * :significant - If +true+, precision will be the number + # of significant_digits. If +false+, the number of fractional + # digits (defaults to +true+) + # * :separator - Sets the separator between the + # fractional and integer digits (defaults to "."). + # * :delimiter - Sets the thousands delimiter (defaults + # to ""). + # * :strip_insignificant_zeros - If +true+ removes + # insignificant zeros after the decimal separator (defaults to + # +true+) + # + # ==== Examples + # + # number_to_human_size(123) # => "123 Bytes" + # number_to_human_size(1234) # => "1.21 KB" + # number_to_human_size(12345) # => "12.1 KB" + # number_to_human_size(1234567) # => "1.18 MB" + # number_to_human_size(1234567890) # => "1.15 GB" + # number_to_human_size(1234567890123) # => "1.12 TB" + # number_to_human_size(1234567890123456) # => "1.1 PB" + # number_to_human_size(1234567890123456789) # => "1.07 EB" + # number_to_human_size(1234567, precision: 2) # => "1.2 MB" + # number_to_human_size(483989, precision: 2) # => "470 KB" + # number_to_human_size(483989, precision: 2, round_mode: :up) # => "480 KB" + # number_to_human_size(1234567, precision: 2, separator: ',') # => "1,2 MB" + # number_to_human_size(1234567890123, precision: 5) # => "1.1228 TB" + # number_to_human_size(524288000, precision: 5) # => "500 MB" + def number_to_human_size(number, options = {}) + NumberToHumanSizeConverter.convert(number, options) + end + + # Pretty prints (formats and approximates) a number in a way it + # is more readable by humans (e.g.: 1200000000 becomes "1.2 + # Billion"). This is useful for numbers that can get very large + # (and too hard to read). + # + # See number_to_human_size if you want to print a file + # size. + # + # You can also define your own unit-quantifier names if you want + # to use other decimal units (e.g.: 1500 becomes "1.5 + # kilometers", 0.150 becomes "150 milliliters", etc). You may + # define a wide range of unit quantifiers, even fractional ones + # (centi, deci, mili, etc). + # + # ==== Options + # + # * :locale - Sets the locale to be used for formatting + # (defaults to current locale). + # * :precision - Sets the precision of the number + # (defaults to 3). + # * :round_mode - Determine how rounding is performed + # (defaults to :default. See BigDecimal::mode) + # * :significant - If +true+, precision will be the number + # of significant_digits. If +false+, the number of fractional + # digits (defaults to +true+) + # * :separator - Sets the separator between the + # fractional and integer digits (defaults to "."). + # * :delimiter - Sets the thousands delimiter (defaults + # to ""). + # * :strip_insignificant_zeros - If +true+ removes + # insignificant zeros after the decimal separator (defaults to + # +true+) + # * :units - A Hash of unit quantifier names. Or a + # string containing an i18n scope where to find this hash. It + # might have the following keys: + # * *integers*: :unit, :ten, + # :hundred, :thousand, :million, + # :billion, :trillion, + # :quadrillion + # * *fractionals*: :deci, :centi, + # :mili, :micro, :nano, + # :pico, :femto + # * :format - Sets the format of the output string + # (defaults to "%n %u"). The field types are: + # * %u - The quantifier (ex.: 'thousand') + # * %n - The number + # + # ==== Examples + # + # number_to_human(123) # => "123" + # number_to_human(1234) # => "1.23 Thousand" + # number_to_human(12345) # => "12.3 Thousand" + # number_to_human(1234567) # => "1.23 Million" + # number_to_human(1234567890) # => "1.23 Billion" + # number_to_human(1234567890123) # => "1.23 Trillion" + # number_to_human(1234567890123456) # => "1.23 Quadrillion" + # number_to_human(1234567890123456789) # => "1230 Quadrillion" + # number_to_human(489939, precision: 2) # => "490 Thousand" + # number_to_human(489939, precision: 4) # => "489.9 Thousand" + # number_to_human(489939, precision: 2 + # , round_mode: :down) # => "480 Thousand" + # number_to_human(1234567, precision: 4, + # significant: false) # => "1.2346 Million" + # number_to_human(1234567, precision: 1, + # separator: ',', + # significant: false) # => "1,2 Million" + # + # number_to_human(500000000, precision: 5) # => "500 Million" + # number_to_human(12345012345, significant: false) # => "12.345 Billion" + # + # Non-significant zeros after the decimal separator are stripped + # out by default (set :strip_insignificant_zeros to + # +false+ to change that): + # + # number_to_human(12.00001) # => "12" + # number_to_human(12.00001, strip_insignificant_zeros: false) # => "12.0" + # + # ==== Custom Unit Quantifiers + # + # You can also use your own custom unit quantifiers: + # number_to_human(500000, units: { unit: 'ml', thousand: 'lt' }) # => "500 lt" + # + # If in your I18n locale you have: + # + # distance: + # centi: + # one: "centimeter" + # other: "centimeters" + # unit: + # one: "meter" + # other: "meters" + # thousand: + # one: "kilometer" + # other: "kilometers" + # billion: "gazillion-distance" + # + # Then you could do: + # + # number_to_human(543934, units: :distance) # => "544 kilometers" + # number_to_human(54393498, units: :distance) # => "54400 kilometers" + # number_to_human(54393498000, units: :distance) # => "54.4 gazillion-distance" + # number_to_human(343, units: :distance, precision: 1) # => "300 meters" + # number_to_human(1, units: :distance) # => "1 meter" + # number_to_human(0.34, units: :distance) # => "34 centimeters" + def number_to_human(number, options = {}) + NumberToHumanConverter.convert(number, options) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_converter.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_converter.rb new file mode 100644 index 0000000..168b1a5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_converter.rb @@ -0,0 +1,181 @@ +# frozen_string_literal: true + +require "active_support/core_ext/big_decimal/conversions" +require "active_support/core_ext/object/blank" +require "active_support/core_ext/hash/keys" +require "active_support/i18n" +require "active_support/core_ext/class/attribute" + +module ActiveSupport + module NumberHelper + class NumberConverter # :nodoc: + # Default and i18n option namespace per class + class_attribute :namespace + + # Does the object need a number that is a valid float? + class_attribute :validate_float + + attr_reader :number, :opts + + DEFAULTS = { + # Used in number_to_delimited + # These are also the defaults for 'currency', 'percentage', 'precision', and 'human' + format: { + # Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5) + separator: ".", + # Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three) + delimiter: ",", + # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00) + precision: 3, + # If set to true, precision will mean the number of significant digits instead + # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2) + significant: false, + # If set, the zeros after the decimal separator will always be stripped (e.g.: 1.200 will be 1.2) + strip_insignificant_zeros: false + }, + + # Used in number_to_currency + currency: { + format: { + format: "%u%n", + negative_format: "-%u%n", + unit: "$", + # These five are to override number.format and are optional + separator: ".", + delimiter: ",", + precision: 2, + significant: false, + strip_insignificant_zeros: false + } + }, + + # Used in number_to_percentage + percentage: { + format: { + delimiter: "", + format: "%n%" + } + }, + + # Used in number_to_rounded + precision: { + format: { + delimiter: "" + } + }, + + # Used in number_to_human_size and number_to_human + human: { + format: { + # These five are to override number.format and are optional + delimiter: "", + precision: 3, + significant: true, + strip_insignificant_zeros: true + }, + # Used in number_to_human_size + storage_units: { + # Storage units output formatting. + # %u is the storage unit, %n is the number (default: 2 MB) + format: "%n %u", + units: { + byte: "Bytes", + kb: "KB", + mb: "MB", + gb: "GB", + tb: "TB" + } + }, + # Used in number_to_human + decimal_units: { + format: "%n %u", + # Decimal units output formatting + # By default we will only quantify some of the exponents + # but the commented ones might be defined or overridden + # by the user. + units: { + # femto: Quadrillionth + # pico: Trillionth + # nano: Billionth + # micro: Millionth + # mili: Thousandth + # centi: Hundredth + # deci: Tenth + unit: "", + # ten: + # one: Ten + # other: Tens + # hundred: Hundred + thousand: "Thousand", + million: "Million", + billion: "Billion", + trillion: "Trillion", + quadrillion: "Quadrillion" + } + } + } + } + + def self.convert(number, options) + new(number, options).execute + end + + def initialize(number, options) + @number = number + @opts = options.symbolize_keys + end + + def execute + if !number + nil + elsif validate_float? && !valid_float? + number + else + convert + end + end + + private + def options + @options ||= format_options.merge(opts) + end + + def format_options + default_format_options.merge!(i18n_format_options) + end + + def default_format_options + options = DEFAULTS[:format].dup + options.merge!(DEFAULTS[namespace][:format]) if namespace + options + end + + def i18n_format_options + locale = opts[:locale] + options = I18n.translate(:'number.format', locale: locale, default: {}).dup + + if namespace + options.merge!(I18n.translate(:"number.#{namespace}.format", locale: locale, default: {})) + end + + options + end + + def translate_number_value_with_default(key, **i18n_options) + I18n.translate(key, **{ default: default_value(key), scope: :number }.merge!(i18n_options)) + end + + def translate_in_locale(key, **i18n_options) + translate_number_value_with_default(key, **{ locale: options[:locale] }.merge(i18n_options)) + end + + def default_value(key) + key.split(".").reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] } + end + + def valid_float? + Float(number, exception: false) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_to_currency_converter.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_to_currency_converter.rb new file mode 100644 index 0000000..241ec90 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_to_currency_converter.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require "active_support/number_helper/number_converter" + +module ActiveSupport + module NumberHelper + class NumberToCurrencyConverter < NumberConverter # :nodoc: + self.namespace = :currency + + def convert + format = options[:format] + + number_f = valid_float? + if number_f + if number_f.negative? + number_f = number_f.abs + format = options[:negative_format] if (number_f * 10**options[:precision]) >= 0.5 + end + number_s = NumberToRoundedConverter.convert(number_f, options) + else + number_s = number.to_s.strip + format = options[:negative_format] if number_s.sub!(/^-/, "") + end + + format.gsub("%n", number_s).gsub("%u", options[:unit]) + end + + private + def options + @options ||= begin + defaults = default_format_options.merge(i18n_opts) + # Override negative format if format options are given + defaults[:negative_format] = "-#{opts[:format]}" if opts[:format] + defaults.merge!(opts) + end + end + + def i18n_opts + # Set International negative format if it does not exist + i18n = i18n_format_options + i18n[:negative_format] ||= "-#{i18n[:format]}" if i18n[:format] + i18n + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_to_delimited_converter.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_to_delimited_converter.rb new file mode 100644 index 0000000..4fb2fb7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_to_delimited_converter.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require "active_support/number_helper/number_converter" + +module ActiveSupport + module NumberHelper + class NumberToDelimitedConverter < NumberConverter # :nodoc: + self.validate_float = true + + DEFAULT_DELIMITER_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/ + + def convert + parts.join(options[:separator]) + end + + private + def parts + left, right = number.to_s.split(".") + left.gsub!(delimiter_pattern) do |digit_to_delimit| + "#{digit_to_delimit}#{options[:delimiter]}" + end + [left, right].compact + end + + def delimiter_pattern + options.fetch(:delimiter_pattern, DEFAULT_DELIMITER_REGEX) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_to_human_converter.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_to_human_converter.rb new file mode 100644 index 0000000..3f92628 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_to_human_converter.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require "active_support/number_helper/number_converter" + +module ActiveSupport + module NumberHelper + class NumberToHumanConverter < NumberConverter # :nodoc: + DECIMAL_UNITS = { 0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion, + -1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto } + INVERTED_DECIMAL_UNITS = DECIMAL_UNITS.invert + + self.namespace = :human + self.validate_float = true + + def convert # :nodoc: + @number = RoundingHelper.new(options).round(number) + @number = Float(number) + + # For backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files. + unless options.key?(:strip_insignificant_zeros) + options[:strip_insignificant_zeros] = true + end + + units = opts[:units] + exponent = calculate_exponent(units) + @number = number / (10**exponent) + + rounded_number = NumberToRoundedConverter.convert(number, options) + unit = determine_unit(units, exponent) + format.gsub("%n", rounded_number).gsub("%u", unit).strip + end + + private + def format + options[:format] || translate_in_locale("human.decimal_units.format") + end + + def determine_unit(units, exponent) + exp = DECIMAL_UNITS[exponent] + case units + when Hash + units[exp] || "" + when String, Symbol + I18n.translate("#{units}.#{exp}", locale: options[:locale], count: number.to_i) + else + translate_in_locale("human.decimal_units.units.#{exp}", count: number.to_i) + end + end + + def calculate_exponent(units) + exponent = number != 0 ? Math.log10(number.abs).floor : 0 + unit_exponents(units).find { |e| exponent >= e } || 0 + end + + def unit_exponents(units) + case units + when Hash + units + when String, Symbol + I18n.translate(units.to_s, locale: options[:locale], raise: true) + when nil + translate_in_locale("human.decimal_units.units", raise: true) + else + raise ArgumentError, ":units must be a Hash or String translation scope." + end.keys.map { |e_name| INVERTED_DECIMAL_UNITS[e_name] }.sort_by(&:-@) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_to_human_size_converter.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_to_human_size_converter.rb new file mode 100644 index 0000000..57e1ffe --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_to_human_size_converter.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require "active_support/number_helper/number_converter" + +module ActiveSupport + module NumberHelper + class NumberToHumanSizeConverter < NumberConverter # :nodoc: + STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb, :pb, :eb] + + self.namespace = :human + self.validate_float = true + + def convert + @number = Float(number) + + # For backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files. + unless options.key?(:strip_insignificant_zeros) + options[:strip_insignificant_zeros] = true + end + + if smaller_than_base? + number_to_format = number.to_i.to_s + else + human_size = number / (base**exponent) + number_to_format = NumberToRoundedConverter.convert(human_size, options) + end + conversion_format.gsub("%n", number_to_format).gsub("%u", unit) + end + + private + def conversion_format + translate_number_value_with_default("human.storage_units.format", locale: options[:locale], raise: true) + end + + def unit + translate_number_value_with_default(storage_unit_key, locale: options[:locale], count: number.to_i, raise: true) + end + + def storage_unit_key + key_end = smaller_than_base? ? "byte" : STORAGE_UNITS[exponent] + "human.storage_units.units.#{key_end}" + end + + def exponent + max = STORAGE_UNITS.size - 1 + exp = (Math.log(number) / Math.log(base)).to_i + exp = max if exp > max # avoid overflow for the highest unit + exp + end + + def smaller_than_base? + number.to_i < base + end + + def base + 1024 + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_to_percentage_converter.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_to_percentage_converter.rb new file mode 100644 index 0000000..0c2e190 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_to_percentage_converter.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require "active_support/number_helper/number_converter" + +module ActiveSupport + module NumberHelper + class NumberToPercentageConverter < NumberConverter # :nodoc: + self.namespace = :percentage + + def convert + rounded_number = NumberToRoundedConverter.convert(number, options) + options[:format].gsub("%n", rounded_number) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_to_phone_converter.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_to_phone_converter.rb new file mode 100644 index 0000000..c9771d0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_to_phone_converter.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require "active_support/number_helper/number_converter" + +module ActiveSupport + module NumberHelper + class NumberToPhoneConverter < NumberConverter # :nodoc: + def convert + str = country_code(opts[:country_code]).dup + str << convert_to_phone_number(number.to_s.strip) + str << phone_ext(opts[:extension]) + end + + private + def convert_to_phone_number(number) + if opts[:area_code] + convert_with_area_code(number) + else + convert_without_area_code(number) + end + end + + def convert_with_area_code(number) + default_pattern = /(\d{1,3})(\d{3})(\d{4}$)/ + number.gsub!(regexp_pattern(default_pattern), + "(\\1) \\2#{delimiter}\\3") + number + end + + def convert_without_area_code(number) + default_pattern = /(\d{0,3})(\d{3})(\d{4})$/ + number.gsub!(regexp_pattern(default_pattern), + "\\1#{delimiter}\\2#{delimiter}\\3") + number.slice!(0, 1) if start_with_delimiter?(number) + number + end + + def start_with_delimiter?(number) + delimiter.present? && number.start_with?(delimiter) + end + + def delimiter + opts[:delimiter] || "-" + end + + def country_code(code) + code.blank? ? "" : "+#{code}#{delimiter}" + end + + def phone_ext(ext) + ext.blank? ? "" : " x #{ext}" + end + + def regexp_pattern(default_pattern) + opts.fetch :pattern, default_pattern + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_to_rounded_converter.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_to_rounded_converter.rb new file mode 100644 index 0000000..f48a515 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/number_to_rounded_converter.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require "active_support/number_helper/number_converter" + +module ActiveSupport + module NumberHelper + class NumberToRoundedConverter < NumberConverter # :nodoc: + self.namespace = :precision + self.validate_float = true + + def convert + helper = RoundingHelper.new(options) + rounded_number = helper.round(number) + + if precision = options[:precision] + if options[:significant] && precision > 0 + digits = helper.digit_count(rounded_number) + precision -= digits + precision = 0 if precision < 0 # don't let it be negative + end + + formatted_string = + if rounded_number.finite? + s = rounded_number.to_s("F") + a, b = s.split(".", 2) + if precision != 0 + b << "0" * precision + a << "." + a << b[0, precision] + end + a + else + # Infinity/NaN + "%f" % rounded_number + end + else + formatted_string = rounded_number + end + + delimited_number = NumberToDelimitedConverter.convert(formatted_string, options) + format_number(delimited_number) + end + + private + def strip_insignificant_zeros + options[:strip_insignificant_zeros] + end + + def format_number(number) + if strip_insignificant_zeros + escaped_separator = Regexp.escape(options[:separator]) + number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, "") + else + number + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/rounding_helper.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/rounding_helper.rb new file mode 100644 index 0000000..14deca1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/number_helper/rounding_helper.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module ActiveSupport + module NumberHelper + class RoundingHelper # :nodoc: + attr_reader :options + + def initialize(options) + @options = options + end + + def round(number) + precision = absolute_precision(number) + return number unless precision + + rounded_number = convert_to_decimal(number).round(precision, options.fetch(:round_mode, :default).to_sym) + rounded_number.zero? ? rounded_number.abs : rounded_number # prevent showing negative zeros + end + + def digit_count(number) + return 1 if number.zero? + (Math.log10(number.abs) + 1).floor + end + + private + def convert_to_decimal(number) + case number + when Float, String + BigDecimal(number.to_s) + when Rational + BigDecimal(number, digit_count(number.to_i) + options[:precision]) + else + number.to_d + end + end + + def absolute_precision(number) + if options[:significant] && options[:precision] > 0 + options[:precision] - digit_count(convert_to_decimal(number)) + else + options[:precision] + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/option_merger.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/option_merger.rb new file mode 100644 index 0000000..b6dd126 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/option_merger.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require "active_support/core_ext/hash/deep_merge" + +module ActiveSupport + class OptionMerger # :nodoc: + instance_methods.each do |method| + undef_method(method) unless method.start_with?("__", "instance_eval", "class", "object_id") + end + + def initialize(context, options) + @context, @options = context, options + end + + private + def method_missing(method, *arguments, &block) + options = nil + if arguments.size == 1 && arguments.first.is_a?(Proc) + proc = arguments.shift + arguments << lambda { |*args| @options.deep_merge(proc.call(*args)) } + elsif arguments.last.respond_to?(:to_hash) + options = @options.deep_merge(arguments.pop) + else + options = @options + end + + if options + @context.__send__(method, *arguments, **options, &block) + else + @context.__send__(method, *arguments, &block) + end + end + + def respond_to_missing?(*arguments) + @context.respond_to?(*arguments) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/ordered_hash.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/ordered_hash.rb new file mode 100644 index 0000000..39505bc --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/ordered_hash.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require "yaml" + +YAML.add_builtin_type("omap") do |type, val| + ActiveSupport::OrderedHash[val.map { |v| v.to_a.first }] +end + +module ActiveSupport + # DEPRECATED: ActiveSupport::OrderedHash implements a hash that preserves + # insertion order. + # + # oh = ActiveSupport::OrderedHash.new + # oh[:a] = 1 + # oh[:b] = 2 + # oh.keys # => [:a, :b], this order is guaranteed + # + # Also, maps the +omap+ feature for YAML files + # (See https://yaml.org/type/omap.html) to support ordered items + # when loading from yaml. + # + # ActiveSupport::OrderedHash is namespaced to prevent conflicts + # with other implementations. + class OrderedHash < ::Hash # :nodoc: + def to_yaml_type + "!tag:yaml.org,2002:omap" + end + + def encode_with(coder) + coder.represent_seq "!omap", map { |k, v| { k => v } } + end + + def select(*args, &block) + dup.tap { |hash| hash.select!(*args, &block) } + end + + def reject(*args, &block) + dup.tap { |hash| hash.reject!(*args, &block) } + end + + def nested_under_indifferent_access + self + end + + # Returns true to make sure that this hash is extractable via Array#extract_options! + def extractable_options? + true + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/ordered_options.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/ordered_options.rb new file mode 100644 index 0000000..1a7f4d0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/ordered_options.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +require "active_support/core_ext/object/blank" + +module ActiveSupport + # +OrderedOptions+ inherits from +Hash+ and provides dynamic accessor methods. + # + # With a +Hash+, key-value pairs are typically managed like this: + # + # h = {} + # h[:boy] = 'John' + # h[:girl] = 'Mary' + # h[:boy] # => 'John' + # h[:girl] # => 'Mary' + # h[:dog] # => nil + # + # Using +OrderedOptions+, the above code can be written as: + # + # h = ActiveSupport::OrderedOptions.new + # h.boy = 'John' + # h.girl = 'Mary' + # h.boy # => 'John' + # h.girl # => 'Mary' + # h.dog # => nil + # + # To raise an exception when the value is blank, append a + # bang to the key name, like: + # + # h.dog! # => raises KeyError: :dog is blank + # + class OrderedOptions < Hash + alias_method :_get, :[] # preserve the original #[] method + protected :_get # make it protected + + def []=(key, value) + super(key.to_sym, value) + end + + def [](key) + super(key.to_sym) + end + + def method_missing(name, *args) + name_string = +name.to_s + if name_string.chomp!("=") + self[name_string] = args.first + else + bangs = name_string.chomp!("!") + + if bangs + self[name_string].presence || raise(KeyError.new(":#{name_string} is blank")) + else + self[name_string] + end + end + end + + def respond_to_missing?(name, include_private) + true + end + + def extractable_options? + true + end + + def inspect + "#<#{self.class.name} #{super}>" + end + end + + # +InheritableOptions+ provides a constructor to build an OrderedOptions + # hash inherited from another hash. + # + # Use this if you already have some hash and you want to create a new one based on it. + # + # h = ActiveSupport::InheritableOptions.new({ girl: 'Mary', boy: 'John' }) + # h.girl # => 'Mary' + # h.boy # => 'John' + class InheritableOptions < OrderedOptions + def initialize(parent = nil) + if parent.kind_of?(OrderedOptions) + # use the faster _get when dealing with OrderedOptions + super() { |h, k| parent._get(k) } + elsif parent + super() { |h, k| parent[k] } + else + super() + end + end + + def inheritable_copy + self.class.new(self) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/parameter_filter.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/parameter_filter.rb new file mode 100644 index 0000000..b2a19d6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/parameter_filter.rb @@ -0,0 +1,138 @@ +# frozen_string_literal: true + +require "active_support/core_ext/object/duplicable" + +module ActiveSupport + # +ParameterFilter+ allows you to specify keys for sensitive data from + # hash-like object and replace corresponding value. Filtering only certain + # sub-keys from a hash is possible by using the dot notation: + # 'credit_card.number'. If a proc is given, each key and value of a hash and + # all sub-hashes are passed to it, where the value or the key can be replaced + # using String#replace or similar methods. + # + # ActiveSupport::ParameterFilter.new([:password]) + # => replaces the value to all keys matching /password/i with "[FILTERED]" + # + # ActiveSupport::ParameterFilter.new([:foo, "bar"]) + # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]" + # + # ActiveSupport::ParameterFilter.new([/\Apin\z/i, /\Apin_/i]) + # => replaces the value for the exact (case-insensitive) key 'pin' and all + # (case-insensitive) keys beginning with 'pin_', with "[FILTERED]". + # Does not match keys with 'pin' as a substring, such as 'shipping_id'. + # + # ActiveSupport::ParameterFilter.new(["credit_card.code"]) + # => replaces { credit_card: {code: "xxxx"} } with "[FILTERED]", does not + # change { file: { code: "xxxx"} } + # + # ActiveSupport::ParameterFilter.new([-> (k, v) do + # v.reverse! if /secret/i.match?(k) + # end]) + # => reverses the value to all keys matching /secret/i + class ParameterFilter + FILTERED = "[FILTERED]" # :nodoc: + + # Create instance with given filters. Supported type of filters are +String+, +Regexp+, and +Proc+. + # Other types of filters are treated as +String+ using +to_s+. + # For +Proc+ filters, key, value, and optional original hash is passed to block arguments. + # + # ==== Options + # + # * :mask - A replaced object when filtered. Defaults to "[FILTERED]". + def initialize(filters = [], mask: FILTERED) + @filters = filters + @mask = mask + end + + # Mask value of +params+ if key matches one of filters. + def filter(params) + compiled_filter.call(params) + end + + # Returns filtered value for given key. For +Proc+ filters, third block argument is not populated. + def filter_param(key, value) + @filters.empty? ? value : compiled_filter.value_for_key(key, value) + end + + private + def compiled_filter + @compiled_filter ||= CompiledFilter.compile(@filters, mask: @mask) + end + + class CompiledFilter # :nodoc: + def self.compile(filters, mask:) + return lambda { |params| params.dup } if filters.empty? + + strings, regexps, blocks, deep_regexps, deep_strings = [], [], [], nil, nil + + filters.each do |item| + case item + when Proc + blocks << item + when Regexp + if item.to_s.include?("\\.") + (deep_regexps ||= []) << item + else + regexps << item + end + else + s = Regexp.escape(item.to_s) + if s.include?("\\.") + (deep_strings ||= []) << s + else + strings << s + end + end + end + + regexps << Regexp.new(strings.join("|"), true) unless strings.empty? + (deep_regexps ||= []) << Regexp.new(deep_strings.join("|"), true) if deep_strings&.any? + + new regexps, deep_regexps, blocks, mask: mask + end + + attr_reader :regexps, :deep_regexps, :blocks + + def initialize(regexps, deep_regexps, blocks, mask:) + @regexps = regexps + @deep_regexps = deep_regexps&.any? ? deep_regexps : nil + @blocks = blocks + @mask = mask + end + + def call(params, parents = [], original_params = params) + filtered_params = params.class.new + + params.each do |key, value| + filtered_params[key] = value_for_key(key, value, parents, original_params) + end + + filtered_params + end + + def value_for_key(key, value, parents = [], original_params = nil) + parents.push(key) if deep_regexps + if regexps.any? { |r| r.match?(key.to_s) } + value = @mask + elsif deep_regexps && (joined = parents.join(".")) && deep_regexps.any? { |r| r.match?(joined) } + value = @mask + elsif value.is_a?(Hash) + value = call(value, parents, original_params) + elsif value.is_a?(Array) + # If we don't pop the current parent it will be duplicated as we + # process each array value. + parents.pop if deep_regexps + value = value.map { |v| value_for_key(key, v, parents, original_params) } + # Restore the parent stack after processing the array. + parents.push(key) if deep_regexps + elsif blocks.any? + key = key.dup if key.duplicable? + value = value.dup if value.duplicable? + blocks.each { |b| b.arity == 2 ? b.call(key, value) : b.call(key, value, original_params) } + end + parents.pop if deep_regexps + value + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/per_thread_registry.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/per_thread_registry.rb new file mode 100644 index 0000000..cd8db91 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/per_thread_registry.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require "active_support/core_ext/module/delegation" + +module ActiveSupport + # NOTE: This approach has been deprecated for end-user code in favor of {thread_mattr_accessor}[rdoc-ref:Module#thread_mattr_accessor] and friends. + # Please use that approach instead. + # + # This module is used to encapsulate access to thread local variables. + # + # Instead of polluting the thread locals namespace: + # + # Thread.current[:connection_handler] + # + # you define a class that extends this module: + # + # module ActiveRecord + # class RuntimeRegistry + # extend ActiveSupport::PerThreadRegistry + # + # attr_accessor :connection_handler + # end + # end + # + # and invoke the declared instance accessors as class methods. So + # + # ActiveRecord::RuntimeRegistry.connection_handler = connection_handler + # + # sets a connection handler local to the current thread, and + # + # ActiveRecord::RuntimeRegistry.connection_handler + # + # returns a connection handler local to the current thread. + # + # This feature is accomplished by instantiating the class and storing the + # instance as a thread local keyed by the class name. In the example above + # a key "ActiveRecord::RuntimeRegistry" is stored in Thread.current. + # The class methods proxy to said thread local instance. + # + # If the class has an initializer, it must accept no arguments. + module PerThreadRegistry + def self.extended(object) + ActiveSupport::Deprecation.warn(<<~MSG) + ActiveSupport::PerThreadRegistry is deprecated and will be removed in Rails 7.1. + Use `Module#thread_mattr_accessor` instead. + MSG + object.instance_variable_set :@per_thread_registry_key, object.name.freeze + end + + def instance + Thread.current[@per_thread_registry_key] ||= new + end + + private + def method_missing(name, *args, &block) + # Caches the method definition as a singleton method of the receiver. + # + # By letting #delegate handle it, we avoid an enclosure that'll capture args. + singleton_class.delegate name, to: :instance + + send(name, *args, &block) + end + ruby2_keywords(:method_missing) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/proxy_object.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/proxy_object.rb new file mode 100644 index 0000000..0965fcd --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/proxy_object.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module ActiveSupport + # A class with no predefined methods that behaves similarly to Builder's + # BlankSlate. Used for proxy classes. + class ProxyObject < ::BasicObject + undef_method :== + undef_method :equal? + + # Let ActiveSupport::ProxyObject at least raise exceptions. + def raise(*args) + ::Object.send(:raise, *args) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/rails.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/rails.rb new file mode 100644 index 0000000..75676a2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/rails.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# This is a private interface. +# +# Rails components cherry pick from Active Support as needed, but there are a +# few features that are used for sure in some way or another and it is not worth +# putting individual requires absolutely everywhere. Think blank? for example. +# +# This file is loaded by every Rails component except Active Support itself, +# but it does not belong to the Rails public interface. It is internal to +# Rails and can change anytime. + +# Defines Object#blank? and Object#present?. +require "active_support/core_ext/object/blank" + +# Support for ClassMethods and the included macro. +require "active_support/concern" + +# Defines Class#class_attribute. +require "active_support/core_ext/class/attribute" + +# Defines Module#delegate. +require "active_support/core_ext/module/delegation" + +# Defines ActiveSupport::Deprecation. +require "active_support/deprecation" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/railtie.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/railtie.rb new file mode 100644 index 0000000..e86aa43 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/railtie.rb @@ -0,0 +1,152 @@ +# frozen_string_literal: true + +require "active_support" +require "active_support/i18n_railtie" + +module ActiveSupport + class Railtie < Rails::Railtie # :nodoc: + config.active_support = ActiveSupport::OrderedOptions.new + config.active_support.disable_to_s_conversion = false + + config.eager_load_namespaces << ActiveSupport + + initializer "active_support.isolation_level" do |app| + config.after_initialize do + if level = app.config.active_support.delete(:isolation_level) + ActiveSupport::IsolatedExecutionState.isolation_level = level + end + end + end + + initializer "active_support.remove_deprecated_time_with_zone_name" do |app| + config.after_initialize do + if app.config.active_support.remove_deprecated_time_with_zone_name + require "active_support/time_with_zone" + TimeWithZone.singleton_class.remove_method(:name) + end + end + end + + initializer "active_support.set_authenticated_message_encryption" do |app| + config.after_initialize do + unless app.config.active_support.use_authenticated_message_encryption.nil? + ActiveSupport::MessageEncryptor.use_authenticated_message_encryption = + app.config.active_support.use_authenticated_message_encryption + end + end + end + + initializer "active_support.reset_execution_context" do |app| + app.reloader.before_class_unload { ActiveSupport::ExecutionContext.clear } + app.executor.to_run { ActiveSupport::ExecutionContext.clear } + app.executor.to_complete { ActiveSupport::ExecutionContext.clear } + end + + initializer "active_support.reset_all_current_attributes_instances" do |app| + app.reloader.before_class_unload { ActiveSupport::CurrentAttributes.clear_all } + app.executor.to_run { ActiveSupport::CurrentAttributes.reset_all } + app.executor.to_complete { ActiveSupport::CurrentAttributes.reset_all } + + ActiveSupport.on_load(:active_support_test_case) do + if app.config.active_support.executor_around_test_case + require "active_support/executor/test_helper" + include ActiveSupport::Executor::TestHelper + else + require "active_support/current_attributes/test_helper" + include ActiveSupport::CurrentAttributes::TestHelper + + require "active_support/execution_context/test_helper" + include ActiveSupport::ExecutionContext::TestHelper + end + end + end + + initializer "active_support.deprecation_behavior" do |app| + if app.config.active_support.report_deprecations == false + ActiveSupport::Deprecation.silenced = true + ActiveSupport::Deprecation.behavior = :silence + ActiveSupport::Deprecation.disallowed_behavior = :silence + else + if deprecation = app.config.active_support.deprecation + ActiveSupport::Deprecation.behavior = deprecation + end + + if disallowed_deprecation = app.config.active_support.disallowed_deprecation + ActiveSupport::Deprecation.disallowed_behavior = disallowed_deprecation + end + + if disallowed_warnings = app.config.active_support.disallowed_deprecation_warnings + ActiveSupport::Deprecation.disallowed_warnings = disallowed_warnings + end + end + end + + # Sets the default value for Time.zone + # If assigned value cannot be matched to a TimeZone, an exception will be raised. + initializer "active_support.initialize_time_zone" do |app| + begin + TZInfo::DataSource.get + rescue TZInfo::DataSourceNotFound => e + raise e.exception "tzinfo-data is not present. Please add gem 'tzinfo-data' to your Gemfile and run bundle install" + end + require "active_support/core_ext/time/zones" + Time.zone_default = Time.find_zone!(app.config.time_zone) + end + + # Sets the default week start + # If assigned value is not a valid day symbol (e.g. :sunday, :monday, ...), an exception will be raised. + initializer "active_support.initialize_beginning_of_week" do |app| + require "active_support/core_ext/date/calculations" + beginning_of_week_default = Date.find_beginning_of_week!(app.config.beginning_of_week) + + Date.beginning_of_week_default = beginning_of_week_default + end + + initializer "active_support.require_master_key" do |app| + if app.config.respond_to?(:require_master_key) && app.config.require_master_key + begin + app.credentials.key + rescue ActiveSupport::EncryptedFile::MissingKeyError => error + $stderr.puts error.message + exit 1 + end + end + end + + initializer "active_support.set_error_reporter" do |app| + ActiveSupport.error_reporter = app.executor.error_reporter + end + + initializer "active_support.set_configs" do |app| + app.config.active_support.each do |k, v| + k = "#{k}=" + ActiveSupport.public_send(k, v) if ActiveSupport.respond_to? k + end + end + + initializer "active_support.set_hash_digest_class" do |app| + config.after_initialize do + if klass = app.config.active_support.hash_digest_class + ActiveSupport::Digest.hash_digest_class = klass + end + end + end + + initializer "active_support.set_key_generator_hash_digest_class" do |app| + config.after_initialize do + if klass = app.config.active_support.key_generator_hash_digest_class + ActiveSupport::KeyGenerator.hash_digest_class = klass + end + end + end + + initializer "active_support.set_rfc4122_namespaced_uuids" do |app| + config.after_initialize do + if app.config.active_support.use_rfc4122_namespaced_uuids + require "active_support/core_ext/digest" + ::Digest::UUID.use_rfc4122_namespaced_uuids = app.config.active_support.use_rfc4122_namespaced_uuids + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/reloader.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/reloader.rb new file mode 100644 index 0000000..e751866 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/reloader.rb @@ -0,0 +1,130 @@ +# frozen_string_literal: true + +require "active_support/execution_wrapper" +require "active_support/executor" + +module ActiveSupport + #-- + # This class defines several callbacks: + # + # to_prepare -- Run once at application startup, and also from + # +to_run+. + # + # to_run -- Run before a work run that is reloading. If + # +reload_classes_only_on_change+ is true (the default), the class + # unload will have already occurred. + # + # to_complete -- Run after a work run that has reloaded. If + # +reload_classes_only_on_change+ is false, the class unload will + # have occurred after the work run, but before this callback. + # + # before_class_unload -- Run immediately before the classes are + # unloaded. + # + # after_class_unload -- Run immediately after the classes are + # unloaded. + # + class Reloader < ExecutionWrapper + define_callbacks :prepare + + define_callbacks :class_unload + + # Registers a callback that will run once at application startup and every time the code is reloaded. + def self.to_prepare(*args, &block) + set_callback(:prepare, *args, &block) + end + + # Registers a callback that will run immediately before the classes are unloaded. + def self.before_class_unload(*args, &block) + set_callback(:class_unload, *args, &block) + end + + # Registers a callback that will run immediately after the classes are unloaded. + def self.after_class_unload(*args, &block) + set_callback(:class_unload, :after, *args, &block) + end + + to_run(:after) { self.class.prepare! } + + # Initiate a manual reload + def self.reload! + executor.wrap do + new.tap do |instance| + instance.run! + ensure + instance.complete! + end + end + prepare! + end + + def self.run!(reset: false) # :nodoc: + if check! + super + else + Null + end + end + + # Run the supplied block as a work unit, reloading code as needed + def self.wrap + executor.wrap do + super + end + end + + class_attribute :executor, default: Executor + class_attribute :check, default: lambda { false } + + def self.check! # :nodoc: + @should_reload ||= check.call + end + + def self.reloaded! # :nodoc: + @should_reload = false + end + + def self.prepare! # :nodoc: + new.run_callbacks(:prepare) + end + + def initialize + super + @locked = false + end + + # Acquire the ActiveSupport::Dependencies::Interlock unload lock, + # ensuring it will be released automatically + def require_unload_lock! + unless @locked + ActiveSupport::Dependencies.interlock.start_unloading + @locked = true + end + end + + # Release the unload lock if it has been previously obtained + def release_unload_lock! + if @locked + @locked = false + ActiveSupport::Dependencies.interlock.done_unloading + end + end + + def run! # :nodoc: + super + release_unload_lock! + end + + def class_unload!(&block) # :nodoc: + require_unload_lock! + run_callbacks(:class_unload, &block) + end + + def complete! # :nodoc: + super + self.class.reloaded! + ensure + release_unload_lock! + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/rescuable.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/rescuable.rb new file mode 100644 index 0000000..3199d77 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/rescuable.rb @@ -0,0 +1,174 @@ +# frozen_string_literal: true + +require "active_support/concern" +require "active_support/core_ext/class/attribute" +require "active_support/core_ext/string/inflections" + +module ActiveSupport + # Rescuable module adds support for easier exception handling. + module Rescuable + extend Concern + + included do + class_attribute :rescue_handlers, default: [] + end + + module ClassMethods + # Registers exception classes with a handler to be called by rescue_with_handler. + # + # rescue_from receives a series of exception classes or class + # names, and an exception handler specified by a trailing :with + # option containing the name of a method or a Proc object. Alternatively, a block + # can be given as the handler. + # + # Handlers that take one argument will be called with the exception, so + # that the exception can be inspected when dealing with it. + # + # Handlers are inherited. They are searched from right to left, from + # bottom to top, and up the hierarchy. The handler of the first class for + # which exception.is_a?(klass) holds true is the one invoked, if + # any. + # + # class ApplicationController < ActionController::Base + # rescue_from User::NotAuthorized, with: :deny_access + # rescue_from ActiveRecord::RecordInvalid, with: :show_record_errors + # + # rescue_from "MyApp::BaseError" do |exception| + # redirect_to root_url, alert: exception.message + # end + # + # private + # def deny_access + # head :forbidden + # end + # + # def show_record_errors(exception) + # redirect_back_or_to root_url, alert: exception.record.errors.full_messages.to_sentence + # end + # end + # + # Exceptions raised inside exception handlers are not propagated up. + def rescue_from(*klasses, with: nil, &block) + unless with + if block_given? + with = block + else + raise ArgumentError, "Need a handler. Pass the with: keyword argument or provide a block." + end + end + + klasses.each do |klass| + key = if klass.is_a?(Module) && klass.respond_to?(:===) + klass.name + elsif klass.is_a?(String) + klass + else + raise ArgumentError, "#{klass.inspect} must be an Exception class or a String referencing an Exception class" + end + + # Put the new handler at the end because the list is read in reverse. + self.rescue_handlers += [[key, with]] + end + end + + # Matches an exception to a handler based on the exception class. + # + # If no handler matches the exception, check for a handler matching the + # (optional) +exception.cause+. If no handler matches the exception or its + # cause, this returns +nil+, so you can deal with unhandled exceptions. + # Be sure to re-raise unhandled exceptions if this is what you expect. + # + # begin + # # ... + # rescue => exception + # rescue_with_handler(exception) || raise + # end + # + # Returns the exception if it was handled and +nil+ if it was not. + def rescue_with_handler(exception, object: self, visited_exceptions: []) + visited_exceptions << exception + + if handler = handler_for_rescue(exception, object: object) + handler.call exception + exception + elsif exception + if visited_exceptions.include?(exception.cause) + nil + else + rescue_with_handler(exception.cause, object: object, visited_exceptions: visited_exceptions) + end + end + end + + def handler_for_rescue(exception, object: self) # :nodoc: + case rescuer = find_rescue_handler(exception) + when Symbol + method = object.method(rescuer) + if method.arity == 0 + -> e { method.call } + else + method + end + when Proc + if rescuer.arity == 0 + -> e { object.instance_exec(&rescuer) } + else + -> e { object.instance_exec(e, &rescuer) } + end + end + end + + private + def find_rescue_handler(exception) + if exception + # Handlers are in order of declaration but the most recently declared + # is the highest priority match, so we search for matching handlers + # in reverse. + _, handler = rescue_handlers.reverse_each.detect do |class_or_name, _| + if klass = constantize_rescue_handler_class(class_or_name) + klass === exception + end + end + + handler + end + end + + def constantize_rescue_handler_class(class_or_name) + case class_or_name + when String, Symbol + begin + # Try a lexical lookup first since we support + # + # class Super + # rescue_from 'Error', with: … + # end + # + # class Sub + # class Error < StandardError; end + # end + # + # so an Error raised in Sub will hit the 'Error' handler. + const_get class_or_name + rescue NameError + class_or_name.safe_constantize + end + else + class_or_name + end + end + end + + # Delegates to the class method, but uses the instance as the subject for + # rescue_from handlers (method calls, +instance_exec+ blocks). + def rescue_with_handler(exception) + self.class.rescue_with_handler exception, object: self + end + + # Internal handler lookup. Delegates to class method. Some libraries call + # this directly, so keeping it around for compatibility. + def handler_for_rescue(exception) # :nodoc: + self.class.handler_for_rescue exception, object: self + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/ruby_features.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/ruby_features.rb new file mode 100644 index 0000000..8cdb89c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/ruby_features.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module ActiveSupport + module RubyFeatures # :nodoc: + CLASS_SUBCLASSES = Class.method_defined?(:subclasses) # RUBY_VERSION >= "3.1" + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/secure_compare_rotator.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/secure_compare_rotator.rb new file mode 100644 index 0000000..982ebf1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/secure_compare_rotator.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require "active_support/security_utils" +require "active_support/messages/rotator" + +module ActiveSupport + # The ActiveSupport::SecureCompareRotator is a wrapper around ActiveSupport::SecurityUtils.secure_compare + # and allows you to rotate a previously defined value to a new one. + # + # It can be used as follow: + # + # rotator = ActiveSupport::SecureCompareRotator.new('new_production_value') + # rotator.rotate('previous_production_value') + # rotator.secure_compare!('previous_production_value') + # + # One real use case example would be to rotate a basic auth credentials: + # + # class MyController < ApplicationController + # def authenticate_request + # rotator = ActiveSupport::SecureCompareRotator.new('new_password') + # rotator.rotate('old_password') + # + # authenticate_or_request_with_http_basic do |username, password| + # rotator.secure_compare!(password) + # rescue ActiveSupport::SecureCompareRotator::InvalidMatch + # false + # end + # end + # end + class SecureCompareRotator + include SecurityUtils + prepend Messages::Rotator + + InvalidMatch = Class.new(StandardError) + + def initialize(value, **_options) + @value = value + end + + def secure_compare!(other_value, on_rotation: @on_rotation) + secure_compare(@value, other_value) || + run_rotations(on_rotation) { |wrapper| wrapper.secure_compare!(other_value) } || + raise(InvalidMatch) + end + + private + def build_rotation(previous_value, _options) + self.class.new(previous_value) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/security_utils.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/security_utils.rb new file mode 100644 index 0000000..aa00474 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/security_utils.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module ActiveSupport + module SecurityUtils + # Constant time string comparison, for fixed length strings. + # + # The values compared should be of fixed length, such as strings + # that have already been processed by HMAC. Raises in case of length mismatch. + + if defined?(OpenSSL.fixed_length_secure_compare) + def fixed_length_secure_compare(a, b) + OpenSSL.fixed_length_secure_compare(a, b) + end + else + def fixed_length_secure_compare(a, b) + raise ArgumentError, "string length mismatch." unless a.bytesize == b.bytesize + + l = a.unpack "C#{a.bytesize}" + + res = 0 + b.each_byte { |byte| res |= byte ^ l.shift } + res == 0 + end + end + module_function :fixed_length_secure_compare + + # Secure string comparison for strings of variable length. + # + # While a timing attack would not be able to discern the content of + # a secret compared via secure_compare, it is possible to determine + # the secret length. This should be considered when using secure_compare + # to compare weak, short secrets to user input. + def secure_compare(a, b) + a.bytesize == b.bytesize && fixed_length_secure_compare(a, b) + end + module_function :secure_compare + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/string_inquirer.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/string_inquirer.rb new file mode 100644 index 0000000..8c4bf55 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/string_inquirer.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module ActiveSupport + # Wrapping a string in this class gives you a prettier way to test + # for equality. The value returned by Rails.env is wrapped + # in a StringInquirer object, so instead of calling this: + # + # Rails.env == 'production' + # + # you can call this: + # + # Rails.env.production? + # + # == Instantiating a new StringInquirer + # + # vehicle = ActiveSupport::StringInquirer.new('car') + # vehicle.car? # => true + # vehicle.bike? # => false + class StringInquirer < String + private + def respond_to_missing?(method_name, include_private = false) + method_name.end_with?("?") || super + end + + def method_missing(method_name, *arguments) + if method_name.end_with?("?") + self == method_name[0..-2] + else + super + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/subscriber.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/subscriber.rb new file mode 100644 index 0000000..0f71443 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/subscriber.rb @@ -0,0 +1,163 @@ +# frozen_string_literal: true + +require "active_support/notifications" + +module ActiveSupport + # ActiveSupport::Subscriber is an object set to consume + # ActiveSupport::Notifications. The subscriber dispatches notifications to + # a registered object based on its given namespace. + # + # An example would be an Active Record subscriber responsible for collecting + # statistics about queries: + # + # module ActiveRecord + # class StatsSubscriber < ActiveSupport::Subscriber + # attach_to :active_record + # + # def sql(event) + # Statsd.timing("sql.#{event.payload[:name]}", event.duration) + # end + # end + # end + # + # After configured, whenever a "sql.active_record" notification is published, + # it will properly dispatch the event (ActiveSupport::Notifications::Event) to + # the +sql+ method. + # + # We can detach a subscriber as well: + # + # ActiveRecord::StatsSubscriber.detach_from(:active_record) + class Subscriber + class << self + # Attach the subscriber to a namespace. + def attach_to(namespace, subscriber = new, notifier = ActiveSupport::Notifications, inherit_all: false) + @namespace = namespace + @subscriber = subscriber + @notifier = notifier + @inherit_all = inherit_all + + subscribers << subscriber + + # Add event subscribers for all existing methods on the class. + fetch_public_methods(subscriber, inherit_all).each do |event| + add_event_subscriber(event) + end + end + + # Detach the subscriber from a namespace. + def detach_from(namespace, notifier = ActiveSupport::Notifications) + @namespace = namespace + @subscriber = find_attached_subscriber + @notifier = notifier + + return unless subscriber + + subscribers.delete(subscriber) + + # Remove event subscribers of all existing methods on the class. + fetch_public_methods(subscriber, true).each do |event| + remove_event_subscriber(event) + end + + # Reset notifier so that event subscribers will not add for new methods added to the class. + @notifier = nil + end + + # Adds event subscribers for all new methods added to the class. + def method_added(event) + # Only public methods are added as subscribers, and only if a notifier + # has been set up. This means that subscribers will only be set up for + # classes that call #attach_to. + if public_method_defined?(event) && notifier + add_event_subscriber(event) + end + end + + def subscribers + @@subscribers ||= [] + end + + private + attr_reader :subscriber, :notifier, :namespace + + def add_event_subscriber(event) # :doc: + return if invalid_event?(event) + + pattern = prepare_pattern(event) + + # Don't add multiple subscribers (e.g. if methods are redefined). + return if pattern_subscribed?(pattern) + + subscriber.patterns[pattern] = notifier.subscribe(pattern, subscriber) + end + + def remove_event_subscriber(event) # :doc: + return if invalid_event?(event) + + pattern = prepare_pattern(event) + + return unless pattern_subscribed?(pattern) + + notifier.unsubscribe(subscriber.patterns[pattern]) + subscriber.patterns.delete(pattern) + end + + def find_attached_subscriber + subscribers.find { |attached_subscriber| attached_subscriber.instance_of?(self) } + end + + def invalid_event?(event) + %i{ start finish }.include?(event.to_sym) + end + + def prepare_pattern(event) + "#{event}.#{namespace}" + end + + def pattern_subscribed?(pattern) + subscriber.patterns.key?(pattern) + end + + def fetch_public_methods(subscriber, inherit_all) + subscriber.public_methods(inherit_all) - Subscriber.public_instance_methods(true) + end + end + + attr_reader :patterns # :nodoc: + + def initialize + @queue_key = [self.class.name, object_id].join "-" + @patterns = {} + super + end + + def start(name, id, payload) + event = ActiveSupport::Notifications::Event.new(name, nil, nil, id, payload) + event.start! + parent = event_stack.last + parent << event if parent + + event_stack.push event + end + + def finish(name, id, payload) + event = event_stack.pop + event.finish! + event.payload.merge!(payload) + + method = name.split(".").first + send(method, event) + end + + def publish_event(event) # :nodoc: + method = event.name.split(".").first + send(method, event) + end + + private + def event_stack + registry = ActiveSupport::IsolatedExecutionState[:active_support_subscriber_queue_registry] ||= {} + registry[@queue_key] ||= [] + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/tagged_logging.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/tagged_logging.rb new file mode 100644 index 0000000..26852e5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/tagged_logging.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +require "active_support/core_ext/module/delegation" +require "active_support/core_ext/object/blank" +require "logger" +require "active_support/logger" + +module ActiveSupport + # Wraps any standard Logger object to provide tagging capabilities. + # + # May be called with a block: + # + # logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)) + # logger.tagged('BCX') { logger.info 'Stuff' } # Logs "[BCX] Stuff" + # logger.tagged('BCX', "Jason") { logger.info 'Stuff' } # Logs "[BCX] [Jason] Stuff" + # logger.tagged('BCX') { logger.tagged('Jason') { logger.info 'Stuff' } } # Logs "[BCX] [Jason] Stuff" + # + # If called without a block, a new logger will be returned with applied tags: + # + # logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)) + # logger.tagged("BCX").info "Stuff" # Logs "[BCX] Stuff" + # logger.tagged("BCX", "Jason").info "Stuff" # Logs "[BCX] [Jason] Stuff" + # logger.tagged("BCX").tagged("Jason").info "Stuff" # Logs "[BCX] [Jason] Stuff" + # + # This is used by the default Rails.logger as configured by Railties to make + # it easy to stamp log lines with subdomains, request ids, and anything else + # to aid debugging of multi-user production applications. + module TaggedLogging + module Formatter # :nodoc: + # This method is invoked when a log event occurs. + def call(severity, timestamp, progname, msg) + super(severity, timestamp, progname, "#{tags_text}#{msg}") + end + + def tagged(*tags) + new_tags = push_tags(*tags) + yield self + ensure + pop_tags(new_tags.size) + end + + def push_tags(*tags) + tags.flatten! + tags.reject!(&:blank?) + current_tags.concat tags + tags + end + + def pop_tags(size = 1) + current_tags.pop size + end + + def clear_tags! + current_tags.clear + end + + def current_tags + # We use our object ID here to avoid conflicting with other instances + thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}" + IsolatedExecutionState[thread_key] ||= [] + end + + def tags_text + tags = current_tags + if tags.one? + "[#{tags[0]}] " + elsif tags.any? + tags.collect { |tag| "[#{tag}] " }.join + end + end + end + + module LocalTagStorage # :nodoc: + attr_accessor :current_tags + + def self.extended(base) + base.current_tags = [] + end + end + + def self.new(logger) + logger = logger.clone + + if logger.formatter + logger.formatter = logger.formatter.dup + else + # Ensure we set a default formatter so we aren't extending nil! + logger.formatter = ActiveSupport::Logger::SimpleFormatter.new + end + + logger.formatter.extend Formatter + logger.extend(self) + end + + delegate :push_tags, :pop_tags, :clear_tags!, to: :formatter + + def tagged(*tags) + if block_given? + formatter.tagged(*tags) { yield self } + else + logger = ActiveSupport::TaggedLogging.new(self) + logger.formatter.extend LocalTagStorage + logger.push_tags(*formatter.current_tags, *tags) + logger + end + end + + def flush + clear_tags! + super if defined?(super) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/test_case.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/test_case.rb new file mode 100644 index 0000000..2df7b80 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/test_case.rb @@ -0,0 +1,155 @@ +# frozen_string_literal: true + +gem "minitest" # make sure we get the gem, not stdlib +require "minitest" +require "active_support/testing/tagged_logging" +require "active_support/testing/setup_and_teardown" +require "active_support/testing/assertions" +require "active_support/testing/deprecation" +require "active_support/testing/declarative" +require "active_support/testing/isolation" +require "active_support/testing/constant_lookup" +require "active_support/testing/time_helpers" +require "active_support/testing/file_fixtures" +require "active_support/testing/parallelization" +require "active_support/testing/parallelize_executor" +require "concurrent/utility/processor_counter" + +module ActiveSupport + class TestCase < ::Minitest::Test + Assertion = Minitest::Assertion + + class << self + # Sets the order in which test cases are run. + # + # ActiveSupport::TestCase.test_order = :random # => :random + # + # Valid values are: + # * +:random+ (to run tests in random order) + # * +:parallel+ (to run tests in parallel) + # * +:sorted+ (to run tests alphabetically by method name) + # * +:alpha+ (equivalent to +:sorted+) + def test_order=(new_order) + ActiveSupport.test_order = new_order + end + + # Returns the order in which test cases are run. + # + # ActiveSupport::TestCase.test_order # => :random + # + # Possible values are +:random+, +:parallel+, +:alpha+, +:sorted+. + # Defaults to +:random+. + def test_order + ActiveSupport.test_order ||= :random + end + + # Parallelizes the test suite. + # + # Takes a +workers+ argument that controls how many times the process + # is forked. For each process a new database will be created suffixed + # with the worker number. + # + # test-database-0 + # test-database-1 + # + # If ENV["PARALLEL_WORKERS"] is set the workers argument will be ignored + # and the environment variable will be used instead. This is useful for CI + # environments, or other environments where you may need more workers than + # you do for local testing. + # + # If the number of workers is set to +1+ or fewer, the tests will not be + # parallelized. + # + # If +workers+ is set to +:number_of_processors+, the number of workers will be + # set to the actual core count on the machine you are on. + # + # The default parallelization method is to fork processes. If you'd like to + # use threads instead you can pass with: :threads to the +parallelize+ + # method. Note the threaded parallelization does not create multiple + # database and will not work with system tests at this time. + # + # parallelize(workers: :number_of_processors, with: :threads) + # + # The threaded parallelization uses minitest's parallel executor directly. + # The processes parallelization uses a Ruby DRb server. + # + # Because parallelization presents an overhead, it is only enabled when the + # number of tests to run is above the +threshold+ param. The default value is + # 50, and it's configurable via +config.active_support.test_parallelization_threshold+. + def parallelize(workers: :number_of_processors, with: :processes, threshold: ActiveSupport.test_parallelization_threshold) + workers = Concurrent.physical_processor_count if workers == :number_of_processors + workers = ENV["PARALLEL_WORKERS"].to_i if ENV["PARALLEL_WORKERS"] + + return if workers <= 1 + + Minitest.parallel_executor = ActiveSupport::Testing::ParallelizeExecutor.new(size: workers, with: with, threshold: threshold) + end + + # Set up hook for parallel testing. This can be used if you have multiple + # databases or any behavior that needs to be run after the process is forked + # but before the tests run. + # + # Note: this feature is not available with the threaded parallelization. + # + # In your +test_helper.rb+ add the following: + # + # class ActiveSupport::TestCase + # parallelize_setup do + # # create databases + # end + # end + def parallelize_setup(&block) + ActiveSupport::Testing::Parallelization.after_fork_hook(&block) + end + + # Clean up hook for parallel testing. This can be used to drop databases + # if your app uses multiple write/read databases or other clean up before + # the tests finish. This runs before the forked process is closed. + # + # Note: this feature is not available with the threaded parallelization. + # + # In your +test_helper.rb+ add the following: + # + # class ActiveSupport::TestCase + # parallelize_teardown do + # # drop databases + # end + # end + def parallelize_teardown(&block) + ActiveSupport::Testing::Parallelization.run_cleanup_hook(&block) + end + end + + alias_method :method_name, :name + + include ActiveSupport::Testing::TaggedLogging + prepend ActiveSupport::Testing::SetupAndTeardown + include ActiveSupport::Testing::Assertions + include ActiveSupport::Testing::Deprecation + include ActiveSupport::Testing::TimeHelpers + include ActiveSupport::Testing::FileFixtures + extend ActiveSupport::Testing::Declarative + + # test/unit backwards compatibility methods + alias :assert_raise :assert_raises + alias :assert_not_empty :refute_empty + alias :assert_not_equal :refute_equal + alias :assert_not_in_delta :refute_in_delta + alias :assert_not_in_epsilon :refute_in_epsilon + alias :assert_not_includes :refute_includes + alias :assert_not_instance_of :refute_instance_of + alias :assert_not_kind_of :refute_kind_of + alias :assert_no_match :refute_match + alias :assert_not_nil :refute_nil + alias :assert_not_operator :refute_operator + alias :assert_not_predicate :refute_predicate + alias :assert_not_respond_to :refute_respond_to + alias :assert_not_same :refute_same + + ActiveSupport.run_load_hooks(:active_support_test_case, self) + + def inspect # :nodoc: + Object.instance_method(:to_s).bind_call(self) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/assertions.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/assertions.rb new file mode 100644 index 0000000..2e48ba1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/assertions.rb @@ -0,0 +1,265 @@ +# frozen_string_literal: true + +require "active_support/core_ext/enumerable" + +module ActiveSupport + module Testing + module Assertions + UNTRACKED = Object.new # :nodoc: + + # Asserts that an expression is not truthy. Passes if object is + # +nil+ or +false+. "Truthy" means "considered true in a conditional" + # like if foo. + # + # assert_not nil # => true + # assert_not false # => true + # assert_not 'foo' # => Expected "foo" to be nil or false + # + # An error message can be specified. + # + # assert_not foo, 'foo should be false' + def assert_not(object, message = nil) + message ||= "Expected #{mu_pp(object)} to be nil or false" + assert !object, message + end + + # Assertion that the block should not raise an exception. + # + # Passes if evaluated code in the yielded block raises no exception. + # + # assert_nothing_raised do + # perform_service(param: 'no_exception') + # end + def assert_nothing_raised + yield + rescue => error + raise Minitest::UnexpectedError.new(error) + end + + # Test numeric difference between the return value of an expression as a + # result of what is evaluated in the yielded block. + # + # assert_difference 'Article.count' do + # post :create, params: { article: {...} } + # end + # + # An arbitrary expression is passed in and evaluated. + # + # assert_difference 'Article.last.comments(:reload).size' do + # post :create, params: { comment: {...} } + # end + # + # An arbitrary positive or negative difference can be specified. + # The default is 1. + # + # assert_difference 'Article.count', -1 do + # post :delete, params: { id: ... } + # end + # + # An array of expressions can also be passed in and evaluated. + # + # assert_difference [ 'Article.count', 'Post.count' ], 2 do + # post :create, params: { article: {...} } + # end + # + # A hash of expressions/numeric differences can also be passed in and evaluated. + # + # assert_difference ->{ Article.count } => 1, ->{ Notification.count } => 2 do + # post :create, params: { article: {...} } + # end + # + # A lambda or a list of lambdas can be passed in and evaluated: + # + # assert_difference ->{ Article.count }, 2 do + # post :create, params: { article: {...} } + # end + # + # assert_difference [->{ Article.count }, ->{ Post.count }], 2 do + # post :create, params: { article: {...} } + # end + # + # An error message can be specified. + # + # assert_difference 'Article.count', -1, 'An Article should be destroyed' do + # post :delete, params: { id: ... } + # end + def assert_difference(expression, *args, &block) + expressions = + if expression.is_a?(Hash) + message = args[0] + expression + else + difference = args[0] || 1 + message = args[1] + Array(expression).index_with(difference) + end + + exps = expressions.keys.map { |e| + e.respond_to?(:call) ? e : lambda { eval(e, block.binding) } + } + before = exps.map(&:call) + + retval = _assert_nothing_raised_or_warn("assert_difference", &block) + + expressions.zip(exps, before) do |(code, diff), exp, before_value| + error = "#{code.inspect} didn't change by #{diff}" + error = "#{message}.\n#{error}" if message + assert_equal(before_value + diff, exp.call, error) + end + + retval + end + + # Assertion that the numeric result of evaluating an expression is not + # changed before and after invoking the passed in block. + # + # assert_no_difference 'Article.count' do + # post :create, params: { article: invalid_attributes } + # end + # + # A lambda can be passed in and evaluated. + # + # assert_no_difference -> { Article.count } do + # post :create, params: { article: invalid_attributes } + # end + # + # An error message can be specified. + # + # assert_no_difference 'Article.count', 'An Article should not be created' do + # post :create, params: { article: invalid_attributes } + # end + # + # An array of expressions can also be passed in and evaluated. + # + # assert_no_difference [ 'Article.count', -> { Post.count } ] do + # post :create, params: { article: invalid_attributes } + # end + def assert_no_difference(expression, message = nil, &block) + assert_difference expression, 0, message, &block + end + + # Assertion that the result of evaluating an expression is changed before + # and after invoking the passed in block. + # + # assert_changes 'Status.all_good?' do + # post :create, params: { status: { ok: false } } + # end + # + # You can pass the block as a string to be evaluated in the context of + # the block. A lambda can be passed for the block as well. + # + # assert_changes -> { Status.all_good? } do + # post :create, params: { status: { ok: false } } + # end + # + # The assertion is useful to test side effects. The passed block can be + # anything that can be converted to string with #to_s. + # + # assert_changes :@object do + # @object = 42 + # end + # + # The keyword arguments +:from+ and +:to+ can be given to specify the + # expected initial value and the expected value after the block was + # executed. + # + # assert_changes :@object, from: nil, to: :foo do + # @object = :foo + # end + # + # An error message can be specified. + # + # assert_changes -> { Status.all_good? }, 'Expected the status to be bad' do + # post :create, params: { status: { incident: true } } + # end + def assert_changes(expression, message = nil, from: UNTRACKED, to: UNTRACKED, &block) + exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) } + + before = exp.call + retval = _assert_nothing_raised_or_warn("assert_changes", &block) + + unless from == UNTRACKED + error = "Expected change from #{from.inspect}" + error = "#{message}.\n#{error}" if message + assert from === before, error + end + + after = exp.call + + error = "#{expression.inspect} didn't change" + error = "#{error}. It was already #{to}" if before == to + error = "#{message}.\n#{error}" if message + refute_equal before, after, error + + unless to == UNTRACKED + error = "Expected change to #{to}\n" + error = "#{message}.\n#{error}" if message + assert to === after, error + end + + retval + end + + # Assertion that the result of evaluating an expression is not changed before + # and after invoking the passed in block. + # + # assert_no_changes 'Status.all_good?' do + # post :create, params: { status: { ok: true } } + # end + # + # Provide the optional keyword argument :from to specify the expected + # initial value. + # + # assert_no_changes -> { Status.all_good? }, from: true do + # post :create, params: { status: { ok: true } } + # end + # + # An error message can be specified. + # + # assert_no_changes -> { Status.all_good? }, 'Expected the status to be good' do + # post :create, params: { status: { ok: false } } + # end + def assert_no_changes(expression, message = nil, from: UNTRACKED, &block) + exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) } + + before = exp.call + retval = _assert_nothing_raised_or_warn("assert_no_changes", &block) + + unless from == UNTRACKED + error = "Expected initial value of #{from.inspect}" + error = "#{message}.\n#{error}" if message + assert from === before, error + end + + after = exp.call + + error = "#{expression.inspect} changed" + error = "#{message}.\n#{error}" if message + + if before.nil? + assert_nil after, error + else + assert_equal before, after, error + end + + retval + end + + private + def _assert_nothing_raised_or_warn(assertion, &block) + assert_nothing_raised(&block) + rescue Minitest::UnexpectedError => e + if tagged_logger && tagged_logger.warn? + warning = <<~MSG + #{self.class} - #{name}: #{e.error.class} raised. + If you expected this exception, use `assert_raises` as near to the code that raises as possible. + Other block based assertions (e.g. `#{assertion}`) can be used, as long as `assert_raises` is inside their block. + MSG + tagged_logger.warn warning + end + + raise + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/autorun.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/autorun.rb new file mode 100644 index 0000000..889b416 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/autorun.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +gem "minitest" + +require "minitest" + +Minitest.autorun diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/constant_lookup.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/constant_lookup.rb new file mode 100644 index 0000000..51167e9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/constant_lookup.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require "active_support/concern" +require "active_support/inflector" + +module ActiveSupport + module Testing + # Resolves a constant from a minitest spec name. + # + # Given the following spec-style test: + # + # describe WidgetsController, :index do + # describe "authenticated user" do + # describe "returns widgets" do + # it "has a controller that exists" do + # assert_kind_of WidgetsController, @controller + # end + # end + # end + # end + # + # The test will have the following name: + # + # "WidgetsController::index::authenticated user::returns widgets" + # + # The constant WidgetsController can be resolved from the name. + # The following code will resolve the constant: + # + # controller = determine_constant_from_test_name(name) do |constant| + # Class === constant && constant < ::ActionController::Metal + # end + module ConstantLookup + extend ::ActiveSupport::Concern + + module ClassMethods # :nodoc: + def determine_constant_from_test_name(test_name) + names = test_name.split "::" + while names.size > 0 do + names.last.sub!(/Test$/, "") + begin + constant = names.join("::").safe_constantize + break(constant) if yield(constant) + ensure + names.pop + end + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/declarative.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/declarative.rb new file mode 100644 index 0000000..7c34036 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/declarative.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module ActiveSupport + module Testing + module Declarative + unless defined?(Spec) + # Helper to define a test method using a String. Under the hood, it replaces + # spaces with underscores and defines the test method. + # + # test "verify something" do + # ... + # end + def test(name, &block) + test_name = "test_#{name.gsub(/\s+/, '_')}".to_sym + defined = method_defined? test_name + raise "#{test_name} is already defined in #{self}" if defined + if block_given? + define_method(test_name, &block) + else + define_method(test_name) do + flunk "No implementation provided for #{name}" + end + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/deprecation.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/deprecation.rb new file mode 100644 index 0000000..f762d24 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/deprecation.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +require "active_support/deprecation" + +module ActiveSupport + module Testing + module Deprecation + # Asserts that a matching deprecation warning was emitted by the given deprecator during the execution of the yielded block. + # + # assert_deprecated(/foo/, CustomDeprecator) do + # CustomDeprecator.warn "foo should no longer be used" + # end + # + # The +match+ object may be a +Regexp+, or +String+ appearing in the message. + # + # assert_deprecated('foo', CustomDeprecator) do + # CustomDeprecator.warn "foo should no longer be used" + # end + # + # If the +match+ is omitted (or explicitly +nil+), any deprecation warning will match. + # + # assert_deprecated(nil, CustomDeprecator) do + # CustomDeprecator.warn "foo should no longer be used" + # end + # + # If no +deprecator+ is given, defaults to ActiveSupport::Deprecation. + # + # assert_deprecated do + # ActiveSupport::Deprecation.warn "foo should no longer be used" + # end + def assert_deprecated(match = nil, deprecator = nil, &block) + result, warnings = collect_deprecations(deprecator, &block) + assert !warnings.empty?, "Expected a deprecation warning within the block but received none" + if match + match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp) + assert warnings.any? { |w| match.match?(w) }, "No deprecation warning matched #{match}: #{warnings.join(', ')}" + end + result + end + + # Asserts that no deprecation warnings are emitted by the given deprecator during the execution of the yielded block. + # + # assert_not_deprecated(CustomDeprecator) do + # CustomDeprecator.warn "message" # fails assertion + # end + # + # If no +deprecator+ is given, defaults to ActiveSupport::Deprecation. + # + # assert_not_deprecated do + # ActiveSupport::Deprecation.warn "message" # fails assertion + # end + # + # assert_not_deprecated do + # CustomDeprecator.warn "message" # passes assertion + # end + def assert_not_deprecated(deprecator = nil, &block) + result, deprecations = collect_deprecations(deprecator, &block) + assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}" + result + end + + # Returns an array of all the deprecation warnings emitted by the given + # +deprecator+ during the execution of the yielded block. + # + # collect_deprecations(CustomDeprecator) do + # CustomDeprecator.warn "message" + # end # => ["message"] + # + # If no +deprecator+ is given, defaults to ActiveSupport::Deprecation. + # + # collect_deprecations do + # CustomDeprecator.warn "custom message" + # ActiveSupport::Deprecation.warn "message" + # end # => ["message"] + def collect_deprecations(deprecator = nil) + deprecator ||= ActiveSupport::Deprecation + old_behavior = deprecator.behavior + deprecations = [] + deprecator.behavior = Proc.new do |message, callstack| + deprecations << message + end + result = yield + [result, deprecations] + ensure + deprecator.behavior = old_behavior + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/file_fixtures.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/file_fixtures.rb new file mode 100644 index 0000000..4eb7a88 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/file_fixtures.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require "active_support/concern" + +module ActiveSupport + module Testing + # Adds simple access to sample files called file fixtures. + # File fixtures are normal files stored in + # ActiveSupport::TestCase.file_fixture_path. + # + # File fixtures are represented as +Pathname+ objects. + # This makes it easy to extract specific information: + # + # file_fixture("example.txt").read # get the file's content + # file_fixture("example.mp3").size # get the file size + module FileFixtures + extend ActiveSupport::Concern + + included do + class_attribute :file_fixture_path, instance_writer: false + end + + # Returns a +Pathname+ to the fixture file named +fixture_name+. + # + # Raises +ArgumentError+ if +fixture_name+ can't be found. + def file_fixture(fixture_name) + path = Pathname.new(File.join(file_fixture_path, fixture_name)) + + if path.exist? + path + else + msg = "the directory '%s' does not contain a file named '%s'" + raise ArgumentError, msg % [file_fixture_path, fixture_name] + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/isolation.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/isolation.rb new file mode 100644 index 0000000..129ea69 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/isolation.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +module ActiveSupport + module Testing + module Isolation + require "thread" + + def self.included(klass) # :nodoc: + klass.class_eval do + parallelize_me! + end + end + + def self.forking_env? + !ENV["NO_FORK"] && Process.respond_to?(:fork) + end + + def run + serialized = run_in_isolation do + super + end + + Marshal.load(serialized) + end + + module Forking + def run_in_isolation(&blk) + read, write = IO.pipe + read.binmode + write.binmode + + pid = fork do + read.close + yield + begin + if error? + failures.map! { |e| + begin + Marshal.dump e + e + rescue TypeError + ex = Exception.new e.message + ex.set_backtrace e.backtrace + Minitest::UnexpectedError.new ex + end + } + end + test_result = defined?(Minitest::Result) ? Minitest::Result.from(self) : dup + result = Marshal.dump(test_result) + end + + write.puts [result].pack("m") + exit! + end + + write.close + result = read.read + Process.wait2(pid) + result.unpack1("m") + end + end + + module Subprocess + ORIG_ARGV = ARGV.dup unless defined?(ORIG_ARGV) + + # Complicated H4X to get this working in windows / jruby with + # no forking. + def run_in_isolation(&blk) + require "tempfile" + + if ENV["ISOLATION_TEST"] + yield + test_result = defined?(Minitest::Result) ? Minitest::Result.from(self) : dup + File.open(ENV["ISOLATION_OUTPUT"], "w") do |file| + file.puts [Marshal.dump(test_result)].pack("m") + end + exit! + else + Tempfile.open("isolation") do |tmpfile| + env = { + "ISOLATION_TEST" => self.class.name, + "ISOLATION_OUTPUT" => tmpfile.path + } + + test_opts = "-n#{self.class.name}##{name}" + + load_path_args = [] + $-I.each do |p| + load_path_args << "-I" + load_path_args << File.expand_path(p) + end + + child = IO.popen([env, Gem.ruby, *load_path_args, $0, *ORIG_ARGV, test_opts]) + + begin + Process.wait(child.pid) + rescue Errno::ECHILD # The child process may exit before we wait + nil + end + + return tmpfile.read.unpack1("m") + end + end + end + end + + include forking_env? ? Forking : Subprocess + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/method_call_assertions.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/method_call_assertions.rb new file mode 100644 index 0000000..1d016b0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/method_call_assertions.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +require "minitest/mock" + +module ActiveSupport + module Testing + module MethodCallAssertions # :nodoc: + private + def assert_called(object, method_name, message = nil, times: 1, returns: nil, &block) + times_called = 0 + + object.stub(method_name, proc { times_called += 1; returns }, &block) + + error = "Expected #{method_name} to be called #{times} times, " \ + "but was called #{times_called} times" + error = "#{message}.\n#{error}" if message + assert_equal times, times_called, error + end + + def assert_called_with(object, method_name, args, returns: nil, &block) + mock = Minitest::Mock.new + + if args.all?(Array) + args.each { |arg| mock.expect(:call, returns, arg) } + else + mock.expect(:call, returns, args) + end + + object.stub(method_name, mock, &block) + + mock.verify + end + + def assert_not_called(object, method_name, message = nil, &block) + assert_called(object, method_name, message, times: 0, &block) + end + + def assert_called_on_instance_of(klass, method_name, message = nil, times: 1, returns: nil) + times_called = 0 + klass.define_method("stubbed_#{method_name}") do |*| + times_called += 1 + + returns + end + + klass.alias_method "original_#{method_name}", method_name + klass.alias_method method_name, "stubbed_#{method_name}" + + yield + + error = "Expected #{method_name} to be called #{times} times, but was called #{times_called} times" + error = "#{message}.\n#{error}" if message + + assert_equal times, times_called, error + ensure + klass.alias_method method_name, "original_#{method_name}" + klass.undef_method "original_#{method_name}" + klass.undef_method "stubbed_#{method_name}" + end + + def assert_not_called_on_instance_of(klass, method_name, message = nil, &block) + assert_called_on_instance_of(klass, method_name, message, times: 0, &block) + end + + def stub_any_instance(klass, instance: klass.new) + klass.stub(:new, instance) { yield instance } + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/parallelization.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/parallelization.rb new file mode 100644 index 0000000..d1b2734 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/parallelization.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require "drb" +require "drb/unix" unless Gem.win_platform? +require "active_support/core_ext/module/attribute_accessors" +require "active_support/testing/parallelization/server" +require "active_support/testing/parallelization/worker" + +module ActiveSupport + module Testing + class Parallelization # :nodoc: + @@after_fork_hooks = [] + + def self.after_fork_hook(&blk) + @@after_fork_hooks << blk + end + + cattr_reader :after_fork_hooks + + @@run_cleanup_hooks = [] + + def self.run_cleanup_hook(&blk) + @@run_cleanup_hooks << blk + end + + cattr_reader :run_cleanup_hooks + + def initialize(worker_count) + @worker_count = worker_count + @queue_server = Server.new + @worker_pool = [] + @url = DRb.start_service("drbunix:", @queue_server).uri + end + + def start + @worker_pool = @worker_count.times.map do |worker| + Worker.new(worker, @url).start + end + end + + def <<(work) + @queue_server << work + end + + def size + @worker_count + end + + def shutdown + @queue_server.shutdown + @worker_pool.each { |pid| Process.waitpid pid } + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/parallelization/server.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/parallelization/server.rb new file mode 100644 index 0000000..3961367 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/parallelization/server.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +require "drb" +require "drb/unix" unless Gem.win_platform? + +module ActiveSupport + module Testing + class Parallelization # :nodoc: + class Server + include DRb::DRbUndumped + + def initialize + @queue = Queue.new + @active_workers = Concurrent::Map.new + @in_flight = Concurrent::Map.new + end + + def record(reporter, result) + raise DRb::DRbConnError if result.is_a?(DRb::DRbUnknown) + + @in_flight.delete([result.klass, result.name]) + + reporter.synchronize do + reporter.record(result) + end + end + + def <<(o) + o[2] = DRbObject.new(o[2]) if o + @queue << o + end + + def pop + if test = @queue.pop + @in_flight[[test[0].to_s, test[1]]] = test + test + end + end + + def start_worker(worker_id) + @active_workers[worker_id] = true + end + + def stop_worker(worker_id) + @active_workers.delete(worker_id) + end + + def active_workers? + @active_workers.size > 0 + end + + def interrupt + @queue.clear + end + + def shutdown + # Wait for initial queue to drain + while @queue.length != 0 + sleep 0.1 + end + + @queue.close + + # Wait until all workers have finished + while active_workers? + sleep 0.1 + end + + @in_flight.values.each do |(klass, name, reporter)| + result = Minitest::Result.from(klass.new(name)) + error = RuntimeError.new("result not reported") + error.set_backtrace([""]) + result.failures << Minitest::UnexpectedError.new(error) + reporter.synchronize do + reporter.record(result) + end + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/parallelization/worker.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/parallelization/worker.rb new file mode 100644 index 0000000..393355a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/parallelization/worker.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true + +module ActiveSupport + module Testing + class Parallelization # :nodoc: + class Worker + def initialize(number, url) + @id = SecureRandom.uuid + @number = number + @url = url + @setup_exception = nil + end + + def start + fork do + set_process_title("(starting)") + + DRb.stop_service + + @queue = DRbObject.new_with_uri(@url) + @queue.start_worker(@id) + + begin + after_fork + rescue => @setup_exception; end + + work_from_queue + ensure + set_process_title("(stopping)") + + run_cleanup + @queue.stop_worker(@id) + end + end + + def work_from_queue + while job = @queue.pop + perform_job(job) + end + end + + def perform_job(job) + klass = job[0] + method = job[1] + reporter = job[2] + + set_process_title("#{klass}##{method}") + + result = klass.with_info_handler reporter do + Minitest.run_one_method(klass, method) + end + + safe_record(reporter, result) + end + + def safe_record(reporter, result) + add_setup_exception(result) if @setup_exception + + begin + @queue.record(reporter, result) + rescue DRb::DRbConnError + result.failures.map! do |failure| + if failure.respond_to?(:error) + # minitest >5.14.0 + error = DRb::DRbRemoteError.new(failure.error) + else + error = DRb::DRbRemoteError.new(failure.exception) + end + Minitest::UnexpectedError.new(error) + end + @queue.record(reporter, result) + rescue Interrupt + @queue.interrupt + raise + end + + set_process_title("(idle)") + end + + def after_fork + Parallelization.after_fork_hooks.each do |cb| + cb.call(@number) + end + end + + def run_cleanup + Parallelization.run_cleanup_hooks.each do |cb| + cb.call(@number) + end + end + + private + def add_setup_exception(result) + result.failures.prepend Minitest::UnexpectedError.new(@setup_exception) + end + + def set_process_title(status) + Process.setproctitle("Rails test worker #{@number} - #{status}") + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/parallelize_executor.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/parallelize_executor.rb new file mode 100644 index 0000000..88299db --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/parallelize_executor.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +module ActiveSupport + module Testing + class ParallelizeExecutor # :nodoc: + attr_reader :size, :parallelize_with, :threshold + + def initialize(size:, with:, threshold: ActiveSupport.test_parallelization_threshold) + @size = size + @parallelize_with = with + @threshold = threshold + end + + def start + parallelize if should_parallelize? + show_execution_info + + parallel_executor.start if parallelized? + end + + def <<(work) + parallel_executor << work if parallelized? + end + + def shutdown + parallel_executor.shutdown if parallelized? + end + + private + def parallel_executor + @parallel_executor ||= build_parallel_executor + end + + def build_parallel_executor + case parallelize_with + when :processes + Testing::Parallelization.new(size) + when :threads + ActiveSupport::TestCase.lock_threads = false if defined?(ActiveSupport::TestCase.lock_threads) + Minitest::Parallel::Executor.new(size) + else + raise ArgumentError, "#{parallelize_with} is not a supported parallelization executor." + end + end + + def parallelize + @parallelized = true + Minitest::Test.parallelize_me! + end + + def parallelized? + @parallelized if defined?(@parallelized) + end + + def should_parallelize? + ENV["PARALLEL_WORKERS"] || tests_count > threshold + end + + def tests_count + @tests_count ||= Minitest::Runnable.runnables.sum { |runnable| runnable.runnable_methods.size } + end + + def show_execution_info + puts execution_info + end + + def execution_info + if parallelized? + "Running #{tests_count} tests in parallel using #{parallel_executor.size} #{parallelize_with}" + else + "Running #{tests_count} tests in a single process (parallelization threshold is #{threshold})" + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/setup_and_teardown.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/setup_and_teardown.rb new file mode 100644 index 0000000..35321cd --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/setup_and_teardown.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require "active_support/callbacks" + +module ActiveSupport + module Testing + # Adds support for +setup+ and +teardown+ callbacks. + # These callbacks serve as a replacement to overwriting the + # #setup and #teardown methods of your TestCase. + # + # class ExampleTest < ActiveSupport::TestCase + # setup do + # # ... + # end + # + # teardown do + # # ... + # end + # end + module SetupAndTeardown + def self.prepended(klass) + klass.include ActiveSupport::Callbacks + klass.define_callbacks :setup, :teardown + klass.extend ClassMethods + end + + module ClassMethods + # Add a callback, which runs before TestCase#setup. + def setup(*args, &block) + set_callback(:setup, :before, *args, &block) + end + + # Add a callback, which runs after TestCase#teardown. + def teardown(*args, &block) + set_callback(:teardown, :after, *args, &block) + end + end + + def before_setup # :nodoc: + super + run_callbacks :setup + end + + def after_teardown # :nodoc: + begin + run_callbacks :teardown + rescue => e + self.failures << Minitest::UnexpectedError.new(e) + end + + super + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/stream.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/stream.rb new file mode 100644 index 0000000..55017d3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/stream.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module ActiveSupport + module Testing + module Stream # :nodoc: + private + def silence_stream(stream) + old_stream = stream.dup + stream.reopen(IO::NULL) + stream.sync = true + yield + ensure + stream.reopen(old_stream) + old_stream.close + end + + def quietly(&block) + silence_stream(STDOUT) do + silence_stream(STDERR, &block) + end + end + + def capture(stream) + stream = stream.to_s + captured_stream = Tempfile.new(stream) + stream_io = eval("$#{stream}") + origin_stream = stream_io.dup + stream_io.reopen(captured_stream) + + yield + + stream_io.rewind + captured_stream.read + ensure + captured_stream.close + captured_stream.unlink + stream_io.reopen(origin_stream) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/tagged_logging.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/tagged_logging.rb new file mode 100644 index 0000000..7d38268 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/tagged_logging.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module ActiveSupport + module Testing + # Logs a "PostsControllerTest: test name" heading before each test to + # make test.log easier to search and follow along with. + module TaggedLogging # :nodoc: + attr_writer :tagged_logger + + def before_setup + if tagged_logger && tagged_logger.info? + heading = "#{self.class}: #{name}" + divider = "-" * heading.size + tagged_logger.info divider + tagged_logger.info heading + tagged_logger.info divider + end + super + end + + private + def tagged_logger + @tagged_logger ||= (defined?(Rails.logger) && Rails.logger) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/time_helpers.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/time_helpers.rb new file mode 100644 index 0000000..a1155bf --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/testing/time_helpers.rb @@ -0,0 +1,246 @@ +# frozen_string_literal: true + +require "active_support/core_ext/module/redefine_method" +require "active_support/core_ext/time/calculations" +require "concurrent/map" + +module ActiveSupport + module Testing + # Manages stubs for TimeHelpers + class SimpleStubs # :nodoc: + Stub = Struct.new(:object, :method_name, :original_method) + + def initialize + @stubs = Concurrent::Map.new { |h, k| h[k] = {} } + end + + # Stubs object.method_name with the given block + # If the method is already stubbed, remove that stub + # so that removing this stub will restore the original implementation. + # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00 + # target = Time.zone.local(2004, 11, 24, 1, 4, 44) + # simple_stubs.stub_object(Time, :now) { at(target.to_i) } + # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00 + def stub_object(object, method_name, &block) + if stub = stubbing(object, method_name) + unstub_object(stub) + end + + new_name = "__simple_stub__#{method_name}" + + @stubs[object.object_id][method_name] = Stub.new(object, method_name, new_name) + + object.singleton_class.alias_method new_name, method_name + object.define_singleton_method(method_name, &block) + end + + # Remove all object-method stubs held by this instance + def unstub_all! + @stubs.each_value do |object_stubs| + object_stubs.each_value do |stub| + unstub_object(stub) + end + end + @stubs.clear + end + + # Returns the Stub for object#method_name + # (nil if it is not stubbed) + def stubbing(object, method_name) + @stubs[object.object_id][method_name] + end + + # Returns true if any stubs are set, false if there are none + def stubbed? + !@stubs.empty? + end + + private + # Restores the original object.method described by the Stub + def unstub_object(stub) + singleton_class = stub.object.singleton_class + singleton_class.silence_redefinition_of_method stub.method_name + singleton_class.alias_method stub.method_name, stub.original_method + singleton_class.undef_method stub.original_method + end + end + + # Contains helpers that help you test passage of time. + module TimeHelpers + def after_teardown + travel_back + super + end + + # Changes current time to the time in the future or in the past by a given time difference by + # stubbing +Time.now+, +Date.today+, and +DateTime.now+. The stubs are automatically removed + # at the end of the test. + # + # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00 + # travel 1.day + # Time.current # => Sun, 10 Nov 2013 15:34:49 EST -05:00 + # Date.current # => Sun, 10 Nov 2013 + # DateTime.current # => Sun, 10 Nov 2013 15:34:49 -0500 + # + # This method also accepts a block, which will return the current time back to its original + # state at the end of the block: + # + # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00 + # travel 1.day do + # User.create.created_at # => Sun, 10 Nov 2013 15:34:49 EST -05:00 + # end + # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00 + def travel(duration, &block) + travel_to Time.now + duration, &block + end + + # Changes current time to the given time by stubbing +Time.now+, + # +Date.today+, and +DateTime.now+ to return the time or date passed into this method. + # The stubs are automatically removed at the end of the test. + # + # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00 + # travel_to Time.zone.local(2004, 11, 24, 1, 4, 44) + # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00 + # Date.current # => Wed, 24 Nov 2004 + # DateTime.current # => Wed, 24 Nov 2004 01:04:44 -0500 + # + # Dates are taken as their timestamp at the beginning of the day in the + # application time zone. Time.current returns said timestamp, + # and Time.now its equivalent in the system time zone. Similarly, + # Date.current returns a date equal to the argument, and + # Date.today the date according to Time.now, which may + # be different. (Note that you rarely want to deal with Time.now, + # or Date.today, in order to honor the application time zone + # please always use Time.current and Date.current.) + # + # Note that the usec for the time passed will be set to 0 to prevent rounding + # errors with external services, like MySQL (which will round instead of floor, + # leading to off-by-one-second errors). + # + # This method also accepts a block, which will return the current time back to its original + # state at the end of the block: + # + # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00 + # travel_to Time.zone.local(2004, 11, 24, 1, 4, 44) do + # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00 + # end + # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00 + def travel_to(date_or_time) + if block_given? && in_block + travel_to_nested_block_call = <<~MSG + + Calling `travel_to` with a block, when we have previously already made a call to `travel_to`, can lead to confusing time stubbing. + + Instead of: + + travel_to 2.days.from_now do + # 2 days from today + travel_to 3.days.from_now do + # 5 days from today + end + end + + preferred way to achieve above is: + + travel 2.days do + # 2 days from today + end + + travel 5.days do + # 5 days from today + end + + MSG + raise travel_to_nested_block_call + end + + if date_or_time.is_a?(Date) && !date_or_time.is_a?(DateTime) + now = date_or_time.midnight.to_time + elsif date_or_time.is_a?(String) + now = Time.zone.parse(date_or_time) + else + now = date_or_time.to_time.change(usec: 0) + end + + stubbed_time = Time.now if simple_stubs.stubbing(Time, :now) + simple_stubs.stub_object(Time, :now) { at(now.to_i) } + simple_stubs.stub_object(Date, :today) { jd(now.to_date.jd) } + simple_stubs.stub_object(DateTime, :now) { jd(now.to_date.jd, now.hour, now.min, now.sec, Rational(now.utc_offset, 86400)) } + + if block_given? + begin + self.in_block = true + yield + ensure + if stubbed_time + travel_to stubbed_time + else + travel_back + end + self.in_block = false + end + end + end + + # Returns the current time back to its original state, by removing the stubs added by + # +travel+, +travel_to+, and +freeze_time+. + # + # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00 + # + # travel_to Time.zone.local(2004, 11, 24, 1, 4, 44) + # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00 + # + # travel_back + # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00 + # + # This method also accepts a block, which brings the stubs back at the end of the block: + # + # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00 + # + # travel_to Time.zone.local(2004, 11, 24, 1, 4, 44) + # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00 + # + # travel_back do + # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00 + # end + # + # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00 + def travel_back + stubbed_time = Time.current if block_given? && simple_stubs.stubbed? + + simple_stubs.unstub_all! + yield if block_given? + ensure + travel_to stubbed_time if stubbed_time + end + alias_method :unfreeze_time, :travel_back + + # Calls +travel_to+ with +Time.now+. + # + # Time.current # => Sun, 09 Jul 2017 15:34:49 EST -05:00 + # freeze_time + # sleep(1) + # Time.current # => Sun, 09 Jul 2017 15:34:49 EST -05:00 + # + # This method also accepts a block, which will return the current time back to its original + # state at the end of the block: + # + # Time.current # => Sun, 09 Jul 2017 15:34:49 EST -05:00 + # freeze_time do + # sleep(1) + # User.create.created_at # => Sun, 09 Jul 2017 15:34:49 EST -05:00 + # end + # Time.current # => Sun, 09 Jul 2017 15:34:50 EST -05:00 + def freeze_time(&block) + travel_to Time.now, &block + end + + private + def simple_stubs + @simple_stubs ||= SimpleStubs.new + end + + attr_accessor :in_block + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/time.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/time.rb new file mode 100644 index 0000000..5185467 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/time.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module ActiveSupport + autoload :Duration, "active_support/duration" + autoload :TimeWithZone, "active_support/time_with_zone" + autoload :TimeZone, "active_support/values/time_zone" +end + +require "date" +require "time" + +require "active_support/core_ext/time" +require "active_support/core_ext/date" +require "active_support/core_ext/date_time" + +require "active_support/core_ext/integer/time" +require "active_support/core_ext/numeric/time" + +require "active_support/core_ext/string/conversions" +require "active_support/core_ext/string/zones" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/time_with_zone.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/time_with_zone.rb new file mode 100644 index 0000000..dfa4171 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/time_with_zone.rb @@ -0,0 +1,625 @@ +# frozen_string_literal: true + +require "yaml" + +require "active_support/duration" +require "active_support/values/time_zone" +require "active_support/core_ext/object/acts_like" +require "active_support/core_ext/date_and_time/compatibility" + +module ActiveSupport + # A Time-like class that can represent a time in any time zone. Necessary + # because standard Ruby Time instances are limited to UTC and the + # system's ENV['TZ'] zone. + # + # You shouldn't ever need to create a TimeWithZone instance directly via +new+. + # Instead use methods +local+, +parse+, +at+, and +now+ on TimeZone instances, + # and +in_time_zone+ on Time and DateTime instances. + # + # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)' + # Time.zone.local(2007, 2, 10, 15, 30, 45) # => Sat, 10 Feb 2007 15:30:45.000000000 EST -05:00 + # Time.zone.parse('2007-02-10 15:30:45') # => Sat, 10 Feb 2007 15:30:45.000000000 EST -05:00 + # Time.zone.at(1171139445) # => Sat, 10 Feb 2007 15:30:45.000000000 EST -05:00 + # Time.zone.now # => Sun, 18 May 2008 13:07:55.754107581 EDT -04:00 + # Time.utc(2007, 2, 10, 20, 30, 45).in_time_zone # => Sat, 10 Feb 2007 15:30:45.000000000 EST -05:00 + # + # See Time and TimeZone for further documentation of these methods. + # + # TimeWithZone instances implement the same API as Ruby Time instances, so + # that Time and TimeWithZone instances are interchangeable. + # + # t = Time.zone.now # => Sun, 18 May 2008 13:27:25.031505668 EDT -04:00 + # t.hour # => 13 + # t.dst? # => true + # t.utc_offset # => -14400 + # t.zone # => "EDT" + # t.to_fs(:rfc822) # => "Sun, 18 May 2008 13:27:25 -0400" + # t + 1.day # => Mon, 19 May 2008 13:27:25.031505668 EDT -04:00 + # t.beginning_of_year # => Tue, 01 Jan 2008 00:00:00.000000000 EST -05:00 + # t > Time.utc(1999) # => true + # t.is_a?(Time) # => true + # t.is_a?(ActiveSupport::TimeWithZone) # => true + class TimeWithZone + # Report class name as 'Time' to thwart type checking. + def self.name + ActiveSupport::Deprecation.warn(<<~EOM) + ActiveSupport::TimeWithZone.name has been deprecated and + from Rails 7.1 will use the default Ruby implementation. + You can set `config.active_support.remove_deprecated_time_with_zone_name = true` + to enable the new behavior now. + EOM + + "Time" + end + + PRECISIONS = Hash.new { |h, n| h[n] = "%FT%T.%#{n}N" } + PRECISIONS[0] = "%FT%T" + + include Comparable, DateAndTime::Compatibility + attr_reader :time_zone + + def initialize(utc_time, time_zone, local_time = nil, period = nil) + @utc = utc_time ? transfer_time_values_to_utc_constructor(utc_time) : nil + @time_zone, @time = time_zone, local_time + @period = @utc ? period : get_period_and_ensure_valid_local_time(period) + end + + # Returns a Time instance that represents the time in +time_zone+. + def time + @time ||= incorporate_utc_offset(@utc, utc_offset) + end + + # Returns a Time instance of the simultaneous time in the UTC timezone. + def utc + @utc ||= incorporate_utc_offset(@time, -utc_offset) + end + alias_method :comparable_time, :utc + alias_method :getgm, :utc + alias_method :getutc, :utc + alias_method :gmtime, :utc + + # Returns the underlying TZInfo::TimezonePeriod. + def period + @period ||= time_zone.period_for_utc(@utc) + end + + # Returns the simultaneous time in Time.zone, or the specified zone. + def in_time_zone(new_zone = ::Time.zone) + return self if time_zone == new_zone + utc.in_time_zone(new_zone) + end + + # Returns a Time instance of the simultaneous time in the system timezone. + def localtime(utc_offset = nil) + utc.getlocal(utc_offset) + end + alias_method :getlocal, :localtime + + # Returns true if the current time is within Daylight Savings Time for the + # specified time zone. + # + # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)' + # Time.zone.parse("2012-5-30").dst? # => true + # Time.zone.parse("2012-11-30").dst? # => false + def dst? + period.dst? + end + alias_method :isdst, :dst? + + # Returns true if the current time zone is set to UTC. + # + # Time.zone = 'UTC' # => 'UTC' + # Time.zone.now.utc? # => true + # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)' + # Time.zone.now.utc? # => false + def utc? + zone == "UTC" || zone == "UCT" + end + alias_method :gmt?, :utc? + + # Returns the offset from current time to UTC time in seconds. + def utc_offset + period.observed_utc_offset + end + alias_method :gmt_offset, :utc_offset + alias_method :gmtoff, :utc_offset + + # Returns a formatted string of the offset from UTC, or an alternative + # string if the time zone is already UTC. + # + # Time.zone = 'Eastern Time (US & Canada)' # => "Eastern Time (US & Canada)" + # Time.zone.now.formatted_offset(true) # => "-05:00" + # Time.zone.now.formatted_offset(false) # => "-0500" + # Time.zone = 'UTC' # => "UTC" + # Time.zone.now.formatted_offset(true, "0") # => "0" + def formatted_offset(colon = true, alternate_utc_string = nil) + utc? && alternate_utc_string || TimeZone.seconds_to_utc_offset(utc_offset, colon) + end + + # Returns the time zone abbreviation. + # + # Time.zone = 'Eastern Time (US & Canada)' # => "Eastern Time (US & Canada)" + # Time.zone.now.zone # => "EST" + def zone + period.abbreviation + end + + # Returns a string of the object's date, time, zone, and offset from UTC. + # + # Time.zone.now.inspect # => "Thu, 04 Dec 2014 11:00:25.624541392 EST -05:00" + def inspect + "#{time.strftime('%a, %d %b %Y %H:%M:%S.%9N')} #{zone} #{formatted_offset}" + end + + # Returns a string of the object's date and time in the ISO 8601 standard + # format. + # + # Time.zone.now.xmlschema # => "2014-12-04T11:02:37-05:00" + def xmlschema(fraction_digits = 0) + "#{time.strftime(PRECISIONS[fraction_digits.to_i])}#{formatted_offset(true, 'Z')}" + end + alias_method :iso8601, :xmlschema + alias_method :rfc3339, :xmlschema + + # Coerces time to a string for JSON encoding. The default format is ISO 8601. + # You can get %Y/%m/%d %H:%M:%S +offset style by setting + # ActiveSupport::JSON::Encoding.use_standard_json_time_format + # to +false+. + # + # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true + # Time.utc(2005,2,1,15,15,10).in_time_zone("Hawaii").to_json + # # => "2005-02-01T05:15:10.000-10:00" + # + # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false + # Time.utc(2005,2,1,15,15,10).in_time_zone("Hawaii").to_json + # # => "2005/02/01 05:15:10 -1000" + def as_json(options = nil) + if ActiveSupport::JSON::Encoding.use_standard_json_time_format + xmlschema(ActiveSupport::JSON::Encoding.time_precision) + else + %(#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}) + end + end + + def init_with(coder) # :nodoc: + initialize(coder["utc"], coder["zone"], coder["time"]) + end + + def encode_with(coder) # :nodoc: + coder.map = { "utc" => utc, "zone" => time_zone, "time" => time } + end + + # Returns a string of the object's date and time in the format used by + # HTTP requests. + # + # Time.zone.now.httpdate # => "Tue, 01 Jan 2013 04:39:43 GMT" + def httpdate + utc.httpdate + end + + # Returns a string of the object's date and time in the RFC 2822 standard + # format. + # + # Time.zone.now.rfc2822 # => "Tue, 01 Jan 2013 04:51:39 +0000" + def rfc2822 + to_fs(:rfc822) + end + alias_method :rfc822, :rfc2822 + + NOT_SET = Object.new # :nodoc: + + # Returns a string of the object's date and time. + def to_s(format = NOT_SET) + if format == :db + ActiveSupport::Deprecation.warn( + "TimeWithZone#to_s(:db) is deprecated. Please use TimeWithZone#to_fs(:db) instead." + ) + utc.to_fs(format) + elsif formatter = ::Time::DATE_FORMATS[format] + ActiveSupport::Deprecation.warn( + "TimeWithZone#to_s(#{format.inspect}) is deprecated. Please use TimeWithZone#to_fs(#{format.inspect}) instead." + ) + formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter) + elsif format == NOT_SET + "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby Time#to_s format + else + ActiveSupport::Deprecation.warn( + "TimeWithZone#to_s(#{format.inspect}) is deprecated. Please use TimeWithZone#to_fs(#{format.inspect}) instead." + ) + "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby Time#to_s format + end + end + + # Returns a string of the object's date and time. + # + # This method is aliased to to_formatted_s. + # + # Accepts an optional format: + # * :default - default value, mimics Ruby Time#to_s format. + # * :db - format outputs time in UTC :db time. See Time#to_fs(:db). + # * Any key in Time::DATE_FORMATS can be used. See active_support/core_ext/time/conversions.rb. + def to_fs(format = :default) + if format == :db + utc.to_fs(format) + elsif formatter = ::Time::DATE_FORMATS[format] + formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter) + else + # Change to to_s when deprecation is gone. + "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" + end + end + alias_method :to_formatted_s, :to_fs + + # Replaces %Z directive with +zone before passing to Time#strftime, + # so that zone information is correct. + def strftime(format) + format = format.gsub(/((?:\A|[^%])(?:%%)*)%Z/, "\\1#{zone}") + getlocal(utc_offset).strftime(format) + end + + # Use the time in UTC for comparisons. + def <=>(other) + utc <=> other + end + alias_method :before?, :< + alias_method :after?, :> + + # Returns true if the current object's time is within the specified + # +min+ and +max+ time. + def between?(min, max) + utc.between?(min, max) + end + + # Returns true if the current object's time is in the past. + def past? + utc.past? + end + + # Returns true if the current object's time falls within + # the current day. + def today? + time.today? + end + + # Returns true if the current object's time falls within + # the next day (tomorrow). + def tomorrow? + time.tomorrow? + end + alias :next_day? :tomorrow? + + # Returns true if the current object's time falls within + # the previous day (yesterday). + def yesterday? + time.yesterday? + end + alias :prev_day? :yesterday? + + # Returns true if the current object's time is in the future. + def future? + utc.future? + end + + # Returns +true+ if +other+ is equal to current object. + def eql?(other) + other.eql?(utc) + end + + def hash + utc.hash + end + + # Adds an interval of time to the current object's time and returns that + # value as a new TimeWithZone object. + # + # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)' + # now = Time.zone.now # => Sun, 02 Nov 2014 01:26:28.725182881 EDT -04:00 + # now + 1000 # => Sun, 02 Nov 2014 01:43:08.725182881 EDT -04:00 + # + # If we're adding a Duration of variable length (i.e., years, months, days), + # move forward from #time, otherwise move forward from #utc, for accuracy + # when moving across DST boundaries. + # + # For instance, a time + 24.hours will advance exactly 24 hours, while a + # time + 1.day will advance 23-25 hours, depending on the day. + # + # now + 24.hours # => Mon, 03 Nov 2014 00:26:28.725182881 EST -05:00 + # now + 1.day # => Mon, 03 Nov 2014 01:26:28.725182881 EST -05:00 + def +(other) + if duration_of_variable_length?(other) + method_missing(:+, other) + else + result = utc.acts_like?(:date) ? utc.since(other) : utc + other rescue utc.since(other) + result.in_time_zone(time_zone) + end + end + alias_method :since, :+ + alias_method :in, :+ + + # Subtracts an interval of time and returns a new TimeWithZone object unless + # the other value +acts_like?+ time. In which case, it will subtract the + # other time and return the difference in seconds as a Float. + # + # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)' + # now = Time.zone.now # => Mon, 03 Nov 2014 00:26:28.725182881 EST -05:00 + # now - 1000 # => Mon, 03 Nov 2014 00:09:48.725182881 EST -05:00 + # + # If subtracting a Duration of variable length (i.e., years, months, days), + # move backward from #time, otherwise move backward from #utc, for accuracy + # when moving across DST boundaries. + # + # For instance, a time - 24.hours will go subtract exactly 24 hours, while a + # time - 1.day will subtract 23-25 hours, depending on the day. + # + # now - 24.hours # => Sun, 02 Nov 2014 01:26:28.725182881 EDT -04:00 + # now - 1.day # => Sun, 02 Nov 2014 00:26:28.725182881 EDT -04:00 + # + # If both the TimeWithZone object and the other value act like Time, a Float + # will be returned. + # + # Time.zone.now - 1.day.ago # => 86399.999967 + # + def -(other) + if other.acts_like?(:time) + to_time - other.to_time + elsif duration_of_variable_length?(other) + method_missing(:-, other) + else + result = utc.acts_like?(:date) ? utc.ago(other) : utc - other rescue utc.ago(other) + result.in_time_zone(time_zone) + end + end + + # Subtracts an interval of time from the current object's time and returns + # the result as a new TimeWithZone object. + # + # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)' + # now = Time.zone.now # => Mon, 03 Nov 2014 00:26:28.725182881 EST -05:00 + # now.ago(1000) # => Mon, 03 Nov 2014 00:09:48.725182881 EST -05:00 + # + # If we're subtracting a Duration of variable length (i.e., years, months, + # days), move backward from #time, otherwise move backward from #utc, for + # accuracy when moving across DST boundaries. + # + # For instance, time.ago(24.hours) will move back exactly 24 hours, + # while time.ago(1.day) will move back 23-25 hours, depending on + # the day. + # + # now.ago(24.hours) # => Sun, 02 Nov 2014 01:26:28.725182881 EDT -04:00 + # now.ago(1.day) # => Sun, 02 Nov 2014 00:26:28.725182881 EDT -04:00 + def ago(other) + since(-other) + end + + # Returns a new +ActiveSupport::TimeWithZone+ where one or more of the elements have + # been changed according to the +options+ parameter. The time options (:hour, + # :min, :sec, :usec, :nsec) reset cascadingly, + # so if only the hour is passed, then minute, sec, usec, and nsec is set to 0. If the + # hour and minute is passed, then sec, usec, and nsec is set to 0. The +options+ + # parameter takes a hash with any of these keys: :year, :month, + # :day, :hour, :min, :sec, :usec, + # :nsec, :offset, :zone. Pass either :usec + # or :nsec, not both. Similarly, pass either :zone or + # :offset, not both. + # + # t = Time.zone.now # => Fri, 14 Apr 2017 11:45:15.116992711 EST -05:00 + # t.change(year: 2020) # => Tue, 14 Apr 2020 11:45:15.116992711 EST -05:00 + # t.change(hour: 12) # => Fri, 14 Apr 2017 12:00:00.116992711 EST -05:00 + # t.change(min: 30) # => Fri, 14 Apr 2017 11:30:00.116992711 EST -05:00 + # t.change(offset: "-10:00") # => Fri, 14 Apr 2017 11:45:15.116992711 HST -10:00 + # t.change(zone: "Hawaii") # => Fri, 14 Apr 2017 11:45:15.116992711 HST -10:00 + def change(options) + if options[:zone] && options[:offset] + raise ArgumentError, "Can't change both :offset and :zone at the same time: #{options.inspect}" + end + + new_time = time.change(options) + + if options[:zone] + new_zone = ::Time.find_zone(options[:zone]) + elsif options[:offset] + new_zone = ::Time.find_zone(new_time.utc_offset) + end + + new_zone ||= time_zone + periods = new_zone.periods_for_local(new_time) + + self.class.new(nil, new_zone, new_time, periods.include?(period) ? period : nil) + end + + # Uses Date to provide precise Time calculations for years, months, and days + # according to the proleptic Gregorian calendar. The result is returned as a + # new TimeWithZone object. + # + # The +options+ parameter takes a hash with any of these keys: + # :years, :months, :weeks, :days, + # :hours, :minutes, :seconds. + # + # If advancing by a value of variable length (i.e., years, weeks, months, + # days), move forward from #time, otherwise move forward from #utc, for + # accuracy when moving across DST boundaries. + # + # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)' + # now = Time.zone.now # => Sun, 02 Nov 2014 01:26:28.558049687 EDT -04:00 + # now.advance(seconds: 1) # => Sun, 02 Nov 2014 01:26:29.558049687 EDT -04:00 + # now.advance(minutes: 1) # => Sun, 02 Nov 2014 01:27:28.558049687 EDT -04:00 + # now.advance(hours: 1) # => Sun, 02 Nov 2014 01:26:28.558049687 EST -05:00 + # now.advance(days: 1) # => Mon, 03 Nov 2014 01:26:28.558049687 EST -05:00 + # now.advance(weeks: 1) # => Sun, 09 Nov 2014 01:26:28.558049687 EST -05:00 + # now.advance(months: 1) # => Tue, 02 Dec 2014 01:26:28.558049687 EST -05:00 + # now.advance(years: 1) # => Mon, 02 Nov 2015 01:26:28.558049687 EST -05:00 + def advance(options) + # If we're advancing a value of variable length (i.e., years, weeks, months, days), advance from #time, + # otherwise advance from #utc, for accuracy when moving across DST boundaries + if options.values_at(:years, :weeks, :months, :days).any? + method_missing(:advance, options) + else + utc.advance(options).in_time_zone(time_zone) + end + end + + %w(year mon month day mday wday yday hour min sec usec nsec to_date).each do |method_name| + class_eval <<-EOV, __FILE__, __LINE__ + 1 + def #{method_name} # def month + time.#{method_name} # time.month + end # end + EOV + end + + # Returns Array of parts of Time in sequence of + # [seconds, minutes, hours, day, month, year, weekday, yearday, dst?, zone]. + # + # now = Time.zone.now # => Tue, 18 Aug 2015 02:29:27.485278555 UTC +00:00 + # now.to_a # => [27, 29, 2, 18, 8, 2015, 2, 230, false, "UTC"] + def to_a + [time.sec, time.min, time.hour, time.day, time.mon, time.year, time.wday, time.yday, dst?, zone] + end + + # Returns the object's date and time as a floating-point number of seconds + # since the Epoch (January 1, 1970 00:00 UTC). + # + # Time.zone.now.to_f # => 1417709320.285418 + def to_f + utc.to_f + end + + # Returns the object's date and time as an integer number of seconds + # since the Epoch (January 1, 1970 00:00 UTC). + # + # Time.zone.now.to_i # => 1417709320 + def to_i + utc.to_i + end + alias_method :tv_sec, :to_i + + # Returns the object's date and time as a rational number of seconds + # since the Epoch (January 1, 1970 00:00 UTC). + # + # Time.zone.now.to_r # => (708854548642709/500000) + def to_r + utc.to_r + end + + # Returns an instance of DateTime with the timezone's UTC offset + # + # Time.zone.now.to_datetime # => Tue, 18 Aug 2015 02:32:20 +0000 + # Time.current.in_time_zone('Hawaii').to_datetime # => Mon, 17 Aug 2015 16:32:20 -1000 + def to_datetime + @to_datetime ||= utc.to_datetime.new_offset(Rational(utc_offset, 86_400)) + end + + # Returns an instance of +Time+, either with the same UTC offset + # as +self+ or in the local system timezone depending on the setting + # of +ActiveSupport.to_time_preserves_timezone+. + def to_time + if preserve_timezone + @to_time_with_instance_offset ||= getlocal(utc_offset) + else + @to_time_with_system_offset ||= getlocal + end + end + + # So that +self+ acts_like?(:time). + def acts_like_time? + true + end + + # Say we're a Time to thwart type checking. + def is_a?(klass) + klass == ::Time || super + end + alias_method :kind_of?, :is_a? + + # An instance of ActiveSupport::TimeWithZone is never blank + def blank? + false + end + + def freeze + # preload instance variables before freezing + period; utc; time; to_datetime; to_time + super + end + + def marshal_dump + [utc, time_zone.name, time] + end + + def marshal_load(variables) + initialize(variables[0].utc, ::Time.find_zone(variables[1]), variables[2].utc) + end + + # respond_to_missing? is not called in some cases, such as when type conversion is + # performed with Kernel#String + def respond_to?(sym, include_priv = false) + # ensure that we're not going to throw and rescue from NoMethodError in method_missing which is slow + return false if sym.to_sym == :to_str + super + end + + # Ensure proxy class responds to all methods that underlying time instance + # responds to. + def respond_to_missing?(sym, include_priv) + return false if sym.to_sym == :acts_like_date? + time.respond_to?(sym, include_priv) + end + + # Send the missing method to +time+ instance, and wrap result in a new + # TimeWithZone with the existing +time_zone+. + def method_missing(sym, *args, &block) + wrap_with_time_zone time.__send__(sym, *args, &block) + rescue NoMethodError => e + raise e, e.message.sub(time.inspect, inspect).sub("Time", "ActiveSupport::TimeWithZone"), e.backtrace + end + + private + SECONDS_PER_DAY = 86400 + + def incorporate_utc_offset(time, offset) + if time.kind_of?(Date) + time + Rational(offset, SECONDS_PER_DAY) + else + time + offset + end + end + + def get_period_and_ensure_valid_local_time(period) + # we don't want a Time.local instance enforcing its own DST rules as well, + # so transfer time values to a utc constructor if necessary + @time = transfer_time_values_to_utc_constructor(@time) unless @time.utc? + begin + period || @time_zone.period_for_local(@time) + rescue ::TZInfo::PeriodNotFound + # time is in the "spring forward" hour gap, so we're moving the time forward one hour and trying again + @time += 1.hour + retry + end + end + + def transfer_time_values_to_utc_constructor(time) + # avoid creating another Time object if possible + return time if time.instance_of?(::Time) && time.utc? + ::Time.utc(time.year, time.month, time.day, time.hour, time.min, time.sec + time.subsec) + end + + def duration_of_variable_length?(obj) + ActiveSupport::Duration === obj && obj.variable? + end + + def wrap_with_time_zone(time) + if time.acts_like?(:time) + periods = time_zone.periods_for_local(time) + self.class.new(nil, time_zone, time, periods.include?(period) ? period : nil) + elsif time.is_a?(Range) + wrap_with_time_zone(time.begin)..wrap_with_time_zone(time.end) + else + time + end + end + end +end + +# These prevent Psych from calling `ActiveSupport::TimeWithZone.name` +# and triggering the deprecation warning about the change in Rails 7.1. +YAML.load_tags["!ruby/object:ActiveSupport::TimeWithZone"] = "ActiveSupport::TimeWithZone" +YAML.dump_tags[ActiveSupport::TimeWithZone] = "!ruby/object:ActiveSupport::TimeWithZone" diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/values/time_zone.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/values/time_zone.rb new file mode 100644 index 0000000..e059142 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/values/time_zone.rb @@ -0,0 +1,601 @@ +# frozen_string_literal: true + +require "tzinfo" +require "concurrent/map" + +module ActiveSupport + # The TimeZone class serves as a wrapper around TZInfo::Timezone instances. + # It allows us to do the following: + # + # * Limit the set of zones provided by TZInfo to a meaningful subset of 134 + # zones. + # * Retrieve and display zones with a friendlier name + # (e.g., "Eastern Time (US & Canada)" instead of "America/New_York"). + # * Lazily load TZInfo::Timezone instances only when they're needed. + # * Create ActiveSupport::TimeWithZone instances via TimeZone's +local+, + # +parse+, +at+, and +now+ methods. + # + # If you set config.time_zone in the Rails Application, you can + # access this TimeZone object via Time.zone: + # + # # application.rb: + # class Application < Rails::Application + # config.time_zone = 'Eastern Time (US & Canada)' + # end + # + # Time.zone # => # + # Time.zone.name # => "Eastern Time (US & Canada)" + # Time.zone.now # => Sun, 18 May 2008 14:30:44 EDT -04:00 + class TimeZone + # Keys are Rails TimeZone names, values are TZInfo identifiers. + MAPPING = { + "International Date Line West" => "Etc/GMT+12", + "Midway Island" => "Pacific/Midway", + "American Samoa" => "Pacific/Pago_Pago", + "Hawaii" => "Pacific/Honolulu", + "Alaska" => "America/Juneau", + "Pacific Time (US & Canada)" => "America/Los_Angeles", + "Tijuana" => "America/Tijuana", + "Mountain Time (US & Canada)" => "America/Denver", + "Arizona" => "America/Phoenix", + "Chihuahua" => "America/Chihuahua", + "Mazatlan" => "America/Mazatlan", + "Central Time (US & Canada)" => "America/Chicago", + "Saskatchewan" => "America/Regina", + "Guadalajara" => "America/Mexico_City", + "Mexico City" => "America/Mexico_City", + "Monterrey" => "America/Monterrey", + "Central America" => "America/Guatemala", + "Eastern Time (US & Canada)" => "America/New_York", + "Indiana (East)" => "America/Indiana/Indianapolis", + "Bogota" => "America/Bogota", + "Lima" => "America/Lima", + "Quito" => "America/Lima", + "Atlantic Time (Canada)" => "America/Halifax", + "Caracas" => "America/Caracas", + "La Paz" => "America/La_Paz", + "Santiago" => "America/Santiago", + "Newfoundland" => "America/St_Johns", + "Brasilia" => "America/Sao_Paulo", + "Buenos Aires" => "America/Argentina/Buenos_Aires", + "Montevideo" => "America/Montevideo", + "Georgetown" => "America/Guyana", + "Puerto Rico" => "America/Puerto_Rico", + "Greenland" => "America/Godthab", + "Mid-Atlantic" => "Atlantic/South_Georgia", + "Azores" => "Atlantic/Azores", + "Cape Verde Is." => "Atlantic/Cape_Verde", + "Dublin" => "Europe/Dublin", + "Edinburgh" => "Europe/London", + "Lisbon" => "Europe/Lisbon", + "London" => "Europe/London", + "Casablanca" => "Africa/Casablanca", + "Monrovia" => "Africa/Monrovia", + "UTC" => "Etc/UTC", + "Belgrade" => "Europe/Belgrade", + "Bratislava" => "Europe/Bratislava", + "Budapest" => "Europe/Budapest", + "Ljubljana" => "Europe/Ljubljana", + "Prague" => "Europe/Prague", + "Sarajevo" => "Europe/Sarajevo", + "Skopje" => "Europe/Skopje", + "Warsaw" => "Europe/Warsaw", + "Zagreb" => "Europe/Zagreb", + "Brussels" => "Europe/Brussels", + "Copenhagen" => "Europe/Copenhagen", + "Madrid" => "Europe/Madrid", + "Paris" => "Europe/Paris", + "Amsterdam" => "Europe/Amsterdam", + "Berlin" => "Europe/Berlin", + "Bern" => "Europe/Zurich", + "Zurich" => "Europe/Zurich", + "Rome" => "Europe/Rome", + "Stockholm" => "Europe/Stockholm", + "Vienna" => "Europe/Vienna", + "West Central Africa" => "Africa/Algiers", + "Bucharest" => "Europe/Bucharest", + "Cairo" => "Africa/Cairo", + "Helsinki" => "Europe/Helsinki", + "Kyiv" => "Europe/Kiev", + "Riga" => "Europe/Riga", + "Sofia" => "Europe/Sofia", + "Tallinn" => "Europe/Tallinn", + "Vilnius" => "Europe/Vilnius", + "Athens" => "Europe/Athens", + "Istanbul" => "Europe/Istanbul", + "Minsk" => "Europe/Minsk", + "Jerusalem" => "Asia/Jerusalem", + "Harare" => "Africa/Harare", + "Pretoria" => "Africa/Johannesburg", + "Kaliningrad" => "Europe/Kaliningrad", + "Moscow" => "Europe/Moscow", + "St. Petersburg" => "Europe/Moscow", + "Volgograd" => "Europe/Volgograd", + "Samara" => "Europe/Samara", + "Kuwait" => "Asia/Kuwait", + "Riyadh" => "Asia/Riyadh", + "Nairobi" => "Africa/Nairobi", + "Baghdad" => "Asia/Baghdad", + "Tehran" => "Asia/Tehran", + "Abu Dhabi" => "Asia/Muscat", + "Muscat" => "Asia/Muscat", + "Baku" => "Asia/Baku", + "Tbilisi" => "Asia/Tbilisi", + "Yerevan" => "Asia/Yerevan", + "Kabul" => "Asia/Kabul", + "Ekaterinburg" => "Asia/Yekaterinburg", + "Islamabad" => "Asia/Karachi", + "Karachi" => "Asia/Karachi", + "Tashkent" => "Asia/Tashkent", + "Chennai" => "Asia/Kolkata", + "Kolkata" => "Asia/Kolkata", + "Mumbai" => "Asia/Kolkata", + "New Delhi" => "Asia/Kolkata", + "Kathmandu" => "Asia/Kathmandu", + "Astana" => "Asia/Dhaka", + "Dhaka" => "Asia/Dhaka", + "Sri Jayawardenepura" => "Asia/Colombo", + "Almaty" => "Asia/Almaty", + "Novosibirsk" => "Asia/Novosibirsk", + "Rangoon" => "Asia/Rangoon", + "Bangkok" => "Asia/Bangkok", + "Hanoi" => "Asia/Bangkok", + "Jakarta" => "Asia/Jakarta", + "Krasnoyarsk" => "Asia/Krasnoyarsk", + "Beijing" => "Asia/Shanghai", + "Chongqing" => "Asia/Chongqing", + "Hong Kong" => "Asia/Hong_Kong", + "Urumqi" => "Asia/Urumqi", + "Kuala Lumpur" => "Asia/Kuala_Lumpur", + "Singapore" => "Asia/Singapore", + "Taipei" => "Asia/Taipei", + "Perth" => "Australia/Perth", + "Irkutsk" => "Asia/Irkutsk", + "Ulaanbaatar" => "Asia/Ulaanbaatar", + "Seoul" => "Asia/Seoul", + "Osaka" => "Asia/Tokyo", + "Sapporo" => "Asia/Tokyo", + "Tokyo" => "Asia/Tokyo", + "Yakutsk" => "Asia/Yakutsk", + "Darwin" => "Australia/Darwin", + "Adelaide" => "Australia/Adelaide", + "Canberra" => "Australia/Melbourne", + "Melbourne" => "Australia/Melbourne", + "Sydney" => "Australia/Sydney", + "Brisbane" => "Australia/Brisbane", + "Hobart" => "Australia/Hobart", + "Vladivostok" => "Asia/Vladivostok", + "Guam" => "Pacific/Guam", + "Port Moresby" => "Pacific/Port_Moresby", + "Magadan" => "Asia/Magadan", + "Srednekolymsk" => "Asia/Srednekolymsk", + "Solomon Is." => "Pacific/Guadalcanal", + "New Caledonia" => "Pacific/Noumea", + "Fiji" => "Pacific/Fiji", + "Kamchatka" => "Asia/Kamchatka", + "Marshall Is." => "Pacific/Majuro", + "Auckland" => "Pacific/Auckland", + "Wellington" => "Pacific/Auckland", + "Nuku'alofa" => "Pacific/Tongatapu", + "Tokelau Is." => "Pacific/Fakaofo", + "Chatham Is." => "Pacific/Chatham", + "Samoa" => "Pacific/Apia" + } + + UTC_OFFSET_WITH_COLON = "%s%02d:%02d" # :nodoc: + UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.tr(":", "") # :nodoc: + private_constant :UTC_OFFSET_WITH_COLON, :UTC_OFFSET_WITHOUT_COLON + + @lazy_zones_map = Concurrent::Map.new + @country_zones = Concurrent::Map.new + + class << self + # Assumes self represents an offset from UTC in seconds (as returned from + # Time#utc_offset) and turns this into an +HH:MM formatted string. + # + # ActiveSupport::TimeZone.seconds_to_utc_offset(-21_600) # => "-06:00" + def seconds_to_utc_offset(seconds, colon = true) + format = colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON + sign = (seconds < 0 ? "-" : "+") + hours = seconds.abs / 3600 + minutes = (seconds.abs % 3600) / 60 + format % [sign, hours, minutes] + end + + def find_tzinfo(name) + TZInfo::Timezone.get(MAPPING[name] || name) + end + + alias_method :create, :new + + # Returns a TimeZone instance with the given name, or +nil+ if no + # such TimeZone instance exists. (This exists to support the use of + # this class with the +composed_of+ macro.) + def new(name) + self[name] + end + + # Returns an array of all TimeZone objects. There are multiple + # TimeZone objects per time zone, in many cases, to make it easier + # for users to find their own time zone. + def all + @zones ||= zones_map.values.sort + end + + # Locate a specific time zone object. If the argument is a string, it + # is interpreted to mean the name of the timezone to locate. If it is a + # numeric value it is either the hour offset, or the second offset, of the + # timezone to find. (The first one with that offset will be returned.) + # Returns +nil+ if no such time zone is known to the system. + def [](arg) + case arg + when self + arg + when String + begin + @lazy_zones_map[arg] ||= create(arg) + rescue TZInfo::InvalidTimezoneIdentifier + nil + end + when TZInfo::Timezone + @lazy_zones_map[arg.name] ||= create(arg.name, nil, arg) + when Numeric, ActiveSupport::Duration + arg *= 3600 if arg.abs <= 13 + all.find { |z| z.utc_offset == arg.to_i } + else + raise ArgumentError, "invalid argument to TimeZone[]: #{arg.inspect}" + end + end + + # A convenience method for returning a collection of TimeZone objects + # for time zones in the USA. + def us_zones + country_zones(:us) + end + + # A convenience method for returning a collection of TimeZone objects + # for time zones in the country specified by its ISO 3166-1 Alpha2 code. + def country_zones(country_code) + code = country_code.to_s.upcase + @country_zones[code] ||= load_country_zones(code) + end + + def clear # :nodoc: + @lazy_zones_map = Concurrent::Map.new + @country_zones = Concurrent::Map.new + @zones = nil + @zones_map = nil + end + + private + def load_country_zones(code) + country = TZInfo::Country.get(code) + country.zone_identifiers.flat_map do |tz_id| + if MAPPING.value?(tz_id) + MAPPING.inject([]) do |memo, (key, value)| + memo << self[key] if value == tz_id + memo + end + else + create(tz_id, nil, TZInfo::Timezone.get(tz_id)) + end + end.sort! + end + + def zones_map + @zones_map ||= MAPPING.each_with_object({}) do |(name, _), zones| + timezone = self[name] + zones[name] = timezone if timezone + end + end + end + + include Comparable + attr_reader :name + attr_reader :tzinfo + + # Create a new TimeZone object with the given name and offset. The + # offset is the number of seconds that this time zone is offset from UTC + # (GMT). Seconds were chosen as the offset unit because that is the unit + # that Ruby uses to represent time zone offsets (see Time#utc_offset). + def initialize(name, utc_offset = nil, tzinfo = nil) + @name = name + @utc_offset = utc_offset + @tzinfo = tzinfo || TimeZone.find_tzinfo(name) + end + + # Returns the offset of this time zone from UTC in seconds. + def utc_offset + @utc_offset || tzinfo&.current_period&.base_utc_offset + end + + # Returns a formatted string of the offset from UTC, or an alternative + # string if the time zone is already UTC. + # + # zone = ActiveSupport::TimeZone['Central Time (US & Canada)'] + # zone.formatted_offset # => "-06:00" + # zone.formatted_offset(false) # => "-0600" + def formatted_offset(colon = true, alternate_utc_string = nil) + utc_offset == 0 && alternate_utc_string || self.class.seconds_to_utc_offset(utc_offset, colon) + end + + # Compare this time zone to the parameter. The two are compared first on + # their offsets, and then by name. + def <=>(zone) + return unless zone.respond_to? :utc_offset + result = (utc_offset <=> zone.utc_offset) + result = (name <=> zone.name) if result == 0 + result + end + + # Compare #name and TZInfo identifier to a supplied regexp, returning +true+ + # if a match is found. + def =~(re) + re === name || re === MAPPING[name] + end + + # Compare #name and TZInfo identifier to a supplied regexp, returning +true+ + # if a match is found. + def match?(re) + (re == name) || (re == MAPPING[name]) || + ((Regexp === re) && (re.match?(name) || re.match?(MAPPING[name]))) + end + + # Returns a textual representation of this time zone. + def to_s + "(GMT#{formatted_offset}) #{name}" + end + + # Method for creating new ActiveSupport::TimeWithZone instance in time zone + # of +self+ from given values. + # + # Time.zone = 'Hawaii' # => "Hawaii" + # Time.zone.local(2007, 2, 1, 15, 30, 45) # => Thu, 01 Feb 2007 15:30:45 HST -10:00 + def local(*args) + time = Time.utc(*args) + ActiveSupport::TimeWithZone.new(nil, self, time) + end + + # Method for creating new ActiveSupport::TimeWithZone instance in time zone + # of +self+ from number of seconds since the Unix epoch. + # + # Time.zone = 'Hawaii' # => "Hawaii" + # Time.utc(2000).to_f # => 946684800.0 + # Time.zone.at(946684800.0) # => Fri, 31 Dec 1999 14:00:00 HST -10:00 + # + # A second argument can be supplied to specify sub-second precision. + # + # Time.zone = 'Hawaii' # => "Hawaii" + # Time.at(946684800, 123456.789).nsec # => 123456789 + def at(*args) + Time.at(*args).utc.in_time_zone(self) + end + + # Method for creating new ActiveSupport::TimeWithZone instance in time zone + # of +self+ from an ISO 8601 string. + # + # Time.zone = 'Hawaii' # => "Hawaii" + # Time.zone.iso8601('1999-12-31T14:00:00') # => Fri, 31 Dec 1999 14:00:00 HST -10:00 + # + # If the time components are missing then they will be set to zero. + # + # Time.zone = 'Hawaii' # => "Hawaii" + # Time.zone.iso8601('1999-12-31') # => Fri, 31 Dec 1999 00:00:00 HST -10:00 + # + # If the string is invalid then an +ArgumentError+ will be raised unlike +parse+ + # which usually returns +nil+ when given an invalid date string. + def iso8601(str) + # Historically `Date._iso8601(nil)` returns `{}`, but in the `date` gem versions `3.2.1`, `3.1.2`, `3.0.2`, + # and `2.0.1`, `Date._iso8601(nil)` raises `TypeError` https://github.com/ruby/date/issues/39 + # Future `date` releases are expected to revert back to the original behavior. + raise ArgumentError, "invalid date" if str.nil? + + parts = Date._iso8601(str) + + year = parts.fetch(:year) + + if parts.key?(:yday) + ordinal_date = Date.ordinal(year, parts.fetch(:yday)) + month = ordinal_date.month + day = ordinal_date.day + else + month = parts.fetch(:mon) + day = parts.fetch(:mday) + end + + time = Time.new( + year, + month, + day, + parts.fetch(:hour, 0), + parts.fetch(:min, 0), + parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0), + parts.fetch(:offset, 0) + ) + + if parts[:offset] + TimeWithZone.new(time.utc, self) + else + TimeWithZone.new(nil, self, time) + end + + rescue Date::Error, KeyError + raise ArgumentError, "invalid date" + end + + # Method for creating new ActiveSupport::TimeWithZone instance in time zone + # of +self+ from parsed string. + # + # Time.zone = 'Hawaii' # => "Hawaii" + # Time.zone.parse('1999-12-31 14:00:00') # => Fri, 31 Dec 1999 14:00:00 HST -10:00 + # + # If upper components are missing from the string, they are supplied from + # TimeZone#now: + # + # Time.zone.now # => Fri, 31 Dec 1999 14:00:00 HST -10:00 + # Time.zone.parse('22:30:00') # => Fri, 31 Dec 1999 22:30:00 HST -10:00 + # + # However, if the date component is not provided, but any other upper + # components are supplied, then the day of the month defaults to 1: + # + # Time.zone.parse('Mar 2000') # => Wed, 01 Mar 2000 00:00:00 HST -10:00 + # + # If the string is invalid then an +ArgumentError+ could be raised. + def parse(str, now = now()) + parts_to_time(Date._parse(str, false), now) + end + + # Method for creating new ActiveSupport::TimeWithZone instance in time zone + # of +self+ from an RFC 3339 string. + # + # Time.zone = 'Hawaii' # => "Hawaii" + # Time.zone.rfc3339('2000-01-01T00:00:00Z') # => Fri, 31 Dec 1999 14:00:00 HST -10:00 + # + # If the time or zone components are missing then an +ArgumentError+ will + # be raised. This is much stricter than either +parse+ or +iso8601+ which + # allow for missing components. + # + # Time.zone = 'Hawaii' # => "Hawaii" + # Time.zone.rfc3339('1999-12-31') # => ArgumentError: invalid date + def rfc3339(str) + parts = Date._rfc3339(str) + + raise ArgumentError, "invalid date" if parts.empty? + + time = Time.new( + parts.fetch(:year), + parts.fetch(:mon), + parts.fetch(:mday), + parts.fetch(:hour), + parts.fetch(:min), + parts.fetch(:sec) + parts.fetch(:sec_fraction, 0), + parts.fetch(:offset) + ) + + TimeWithZone.new(time.utc, self) + end + + # Parses +str+ according to +format+ and returns an ActiveSupport::TimeWithZone. + # + # Assumes that +str+ is a time in the time zone +self+, + # unless +format+ includes an explicit time zone. + # (This is the same behavior as +parse+.) + # In either case, the returned TimeWithZone has the timezone of +self+. + # + # Time.zone = 'Hawaii' # => "Hawaii" + # Time.zone.strptime('1999-12-31 14:00:00', '%Y-%m-%d %H:%M:%S') # => Fri, 31 Dec 1999 14:00:00 HST -10:00 + # + # If upper components are missing from the string, they are supplied from + # TimeZone#now: + # + # Time.zone.now # => Fri, 31 Dec 1999 14:00:00 HST -10:00 + # Time.zone.strptime('22:30:00', '%H:%M:%S') # => Fri, 31 Dec 1999 22:30:00 HST -10:00 + # + # However, if the date component is not provided, but any other upper + # components are supplied, then the day of the month defaults to 1: + # + # Time.zone.strptime('Mar 2000', '%b %Y') # => Wed, 01 Mar 2000 00:00:00 HST -10:00 + def strptime(str, format, now = now()) + parts_to_time(DateTime._strptime(str, format), now) + end + + # Returns an ActiveSupport::TimeWithZone instance representing the current + # time in the time zone represented by +self+. + # + # Time.zone = 'Hawaii' # => "Hawaii" + # Time.zone.now # => Wed, 23 Jan 2008 20:24:27 HST -10:00 + def now + time_now.utc.in_time_zone(self) + end + + # Returns the current date in this time zone. + def today + tzinfo.now.to_date + end + + # Returns the next date in this time zone. + def tomorrow + today + 1 + end + + # Returns the previous date in this time zone. + def yesterday + today - 1 + end + + # Adjust the given time to the simultaneous time in the time zone + # represented by +self+. Returns a local time with the appropriate offset + # -- if you want an ActiveSupport::TimeWithZone instance, use + # Time#in_time_zone() instead. + # + # As of tzinfo 2, utc_to_local returns a Time with a non-zero utc_offset. + # See the +utc_to_local_returns_utc_offset_times+ config for more info. + def utc_to_local(time) + tzinfo.utc_to_local(time).yield_self do |t| + ActiveSupport.utc_to_local_returns_utc_offset_times ? + t : Time.utc(t.year, t.month, t.day, t.hour, t.min, t.sec, t.sec_fraction * 1_000_000) + end + end + + # Adjust the given time to the simultaneous time in UTC. Returns a + # Time.utc() instance. + def local_to_utc(time, dst = true) + tzinfo.local_to_utc(time, dst) + end + + # Available so that TimeZone instances respond like TZInfo::Timezone + # instances. + def period_for_utc(time) + tzinfo.period_for_utc(time) + end + + # Available so that TimeZone instances respond like TZInfo::Timezone + # instances. + def period_for_local(time, dst = true) + tzinfo.period_for_local(time, dst) { |periods| periods.last } + end + + def periods_for_local(time) # :nodoc: + tzinfo.periods_for_local(time) + end + + def init_with(coder) # :nodoc: + initialize(coder["name"]) + end + + def encode_with(coder) # :nodoc: + coder.tag = "!ruby/object:#{self.class}" + coder.map = { "name" => tzinfo.name } + end + + private + def parts_to_time(parts, now) + raise ArgumentError, "invalid date" if parts.nil? + return if parts.empty? + + if parts[:seconds] + time = Time.at(parts[:seconds]) + else + time = Time.new( + parts.fetch(:year, now.year), + parts.fetch(:mon, now.month), + parts.fetch(:mday, parts[:year] || parts[:mon] ? 1 : now.day), + parts.fetch(:hour, 0), + parts.fetch(:min, 0), + parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0), + parts.fetch(:offset, 0) + ) + end + + if parts[:offset] || parts[:seconds] + TimeWithZone.new(time.utc, self) + else + TimeWithZone.new(nil, self, time) + end + end + + def time_now + Time.now + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/version.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/version.rb new file mode 100644 index 0000000..c3a0728 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/version.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require_relative "gem_version" + +module ActiveSupport + # Returns the currently loaded version of Active Support as a Gem::Version. + def self.version + gem_version + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/xml_mini.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/xml_mini.rb new file mode 100644 index 0000000..dd6bee0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/xml_mini.rb @@ -0,0 +1,202 @@ +# frozen_string_literal: true + +require "time" +require "base64" +require "bigdecimal" +require "bigdecimal/util" +require "active_support/core_ext/module/delegation" +require "active_support/core_ext/string/inflections" +require "active_support/core_ext/date_time/calculations" + +module ActiveSupport + # = XmlMini + # + # To use the much faster libxml parser: + # gem 'libxml-ruby', '=0.9.7' + # XmlMini.backend = 'LibXML' + module XmlMini + extend self + + # This module decorates files deserialized using Hash.from_xml with + # the original_filename and content_type methods. + module FileLike # :nodoc: + attr_writer :original_filename, :content_type + + def original_filename + @original_filename || "untitled" + end + + def content_type + @content_type || "application/octet-stream" + end + end + + DEFAULT_ENCODINGS = { + "binary" => "base64" + } unless defined?(DEFAULT_ENCODINGS) + + unless defined?(TYPE_NAMES) + TYPE_NAMES = { + "Symbol" => "symbol", + "Integer" => "integer", + "BigDecimal" => "decimal", + "Float" => "float", + "TrueClass" => "boolean", + "FalseClass" => "boolean", + "Date" => "date", + "DateTime" => "dateTime", + "Time" => "dateTime", + "Array" => "array", + "Hash" => "hash" + } + end + TYPE_NAMES["ActiveSupport::TimeWithZone"] = TYPE_NAMES["Time"] + + FORMATTING = { + "symbol" => Proc.new { |symbol| symbol.to_s }, + "date" => Proc.new { |date| date.to_fs(:db) }, + "dateTime" => Proc.new { |time| time.xmlschema }, + "binary" => Proc.new { |binary| ::Base64.encode64(binary) }, + "yaml" => Proc.new { |yaml| yaml.to_yaml } + } unless defined?(FORMATTING) + + # TODO use regexp instead of Date.parse + unless defined?(PARSING) + PARSING = { + "symbol" => Proc.new { |symbol| symbol.to_s.to_sym }, + "date" => Proc.new { |date| ::Date.parse(date) }, + "datetime" => Proc.new { |time| Time.xmlschema(time).utc rescue ::DateTime.parse(time).utc }, + "integer" => Proc.new { |integer| integer.to_i }, + "float" => Proc.new { |float| float.to_f }, + "decimal" => Proc.new do |number| + if String === number + number.to_d + else + BigDecimal(number) + end + end, + "boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.to_s.strip) }, + "string" => Proc.new { |string| string.to_s }, + "yaml" => Proc.new { |yaml| YAML.load(yaml) rescue yaml }, + "base64Binary" => Proc.new { |bin| ::Base64.decode64(bin) }, + "binary" => Proc.new { |bin, entity| _parse_binary(bin, entity) }, + "file" => Proc.new { |file, entity| _parse_file(file, entity) } + } + + PARSING.update( + "double" => PARSING["float"], + "dateTime" => PARSING["datetime"] + ) + end + + attr_accessor :depth + self.depth = 100 + + delegate :parse, to: :backend + + def backend + current_thread_backend || @backend + end + + def backend=(name) + backend = name && cast_backend_name_to_module(name) + self.current_thread_backend = backend if current_thread_backend + @backend = backend + end + + def with_backend(name) + old_backend = current_thread_backend + self.current_thread_backend = name && cast_backend_name_to_module(name) + yield + ensure + self.current_thread_backend = old_backend + end + + def to_tag(key, value, options) + type_name = options.delete(:type) + merged_options = options.merge(root: key, skip_instruct: true) + + if value.is_a?(::Method) || value.is_a?(::Proc) + if value.arity == 1 + value.call(merged_options) + else + value.call(merged_options, key.to_s.singularize) + end + elsif value.respond_to?(:to_xml) + value.to_xml(merged_options) + else + type_name ||= TYPE_NAMES[value.class.name] + type_name ||= value.class.name if value && !value.respond_to?(:to_str) + type_name = type_name.to_s if type_name + type_name = "dateTime" if type_name == "datetime" + + key = rename_key(key.to_s, options) + + attributes = options[:skip_types] || type_name.nil? ? {} : { type: type_name } + attributes[:nil] = true if value.nil? + + encoding = options[:encoding] || DEFAULT_ENCODINGS[type_name] + attributes[:encoding] = encoding if encoding + + formatted_value = FORMATTING[type_name] && !value.nil? ? + FORMATTING[type_name].call(value) : value + + options[:builder].tag!(key, formatted_value, attributes) + end + end + + def rename_key(key, options = {}) + camelize = options[:camelize] + dasherize = !options.has_key?(:dasherize) || options[:dasherize] + if camelize + key = true == camelize ? key.camelize : key.camelize(camelize) + end + key = _dasherize(key) if dasherize + key + end + + private + def _dasherize(key) + # $2 must be a non-greedy regex for this to work + left, middle, right = /\A(_*)(.*?)(_*)\Z/.match(key.strip)[1, 3] + "#{left}#{middle.tr('_ ', '--')}#{right}" + end + + # TODO: Add support for other encodings + def _parse_binary(bin, entity) + case entity["encoding"] + when "base64" + ::Base64.decode64(bin) + else + bin + end + end + + def _parse_file(file, entity) + f = StringIO.new(::Base64.decode64(file)) + f.extend(FileLike) + f.original_filename = entity["name"] + f.content_type = entity["content_type"] + f + end + + def current_thread_backend + IsolatedExecutionState[:xml_mini_backend] + end + + def current_thread_backend=(name) + IsolatedExecutionState[:xml_mini_backend] = name && cast_backend_name_to_module(name) + end + + def cast_backend_name_to_module(name) + if name.is_a?(Module) + name + else + require "active_support/xml_mini/#{name.downcase}" + ActiveSupport.const_get("XmlMini_#{name}") + end + end + end + + XmlMini.backend = "REXML" +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/xml_mini/jdom.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/xml_mini/jdom.rb new file mode 100644 index 0000000..b5aa909 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/xml_mini/jdom.rb @@ -0,0 +1,182 @@ +# frozen_string_literal: true + +raise "JRuby is required to use the JDOM backend for XmlMini" unless RUBY_PLATFORM.include?("java") + +require "jruby" +include Java + +require "active_support/core_ext/object/blank" + +java_import javax.xml.parsers.DocumentBuilder unless defined? DocumentBuilder +java_import javax.xml.parsers.DocumentBuilderFactory unless defined? DocumentBuilderFactory +java_import java.io.StringReader unless defined? StringReader +java_import org.xml.sax.InputSource unless defined? InputSource +java_import org.xml.sax.Attributes unless defined? Attributes +java_import org.w3c.dom.Node unless defined? Node + +module ActiveSupport + module XmlMini_JDOM # :nodoc: + extend self + + CONTENT_KEY = "__content__" + + NODE_TYPE_NAMES = %w{ATTRIBUTE_NODE CDATA_SECTION_NODE COMMENT_NODE DOCUMENT_FRAGMENT_NODE + DOCUMENT_NODE DOCUMENT_TYPE_NODE ELEMENT_NODE ENTITY_NODE ENTITY_REFERENCE_NODE NOTATION_NODE + PROCESSING_INSTRUCTION_NODE TEXT_NODE} + + node_type_map = {} + NODE_TYPE_NAMES.each { |type| node_type_map[Node.send(type)] = type } + + # Parse an XML Document string or IO into a simple hash using Java's jdom. + # data:: + # XML Document string or IO to parse + def parse(data) + if data.respond_to?(:read) + data = data.read + end + + if data.blank? + {} + else + @dbf = DocumentBuilderFactory.new_instance + # secure processing of java xml + # https://archive.is/9xcQQ + @dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) + @dbf.setFeature("http://xml.org/sax/features/external-general-entities", false) + @dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false) + @dbf.setFeature(javax.xml.XMLConstants::FEATURE_SECURE_PROCESSING, true) + xml_string_reader = StringReader.new(data) + xml_input_source = InputSource.new(xml_string_reader) + doc = @dbf.new_document_builder.parse(xml_input_source) + merge_element!({ CONTENT_KEY => "" }, doc.document_element, XmlMini.depth) + end + end + + private + # Convert an XML element and merge into the hash + # + # hash:: + # Hash to merge the converted element into. + # element:: + # XML element to merge into hash + def merge_element!(hash, element, depth) + raise "Document too deep!" if depth == 0 + delete_empty(hash) + merge!(hash, element.tag_name, collapse(element, depth)) + end + + def delete_empty(hash) + hash.delete(CONTENT_KEY) if hash[CONTENT_KEY] == "" + end + + # Actually converts an XML document element into a data structure. + # + # element:: + # The document element to be collapsed. + def collapse(element, depth) + hash = get_attributes(element) + + child_nodes = element.child_nodes + if child_nodes.length > 0 + (0...child_nodes.length).each do |i| + child = child_nodes.item(i) + merge_element!(hash, child, depth - 1) unless child.node_type == Node.TEXT_NODE + end + merge_texts!(hash, element) unless empty_content?(element) + hash + else + merge_texts!(hash, element) + end + end + + # Merge all the texts of an element into the hash + # + # hash:: + # Hash to add the converted element to. + # element:: + # XML element whose texts are to me merged into the hash + def merge_texts!(hash, element) + delete_empty(hash) + text_children = texts(element) + if text_children.join.empty? + hash + else + # must use value to prevent double-escaping + merge!(hash, CONTENT_KEY, text_children.join) + end + end + + # Adds a new key/value pair to an existing Hash. If the key to be added + # already exists and the existing value associated with key is not + # an Array, it will be wrapped in an Array. Then the new value is + # appended to that Array. + # + # hash:: + # Hash to add key/value pair to. + # key:: + # Key to be added. + # value:: + # Value to be associated with key. + def merge!(hash, key, value) + if hash.has_key?(key) + if hash[key].instance_of?(Array) + hash[key] << value + else + hash[key] = [hash[key], value] + end + elsif value.instance_of?(Array) + hash[key] = [value] + else + hash[key] = value + end + hash + end + + # Converts the attributes array of an XML element into a hash. + # Returns an empty Hash if node has no attributes. + # + # element:: + # XML element to extract attributes from. + def get_attributes(element) + attribute_hash = {} + attributes = element.attributes + (0...attributes.length).each do |i| + attribute_hash[CONTENT_KEY] ||= "" + attribute_hash[attributes.item(i).name] = attributes.item(i).value + end + attribute_hash + end + + # Determines if a document element has text content + # + # element:: + # XML element to be checked. + def texts(element) + texts = [] + child_nodes = element.child_nodes + (0...child_nodes.length).each do |i| + item = child_nodes.item(i) + if item.node_type == Node.TEXT_NODE + texts << item.get_data + end + end + texts + end + + # Determines if a document element has text content + # + # element:: + # XML element to be checked. + def empty_content?(element) + text = +"" + child_nodes = element.child_nodes + (0...child_nodes.length).each do |i| + item = child_nodes.item(i) + if item.node_type == Node.TEXT_NODE + text << item.get_data.strip + end + end + text.strip.length == 0 + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/xml_mini/libxml.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/xml_mini/libxml.rb new file mode 100644 index 0000000..65976a2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/xml_mini/libxml.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +require "libxml" +require "active_support/core_ext/object/blank" +require "stringio" + +module ActiveSupport + module XmlMini_LibXML # :nodoc: + extend self + + # Parse an XML Document string or IO into a simple hash using libxml. + # data:: + # XML Document string or IO to parse + def parse(data) + if !data.respond_to?(:read) + data = StringIO.new(data || "") + end + + if data.eof? + {} + else + LibXML::XML::Parser.io(data).parse.to_hash + end + end + end +end + +module LibXML # :nodoc: + module Conversions # :nodoc: + module Document # :nodoc: + def to_hash + root.to_hash + end + end + + module Node # :nodoc: + CONTENT_ROOT = "__content__" + + # Convert XML document to hash. + # + # hash:: + # Hash to merge the converted element into. + def to_hash(hash = {}) + node_hash = {} + + # Insert node hash into parent hash correctly. + case hash[name] + when Array then hash[name] << node_hash + when Hash then hash[name] = [hash[name], node_hash] + when nil then hash[name] = node_hash + end + + # Handle child elements + each_child do |c| + if c.element? + c.to_hash(node_hash) + elsif c.text? || c.cdata? + node_hash[CONTENT_ROOT] ||= +"" + node_hash[CONTENT_ROOT] << c.content + end + end + + # Remove content node if it is blank + if node_hash.length > 1 && node_hash[CONTENT_ROOT].blank? + node_hash.delete(CONTENT_ROOT) + end + + # Handle attributes + each_attr { |a| node_hash[a.name] = a.value } + + hash + end + end + end +end + +# :enddoc: + +LibXML::XML::Document.include(LibXML::Conversions::Document) +LibXML::XML::Node.include(LibXML::Conversions::Node) diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/xml_mini/libxmlsax.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/xml_mini/libxmlsax.rb new file mode 100644 index 0000000..33d594c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/xml_mini/libxmlsax.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +require "libxml" +require "active_support/core_ext/object/blank" +require "stringio" + +module ActiveSupport + module XmlMini_LibXMLSAX # :nodoc: + extend self + + # Class that will build the hash while the XML document + # is being parsed using SAX events. + class HashBuilder + include LibXML::XML::SaxParser::Callbacks + + CONTENT_KEY = "__content__" + HASH_SIZE_KEY = "__hash_size__" + + attr_reader :hash + + def current_hash + @hash_stack.last + end + + def on_start_document + @hash = { CONTENT_KEY => +"" } + @hash_stack = [@hash] + end + + def on_end_document + @hash = @hash_stack.pop + @hash.delete(CONTENT_KEY) + end + + def on_start_element(name, attrs = {}) + new_hash = { CONTENT_KEY => +"" }.merge!(attrs) + new_hash[HASH_SIZE_KEY] = new_hash.size + 1 + + case current_hash[name] + when Array then current_hash[name] << new_hash + when Hash then current_hash[name] = [current_hash[name], new_hash] + when nil then current_hash[name] = new_hash + end + + @hash_stack.push(new_hash) + end + + def on_end_element(name) + if current_hash.length > current_hash.delete(HASH_SIZE_KEY) && current_hash[CONTENT_KEY].blank? || current_hash[CONTENT_KEY] == "" + current_hash.delete(CONTENT_KEY) + end + @hash_stack.pop + end + + def on_characters(string) + current_hash[CONTENT_KEY] << string + end + + alias_method :on_cdata_block, :on_characters + end + + attr_accessor :document_class + self.document_class = HashBuilder + + def parse(data) + if !data.respond_to?(:read) + data = StringIO.new(data || "") + end + + if data.eof? + {} + else + LibXML::XML::Error.set_handler(&LibXML::XML::Error::QUIET_HANDLER) + parser = LibXML::XML::SaxParser.io(data) + document = document_class.new + + parser.callbacks = document + parser.parse + document.hash + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/xml_mini/nokogiri.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/xml_mini/nokogiri.rb new file mode 100644 index 0000000..3fb58bc --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/xml_mini/nokogiri.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +begin + require "nokogiri" +rescue LoadError => e + $stderr.puts "You don't have nokogiri installed in your application. Please add it to your Gemfile and run bundle install" + raise e +end +require "active_support/core_ext/object/blank" +require "stringio" + +module ActiveSupport + module XmlMini_Nokogiri # :nodoc: + extend self + + # Parse an XML Document string or IO into a simple hash using libxml / nokogiri. + # data:: + # XML Document string or IO to parse + def parse(data) + if !data.respond_to?(:read) + data = StringIO.new(data || "") + end + + if data.eof? + {} + else + doc = Nokogiri::XML(data) + raise doc.errors.first if doc.errors.length > 0 + doc.to_hash + end + end + + module Conversions # :nodoc: + module Document # :nodoc: + def to_hash + root.to_hash + end + end + + module Node # :nodoc: + CONTENT_ROOT = "__content__" + + # Convert XML document to hash. + # + # hash:: + # Hash to merge the converted element into. + def to_hash(hash = {}) + node_hash = {} + + # Insert node hash into parent hash correctly. + case hash[name] + when Array then hash[name] << node_hash + when Hash then hash[name] = [hash[name], node_hash] + when nil then hash[name] = node_hash + end + + # Handle child elements + children.each do |c| + if c.element? + c.to_hash(node_hash) + elsif c.text? || c.cdata? + node_hash[CONTENT_ROOT] ||= +"" + node_hash[CONTENT_ROOT] << c.content + end + end + + # Remove content node if it is blank and there are child tags + if node_hash.length > 1 && node_hash[CONTENT_ROOT].blank? + node_hash.delete(CONTENT_ROOT) + end + + # Handle attributes + attribute_nodes.each { |a| node_hash[a.node_name] = a.value } + + hash + end + end + end + + Nokogiri::XML::Document.include(Conversions::Document) + Nokogiri::XML::Node.include(Conversions::Node) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/xml_mini/nokogirisax.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/xml_mini/nokogirisax.rb new file mode 100644 index 0000000..f3ba109 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/xml_mini/nokogirisax.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +begin + require "nokogiri" +rescue LoadError => e + $stderr.puts "You don't have nokogiri installed in your application. Please add it to your Gemfile and run bundle install" + raise e +end +require "active_support/core_ext/object/blank" +require "stringio" + +module ActiveSupport + module XmlMini_NokogiriSAX # :nodoc: + extend self + + # Class that will build the hash while the XML document + # is being parsed using SAX events. + class HashBuilder < Nokogiri::XML::SAX::Document + CONTENT_KEY = "__content__" + HASH_SIZE_KEY = "__hash_size__" + + attr_reader :hash + + def current_hash + @hash_stack.last + end + + def start_document + @hash = {} + @hash_stack = [@hash] + end + + def end_document + raise "Parse stack not empty!" if @hash_stack.size > 1 + end + + def error(error_message) + raise error_message + end + + def start_element(name, attrs = []) + new_hash = { CONTENT_KEY => +"" }.merge!(Hash[attrs]) + new_hash[HASH_SIZE_KEY] = new_hash.size + 1 + + case current_hash[name] + when Array then current_hash[name] << new_hash + when Hash then current_hash[name] = [current_hash[name], new_hash] + when nil then current_hash[name] = new_hash + end + + @hash_stack.push(new_hash) + end + + def end_element(name) + if current_hash.length > current_hash.delete(HASH_SIZE_KEY) && current_hash[CONTENT_KEY].blank? || current_hash[CONTENT_KEY] == "" + current_hash.delete(CONTENT_KEY) + end + @hash_stack.pop + end + + def characters(string) + current_hash[CONTENT_KEY] << string + end + + alias_method :cdata_block, :characters + end + + attr_accessor :document_class + self.document_class = HashBuilder + + def parse(data) + if !data.respond_to?(:read) + data = StringIO.new(data || "") + end + + if data.eof? + {} + else + document = document_class.new + parser = Nokogiri::XML::SAX::Parser.new(document) + parser.parse(data) + document.hash + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/xml_mini/rexml.rb b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/xml_mini/rexml.rb new file mode 100644 index 0000000..e4e1a1f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.4/lib/active_support/xml_mini/rexml.rb @@ -0,0 +1,137 @@ +# frozen_string_literal: true + +require "active_support/core_ext/kernel/reporting" +require "active_support/core_ext/object/blank" +require "stringio" + +module ActiveSupport + module XmlMini_REXML # :nodoc: + extend self + + CONTENT_KEY = "__content__" + + # Parse an XML Document string or IO into a simple hash. + # + # Same as XmlSimple::xml_in but doesn't shoot itself in the foot, + # and uses the defaults from Active Support. + # + # data:: + # XML Document string or IO to parse + def parse(data) + if !data.respond_to?(:read) + data = StringIO.new(data || "") + end + + if data.eof? + {} + else + require_rexml unless defined?(REXML::Document) + doc = REXML::Document.new(data) + + if doc.root + merge_element!({}, doc.root, XmlMini.depth) + else + raise REXML::ParseException, + "The document #{doc.to_s.inspect} does not have a valid root" + end + end + end + + private + def require_rexml + silence_warnings { require "rexml/document" } + rescue LoadError => e + $stderr.puts "You don't have rexml installed in your application. Please add it to your Gemfile and run bundle install" + raise e + end + + # Convert an XML element and merge into the hash + # + # hash:: + # Hash to merge the converted element into. + # element:: + # XML element to merge into hash + def merge_element!(hash, element, depth) + raise REXML::ParseException, "The document is too deep" if depth == 0 + merge!(hash, element.name, collapse(element, depth)) + end + + # Actually converts an XML document element into a data structure. + # + # element:: + # The document element to be collapsed. + def collapse(element, depth) + hash = get_attributes(element) + + if element.has_elements? + element.each_element { |child| merge_element!(hash, child, depth - 1) } + merge_texts!(hash, element) unless empty_content?(element) + hash + else + merge_texts!(hash, element) + end + end + + # Merge all the texts of an element into the hash + # + # hash:: + # Hash to add the converted element to. + # element:: + # XML element whose texts are to me merged into the hash + def merge_texts!(hash, element) + unless element.has_text? + hash + else + # must use value to prevent double-escaping + texts = +"" + element.texts.each { |t| texts << t.value } + merge!(hash, CONTENT_KEY, texts) + end + end + + # Adds a new key/value pair to an existing Hash. If the key to be added + # already exists and the existing value associated with key is not + # an Array, it will be wrapped in an Array. Then the new value is + # appended to that Array. + # + # hash:: + # Hash to add key/value pair to. + # key:: + # Key to be added. + # value:: + # Value to be associated with key. + def merge!(hash, key, value) + if hash.has_key?(key) + if hash[key].instance_of?(Array) + hash[key] << value + else + hash[key] = [hash[key], value] + end + elsif value.instance_of?(Array) + hash[key] = [value] + else + hash[key] = value + end + hash + end + + # Converts the attributes array of an XML element into a hash. + # Returns an empty Hash if node has no attributes. + # + # element:: + # XML element to extract attributes from. + def get_attributes(element) + attributes = {} + element.attributes.each { |n, v| attributes[n] = v } + attributes + end + + # Determines if a document element has text content + # + # element:: + # XML element to be checked. + def empty_content?(element) + element.texts.join.blank? + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/CHANGELOG.md b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/CHANGELOG.md new file mode 100644 index 0000000..c15168d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/CHANGELOG.md @@ -0,0 +1,262 @@ +# Addressable 2.8.1 +- refactor `Addressable::URI.normalize_path` to address linter offenses ([#430](https://github.com/sporkmonger/addressable/pull/430)) +- remove redundant colon in `Addressable::URI::CharacterClasses::AUTHORITY` regex ([#438](https://github.com/sporkmonger/addressable/pull/438)) +- update gemspec to reflect supported Ruby versions ([#466], [#464], [#463]) +- compatibility w/ public_suffix 5.x ([#466], [#465], [#460]) +- fixes "invalid byte sequence in UTF-8" exception when unencoding URLs containing non UTF-8 characters ([#459](https://github.com/sporkmonger/addressable/pull/459)) +- `Ractor` compatibility ([#449](https://github.com/sporkmonger/addressable/pull/449)) +- use the whole string instead of a single line for template match ([#431](https://github.com/sporkmonger/addressable/pull/431)) +- force UTF-8 encoding only if needed ([#341](https://github.com/sporkmonger/addressable/pull/341)) + +[#460]: https://github.com/sporkmonger/addressable/pull/460 +[#463]: https://github.com/sporkmonger/addressable/pull/463 +[#464]: https://github.com/sporkmonger/addressable/pull/464 +[#465]: https://github.com/sporkmonger/addressable/pull/465 +[#466]: https://github.com/sporkmonger/addressable/pull/466 + +# Addressable 2.8.0 +- fixes ReDoS vulnerability in Addressable::Template#match +- no longer replaces `+` with spaces in queries for non-http(s) schemes +- fixed encoding ipv6 literals +- the `:compacted` flag for `normalized_query` now dedupes parameters +- fix broken `escape_component` alias +- dropping support for Ruby 2.0 and 2.1 +- adding Ruby 3.0 compatibility for development tasks +- drop support for `rack-mount` and remove Addressable::Template#generate +- performance improvements +- switch CI/CD to GitHub Actions + +# Addressable 2.7.0 +- added `:compacted` flag to `normalized_query` +- `heuristic_parse` handles `mailto:` more intuitively +- dropped explicit support for JRuby 9.0.5.0 +- compatibility w/ public_suffix 4.x +- performance improvements + +# Addressable 2.6.0 +- added `tld=` method to allow assignment to the public suffix +- most `heuristic_parse` patterns are now case-insensitive +- `heuristic_parse` handles more `file://` URI variations +- fixes bug in `heuristic_parse` when uri starts with digit +- fixes bug in `request_uri=` with query strings +- fixes template issues with `nil` and `?` operator +- `frozen_string_literal` pragmas added +- minor performance improvements in regexps +- fixes to eliminate warnings + +# Addressable 2.5.2 +- better support for frozen string literals +- fixed bug w/ uppercase characters in scheme +- IDNA errors w/ emoji URLs +- compatibility w/ public_suffix 3.x + +# Addressable 2.5.1 +- allow unicode normalization to be disabled for URI Template expansion +- removed duplicate test + +# Addressable 2.5.0 +- dropping support for Ruby 1.9 +- adding support for Ruby 2.4 preview +- add support for public suffixes and tld; first runtime dependency +- hostname escaping should match RFC; underscores in hostnames no longer escaped +- paths beginning with // and missing an authority are now considered invalid +- validation now also takes place after setting a path +- handle backslashes in authority more like a browser for `heuristic_parse` +- unescaped backslashes in host now raise an `InvalidURIError` +- `merge!`, `join!`, `omit!` and `normalize!` don't disable deferred validation +- `heuristic_parse` now trims whitespace before parsing +- host parts longer than 63 bytes will be ignored and not passed to libidn +- normalized values always encoded as UTF-8 + +# Addressable 2.4.0 +- support for 1.8.x dropped +- double quotes in a host now raises an error +- newlines in host will no longer get unescaped during normalization +- stricter handling of bogus scheme values +- stricter handling of encoded port values +- calling `require 'addressable'` will now load both the URI and Template files +- assigning to the `hostname` component with an `IPAddr` object is now supported +- assigning to the `origin` component is now supported +- fixed minor bug where an exception would be thrown for a missing ACE suffix +- better partial expansion of URI templates + +# Addressable 2.3.8 +- fix warnings +- update dependency gems +- support for 1.8.x officially deprecated + +# Addressable 2.3.7 +- fix scenario in which invalid URIs don't get an exception until inspected +- handle hostnames with two adjacent periods correctly +- upgrade of RSpec + +# Addressable 2.3.6 +- normalization drops empty query string +- better handling in template extract for missing values +- template modifier for `'?'` now treated as optional +- fixed issue where character class parameters were modified +- templates can now be tested for equality +- added `:sorted` option to normalization of query strings +- fixed issue with normalization of hosts given in `'example.com.'` form + +# Addressable 2.3.5 +- added Addressable::URI#empty? method +- Addressable::URI#hostname methods now strip square brackets from IPv6 hosts +- compatibility with Net::HTTP in Ruby 2.0.0 +- Addressable::URI#route_from should always give relative URIs + +# Addressable 2.3.4 +- fixed issue with encoding altering its inputs +- query string normalization now leaves ';' characters alone +- FakeFS is detected before attempting to load unicode tables +- additional testing to ensure frozen objects don't cause problems + +# Addressable 2.3.3 +- fixed issue with converting common primitives during template expansion +- fixed port encoding issue +- removed a few warnings +- normalize should now ignore %2B in query strings +- the IDNA logic should now be handled by libidn in Ruby 1.9 +- no template match should now result in nil instead of an empty MatchData +- added license information to gemspec + +# Addressable 2.3.2 +- added Addressable::URI#default_port method +- fixed issue with Marshalling Unicode data on Windows +- improved heuristic parsing to better handle IPv4 addresses + +# Addressable 2.3.1 +- fixed missing unicode data file + +# Addressable 2.3.0 +- updated Addressable::Template to use RFC 6570, level 4 +- fixed compatibility problems with some versions of Ruby +- moved unicode tables into a data file for performance reasons +- removing support for multiple query value notations + +# Addressable 2.2.8 +- fixed issues with dot segment removal code +- form encoding can now handle multiple values per key +- updated development environment + +# Addressable 2.2.7 +- fixed issues related to Addressable::URI#query_values= +- the Addressable::URI.parse method is now polymorphic + +# Addressable 2.2.6 +- changed the way ambiguous paths are handled +- fixed bug with frozen URIs +- https supported in heuristic parsing + +# Addressable 2.2.5 +- 'parsing' a pre-parsed URI object is now a dup operation +- introduced conditional support for libidn +- fixed normalization issue on ampersands in query strings +- added additional tests around handling of query strings + +# Addressable 2.2.4 +- added origin support from draft-ietf-websec-origin-00 +- resolved issue with attempting to navigate below root +- fixed bug with string splitting in query strings + +# Addressable 2.2.3 +- added :flat_array notation for query strings + +# Addressable 2.2.2 +- fixed issue with percent escaping of '+' character in query strings + +# Addressable 2.2.1 +- added support for application/x-www-form-urlencoded. + +# Addressable 2.2.0 +- added site methods +- improved documentation + +# Addressable 2.1.2 +- added HTTP request URI methods +- better handling of Windows file paths +- validation_deferred boolean replaced with defer_validation block +- normalization of percent-encoded paths should now be correct +- fixed issue with constructing URIs with relative paths +- fixed warnings + +# Addressable 2.1.1 +- more type checking changes +- fixed issue with unicode normalization +- added method to find template defaults +- symbolic keys are now allowed in template mappings +- numeric values and symbolic values are now allowed in template mappings + +# Addressable 2.1.0 +- refactored URI template support out into its own class +- removed extract method due to being useless and unreliable +- removed Addressable::URI.expand_template +- removed Addressable::URI#extract_mapping +- added partial template expansion +- fixed minor bugs in the parse and heuristic_parse methods +- fixed incompatibility with Ruby 1.9.1 +- fixed bottleneck in Addressable::URI#hash and Addressable::URI#to_s +- fixed unicode normalization exception +- updated query_values methods to better handle subscript notation +- worked around issue with freezing URIs +- improved specs + +# Addressable 2.0.2 +- fixed issue with URI template expansion +- fixed issue with percent escaping characters 0-15 + +# Addressable 2.0.1 +- fixed issue with query string assignment +- fixed issue with improperly encoded components + +# Addressable 2.0.0 +- the initialize method now takes an options hash as its only parameter +- added query_values method to URI class +- completely replaced IDNA implementation with pure Ruby +- renamed Addressable::ADDRESSABLE_VERSION to Addressable::VERSION +- completely reworked the Rakefile +- changed the behavior of the port method significantly +- Addressable::URI.encode_segment, Addressable::URI.unencode_segment renamed +- documentation is now in YARD format +- more rigorous type checking +- to_str method implemented, implicit conversion to Strings now allowed +- Addressable::URI#omit method added, Addressable::URI#merge method replaced +- updated URI Template code to match v 03 of the draft spec +- added a bunch of new specifications + +# Addressable 1.0.4 +- switched to using RSpec's pending system for specs that rely on IDN +- fixed issue with creating URIs with paths that are not prefixed with '/' + +# Addressable 1.0.3 +- implemented a hash method + +# Addressable 1.0.2 +- fixed minor bug with the extract_mapping method + +# Addressable 1.0.1 +- fixed minor bug with the extract_mapping method + +# Addressable 1.0.0 +- heuristic parse method added +- parsing is slightly more strict +- replaced to_h with to_hash +- fixed routing methods +- improved specifications +- improved heckle rake task +- no surviving heckle mutations + +# Addressable 0.1.2 +- improved normalization +- fixed bug in joining algorithm +- updated specifications + +# Addressable 0.1.1 +- updated documentation +- added URI Template variable extraction + +# Addressable 0.1.0 +- initial release +- implementation based on RFC 3986, 3987 +- support for IRIs via libidn +- support for the URI Template draft spec diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/Gemfile b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/Gemfile new file mode 100644 index 0000000..0d36ffb --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/Gemfile @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +source 'https://rubygems.org' + +gemspec + +group :test do + gem 'rspec', '~> 3.8' + gem 'rspec-its', '~> 1.3' +end + +group :coverage do + gem "coveralls", "> 0.7", require: false, platforms: :mri + gem "simplecov", require: false +end + +group :development do + gem 'launchy', '~> 2.4', '>= 2.4.3' + gem 'redcarpet', :platform => :mri_19 + gem 'yard' +end + +group :test, :development do + gem 'memory_profiler' + gem "rake", ">= 12.3.3" +end + +unless ENV["IDNA_MODE"] == "pure" + gem "idn-ruby", platform: :mri +end diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/LICENSE.txt b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/LICENSE.txt new file mode 100644 index 0000000..ef51da2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/README.md b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/README.md new file mode 100644 index 0000000..9892f61 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/README.md @@ -0,0 +1,121 @@ +# Addressable + +
+
Homepage
github.com/sporkmonger/addressable
+
Author
Bob Aman
+
Copyright
Copyright © Bob Aman
+
License
Apache 2.0
+
+ +[![Gem Version](https://img.shields.io/gem/dt/addressable.svg)][gem] +[![Build Status](https://github.com/sporkmonger/addressable/workflows/CI/badge.svg)][actions] +[![Test Coverage Status](https://img.shields.io/coveralls/sporkmonger/addressable.svg)][coveralls] +[![Documentation Coverage Status](https://inch-ci.org/github/sporkmonger/addressable.svg?branch=master)][inch] + +[gem]: https://rubygems.org/gems/addressable +[actions]: https://github.com/sporkmonger/addressable/actions +[coveralls]: https://coveralls.io/r/sporkmonger/addressable +[inch]: https://inch-ci.org/github/sporkmonger/addressable + +# Description + +Addressable is an alternative implementation to the URI implementation +that is part of Ruby's standard library. It is flexible, offers heuristic +parsing, and additionally provides extensive support for IRIs and URI templates. + +Addressable closely conforms to RFC 3986, RFC 3987, and RFC 6570 (level 4). + +# Reference + +- {Addressable::URI} +- {Addressable::Template} + +# Example usage + +```ruby +require "addressable/uri" + +uri = Addressable::URI.parse("http://example.com/path/to/resource/") +uri.scheme +#=> "http" +uri.host +#=> "example.com" +uri.path +#=> "/path/to/resource/" + +uri = Addressable::URI.parse("http://www.詹姆斯.com/") +uri.normalize +#=> # +``` + + +# URI Templates + +For more details, see [RFC 6570](https://www.rfc-editor.org/rfc/rfc6570.txt). + + +```ruby + +require "addressable/template" + +template = Addressable::Template.new("http://example.com/{?query*}") +template.expand({ + "query" => { + 'foo' => 'bar', + 'color' => 'red' + } +}) +#=> # + +template = Addressable::Template.new("http://example.com/{?one,two,three}") +template.partial_expand({"one" => "1", "three" => 3}).pattern +#=> "http://example.com/?one=1{&two}&three=3" + +template = Addressable::Template.new( + "http://{host}{/segments*}/{?one,two,bogus}{#fragment}" +) +uri = Addressable::URI.parse( + "http://example.com/a/b/c/?one=1&two=2#foo" +) +template.extract(uri) +#=> +# { +# "host" => "example.com", +# "segments" => ["a", "b", "c"], +# "one" => "1", +# "two" => "2", +# "fragment" => "foo" +# } +``` + +# Install + +```console +$ gem install addressable +``` + +You may optionally turn on native IDN support by installing libidn and the +idn gem: + +```console +$ sudo apt-get install libidn11-dev # Debian/Ubuntu +$ brew install libidn # OS X +$ gem install idn-ruby +``` + +# Semantic Versioning + +This project uses [Semantic Versioning](https://semver.org/). You can (and should) specify your +dependency using a pessimistic version constraint covering the major and minor +values: + +```ruby +spec.add_dependency 'addressable', '~> 2.7' +``` + +If you need a specific bug fix, you can also specify minimum tiny versions +without preventing updates to the latest minor release: + +```ruby +spec.add_dependency 'addressable', '~> 2.3', '>= 2.3.7' +``` diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/Rakefile b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/Rakefile new file mode 100644 index 0000000..b7e0ff3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/Rakefile @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'rubygems' +require 'rake' + +require File.join(File.dirname(__FILE__), 'lib', 'addressable', 'version') + +PKG_DISPLAY_NAME = 'Addressable' +PKG_NAME = PKG_DISPLAY_NAME.downcase +PKG_VERSION = Addressable::VERSION::STRING +PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}" + +RELEASE_NAME = "REL #{PKG_VERSION}" + +PKG_SUMMARY = "URI Implementation" +PKG_DESCRIPTION = <<-TEXT +Addressable is an alternative implementation to the URI implementation that is +part of Ruby's standard library. It is flexible, offers heuristic parsing, and +additionally provides extensive support for IRIs and URI templates. +TEXT + +PKG_FILES = FileList[ + "lib/**/*", "spec/**/*", "vendor/**/*", "data/**/*", + "tasks/**/*", + "[A-Z]*", "Rakefile" +].exclude(/pkg/).exclude(/database\.yml/). + exclude(/Gemfile\.lock/).exclude(/[_\.]git$/) + +task :default => "spec" + +WINDOWS = (RUBY_PLATFORM =~ /mswin|win32|mingw|bccwin|cygwin/) rescue false +SUDO = WINDOWS ? '' : ('sudo' unless ENV['SUDOLESS']) + +Dir['tasks/**/*.rake'].each { |rake| load rake } diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/data/unicode.data b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/data/unicode.data new file mode 100644 index 0000000..cdfc224 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/data/unicode.data differ diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable.rb b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable.rb new file mode 100644 index 0000000..b4e98b6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +require 'addressable/uri' +require 'addressable/template' diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable/idna.rb b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable/idna.rb new file mode 100644 index 0000000..2dbd393 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable/idna.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +#-- +# Copyright (C) Bob Aman +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#++ + + +begin + require "addressable/idna/native" +rescue LoadError + # libidn or the idn gem was not available, fall back on a pure-Ruby + # implementation... + require "addressable/idna/pure" +end diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable/idna/native.rb b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable/idna/native.rb new file mode 100644 index 0000000..302e1b0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable/idna/native.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +#-- +# Copyright (C) Bob Aman +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#++ + + +require "idn" + +module Addressable + module IDNA + def self.punycode_encode(value) + IDN::Punycode.encode(value.to_s) + end + + def self.punycode_decode(value) + IDN::Punycode.decode(value.to_s) + end + + def self.unicode_normalize_kc(value) + IDN::Stringprep.nfkc_normalize(value.to_s) + end + + def self.to_ascii(value) + value.to_s.split('.', -1).map do |segment| + if segment.size > 0 && segment.size < 64 + IDN::Idna.toASCII(segment, IDN::Idna::ALLOW_UNASSIGNED) + elsif segment.size >= 64 + segment + else + '' + end + end.join('.') + end + + def self.to_unicode(value) + value.to_s.split('.', -1).map do |segment| + if segment.size > 0 && segment.size < 64 + IDN::Idna.toUnicode(segment, IDN::Idna::ALLOW_UNASSIGNED) + elsif segment.size >= 64 + segment + else + '' + end + end.join('.') + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable/idna/pure.rb b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable/idna/pure.rb new file mode 100644 index 0000000..a7c796e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable/idna/pure.rb @@ -0,0 +1,677 @@ +# frozen_string_literal: true + +#-- +# Copyright (C) Bob Aman +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#++ + + +module Addressable + module IDNA + # This module is loosely based on idn_actionmailer by Mick Staugaard, + # the unicode library by Yoshida Masato, and the punycode implementation + # by Kazuhiro Nishiyama. Most of the code was copied verbatim, but + # some reformatting was done, and some translation from C was done. + # + # Without their code to work from as a base, we'd all still be relying + # on the presence of libidn. Which nobody ever seems to have installed. + # + # Original sources: + # http://github.com/staugaard/idn_actionmailer + # http://www.yoshidam.net/Ruby.html#unicode + # http://rubyforge.org/frs/?group_id=2550 + + + UNICODE_TABLE = File.expand_path( + File.join(File.dirname(__FILE__), '../../..', 'data/unicode.data') + ) + + ACE_PREFIX = "xn--" + + UTF8_REGEX = /\A(?: + [\x09\x0A\x0D\x20-\x7E] # ASCII + | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte + | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs + | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte + | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates + | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 + | [\xF1-\xF3][\x80-\xBF]{3} # planes 4nil5 + | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 + )*\z/mnx + + UTF8_REGEX_MULTIBYTE = /(?: + [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte + | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs + | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte + | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates + | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 + | [\xF1-\xF3][\x80-\xBF]{3} # planes 4nil5 + | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 + )/mnx + + # :startdoc: + + # Converts from a Unicode internationalized domain name to an ASCII + # domain name as described in RFC 3490. + def self.to_ascii(input) + input = input.to_s unless input.is_a?(String) + input = input.dup + if input.respond_to?(:force_encoding) + input.force_encoding(Encoding::ASCII_8BIT) + end + if input =~ UTF8_REGEX && input =~ UTF8_REGEX_MULTIBYTE + parts = unicode_downcase(input).split('.') + parts.map! do |part| + if part.respond_to?(:force_encoding) + part.force_encoding(Encoding::ASCII_8BIT) + end + if part =~ UTF8_REGEX && part =~ UTF8_REGEX_MULTIBYTE + ACE_PREFIX + punycode_encode(unicode_normalize_kc(part)) + else + part + end + end + parts.join('.') + else + input + end + end + + # Converts from an ASCII domain name to a Unicode internationalized + # domain name as described in RFC 3490. + def self.to_unicode(input) + input = input.to_s unless input.is_a?(String) + parts = input.split('.') + parts.map! do |part| + if part =~ /^#{ACE_PREFIX}(.+)/ + begin + punycode_decode(part[/^#{ACE_PREFIX}(.+)/, 1]) + rescue Addressable::IDNA::PunycodeBadInput + # toUnicode is explicitly defined as never-fails by the spec + part + end + else + part + end + end + output = parts.join('.') + if output.respond_to?(:force_encoding) + output.force_encoding(Encoding::UTF_8) + end + output + end + + # Unicode normalization form KC. + def self.unicode_normalize_kc(input) + input = input.to_s unless input.is_a?(String) + unpacked = input.unpack("U*") + unpacked = + unicode_compose(unicode_sort_canonical(unicode_decompose(unpacked))) + return unpacked.pack("U*") + end + + ## + # Unicode aware downcase method. + # + # @api private + # @param [String] input + # The input string. + # @return [String] The downcased result. + def self.unicode_downcase(input) + input = input.to_s unless input.is_a?(String) + unpacked = input.unpack("U*") + unpacked.map! { |codepoint| lookup_unicode_lowercase(codepoint) } + return unpacked.pack("U*") + end + private_class_method :unicode_downcase + + def self.unicode_compose(unpacked) + unpacked_result = [] + length = unpacked.length + + return unpacked if length == 0 + + starter = unpacked[0] + starter_cc = lookup_unicode_combining_class(starter) + starter_cc = 256 if starter_cc != 0 + for i in 1...length + ch = unpacked[i] + + if (starter_cc == 0 && + (composite = unicode_compose_pair(starter, ch)) != nil) + starter = composite + else + unpacked_result << starter + starter = ch + end + end + unpacked_result << starter + return unpacked_result + end + private_class_method :unicode_compose + + def self.unicode_compose_pair(ch_one, ch_two) + if ch_one >= HANGUL_LBASE && ch_one < HANGUL_LBASE + HANGUL_LCOUNT && + ch_two >= HANGUL_VBASE && ch_two < HANGUL_VBASE + HANGUL_VCOUNT + # Hangul L + V + return HANGUL_SBASE + ( + (ch_one - HANGUL_LBASE) * HANGUL_VCOUNT + (ch_two - HANGUL_VBASE) + ) * HANGUL_TCOUNT + elsif ch_one >= HANGUL_SBASE && + ch_one < HANGUL_SBASE + HANGUL_SCOUNT && + (ch_one - HANGUL_SBASE) % HANGUL_TCOUNT == 0 && + ch_two >= HANGUL_TBASE && ch_two < HANGUL_TBASE + HANGUL_TCOUNT + # Hangul LV + T + return ch_one + (ch_two - HANGUL_TBASE) + end + + p = [] + + ucs4_to_utf8(ch_one, p) + ucs4_to_utf8(ch_two, p) + + return lookup_unicode_composition(p) + end + private_class_method :unicode_compose_pair + + def self.ucs4_to_utf8(char, buffer) + if char < 128 + buffer << char + elsif char < 2048 + buffer << (char >> 6 | 192) + buffer << (char & 63 | 128) + elsif char < 0x10000 + buffer << (char >> 12 | 224) + buffer << (char >> 6 & 63 | 128) + buffer << (char & 63 | 128) + elsif char < 0x200000 + buffer << (char >> 18 | 240) + buffer << (char >> 12 & 63 | 128) + buffer << (char >> 6 & 63 | 128) + buffer << (char & 63 | 128) + elsif char < 0x4000000 + buffer << (char >> 24 | 248) + buffer << (char >> 18 & 63 | 128) + buffer << (char >> 12 & 63 | 128) + buffer << (char >> 6 & 63 | 128) + buffer << (char & 63 | 128) + elsif char < 0x80000000 + buffer << (char >> 30 | 252) + buffer << (char >> 24 & 63 | 128) + buffer << (char >> 18 & 63 | 128) + buffer << (char >> 12 & 63 | 128) + buffer << (char >> 6 & 63 | 128) + buffer << (char & 63 | 128) + end + end + private_class_method :ucs4_to_utf8 + + def self.unicode_sort_canonical(unpacked) + unpacked = unpacked.dup + i = 1 + length = unpacked.length + + return unpacked if length < 2 + + while i < length + last = unpacked[i-1] + ch = unpacked[i] + last_cc = lookup_unicode_combining_class(last) + cc = lookup_unicode_combining_class(ch) + if cc != 0 && last_cc != 0 && last_cc > cc + unpacked[i] = last + unpacked[i-1] = ch + i -= 1 if i > 1 + else + i += 1 + end + end + return unpacked + end + private_class_method :unicode_sort_canonical + + def self.unicode_decompose(unpacked) + unpacked_result = [] + for cp in unpacked + if cp >= HANGUL_SBASE && cp < HANGUL_SBASE + HANGUL_SCOUNT + l, v, t = unicode_decompose_hangul(cp) + unpacked_result << l + unpacked_result << v if v + unpacked_result << t if t + else + dc = lookup_unicode_compatibility(cp) + unless dc + unpacked_result << cp + else + unpacked_result.concat(unicode_decompose(dc.unpack("U*"))) + end + end + end + return unpacked_result + end + private_class_method :unicode_decompose + + def self.unicode_decompose_hangul(codepoint) + sindex = codepoint - HANGUL_SBASE; + if sindex < 0 || sindex >= HANGUL_SCOUNT + l = codepoint + v = t = nil + return l, v, t + end + l = HANGUL_LBASE + sindex / HANGUL_NCOUNT + v = HANGUL_VBASE + (sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT + t = HANGUL_TBASE + sindex % HANGUL_TCOUNT + if t == HANGUL_TBASE + t = nil + end + return l, v, t + end + private_class_method :unicode_decompose_hangul + + def self.lookup_unicode_combining_class(codepoint) + codepoint_data = UNICODE_DATA[codepoint] + (codepoint_data ? + (codepoint_data[UNICODE_DATA_COMBINING_CLASS] || 0) : + 0) + end + private_class_method :lookup_unicode_combining_class + + def self.lookup_unicode_compatibility(codepoint) + codepoint_data = UNICODE_DATA[codepoint] + (codepoint_data ? + codepoint_data[UNICODE_DATA_COMPATIBILITY] : nil) + end + private_class_method :lookup_unicode_compatibility + + def self.lookup_unicode_lowercase(codepoint) + codepoint_data = UNICODE_DATA[codepoint] + (codepoint_data ? + (codepoint_data[UNICODE_DATA_LOWERCASE] || codepoint) : + codepoint) + end + private_class_method :lookup_unicode_lowercase + + def self.lookup_unicode_composition(unpacked) + return COMPOSITION_TABLE[unpacked] + end + private_class_method :lookup_unicode_composition + + HANGUL_SBASE = 0xac00 + HANGUL_LBASE = 0x1100 + HANGUL_LCOUNT = 19 + HANGUL_VBASE = 0x1161 + HANGUL_VCOUNT = 21 + HANGUL_TBASE = 0x11a7 + HANGUL_TCOUNT = 28 + HANGUL_NCOUNT = HANGUL_VCOUNT * HANGUL_TCOUNT # 588 + HANGUL_SCOUNT = HANGUL_LCOUNT * HANGUL_NCOUNT # 11172 + + UNICODE_DATA_COMBINING_CLASS = 0 + UNICODE_DATA_EXCLUSION = 1 + UNICODE_DATA_CANONICAL = 2 + UNICODE_DATA_COMPATIBILITY = 3 + UNICODE_DATA_UPPERCASE = 4 + UNICODE_DATA_LOWERCASE = 5 + UNICODE_DATA_TITLECASE = 6 + + begin + if defined?(FakeFS) + fakefs_state = FakeFS.activated? + FakeFS.deactivate! + end + # This is a sparse Unicode table. Codepoints without entries are + # assumed to have the value: [0, 0, nil, nil, nil, nil, nil] + UNICODE_DATA = File.open(UNICODE_TABLE, "rb") do |file| + Marshal.load(file.read) + end + ensure + if defined?(FakeFS) + FakeFS.activate! if fakefs_state + end + end + + COMPOSITION_TABLE = {} + UNICODE_DATA.each do |codepoint, data| + canonical = data[UNICODE_DATA_CANONICAL] + exclusion = data[UNICODE_DATA_EXCLUSION] + + if canonical && exclusion == 0 + COMPOSITION_TABLE[canonical.unpack("C*")] = codepoint + end + end + + UNICODE_MAX_LENGTH = 256 + ACE_MAX_LENGTH = 256 + + PUNYCODE_BASE = 36 + PUNYCODE_TMIN = 1 + PUNYCODE_TMAX = 26 + PUNYCODE_SKEW = 38 + PUNYCODE_DAMP = 700 + PUNYCODE_INITIAL_BIAS = 72 + PUNYCODE_INITIAL_N = 0x80 + PUNYCODE_DELIMITER = 0x2D + + PUNYCODE_MAXINT = 1 << 64 + + PUNYCODE_PRINT_ASCII = + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" + + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" + + " !\"\#$%&'()*+,-./" + + "0123456789:;<=>?" + + "@ABCDEFGHIJKLMNO" + + "PQRSTUVWXYZ[\\]^_" + + "`abcdefghijklmno" + + "pqrstuvwxyz{|}~\n" + + # Input is invalid. + class PunycodeBadInput < StandardError; end + # Output would exceed the space provided. + class PunycodeBigOutput < StandardError; end + # Input needs wider integers to process. + class PunycodeOverflow < StandardError; end + + def self.punycode_encode(unicode) + unicode = unicode.to_s unless unicode.is_a?(String) + input = unicode.unpack("U*") + output = [0] * (ACE_MAX_LENGTH + 1) + input_length = input.size + output_length = [ACE_MAX_LENGTH] + + # Initialize the state + n = PUNYCODE_INITIAL_N + delta = out = 0 + max_out = output_length[0] + bias = PUNYCODE_INITIAL_BIAS + + # Handle the basic code points: + input_length.times do |j| + if punycode_basic?(input[j]) + if max_out - out < 2 + raise PunycodeBigOutput, + "Output would exceed the space provided." + end + output[out] = input[j] + out += 1 + end + end + + h = b = out + + # h is the number of code points that have been handled, b is the + # number of basic code points, and out is the number of characters + # that have been output. + + if b > 0 + output[out] = PUNYCODE_DELIMITER + out += 1 + end + + # Main encoding loop: + + while h < input_length + # All non-basic code points < n have been + # handled already. Find the next larger one: + + m = PUNYCODE_MAXINT + input_length.times do |j| + m = input[j] if (n...m) === input[j] + end + + # Increase delta enough to advance the decoder's + # state to , but guard against overflow: + + if m - n > (PUNYCODE_MAXINT - delta) / (h + 1) + raise PunycodeOverflow, "Input needs wider integers to process." + end + delta += (m - n) * (h + 1) + n = m + + input_length.times do |j| + # Punycode does not need to check whether input[j] is basic: + if input[j] < n + delta += 1 + if delta == 0 + raise PunycodeOverflow, + "Input needs wider integers to process." + end + end + + if input[j] == n + # Represent delta as a generalized variable-length integer: + + q = delta; k = PUNYCODE_BASE + while true + if out >= max_out + raise PunycodeBigOutput, + "Output would exceed the space provided." + end + t = ( + if k <= bias + PUNYCODE_TMIN + elsif k >= bias + PUNYCODE_TMAX + PUNYCODE_TMAX + else + k - bias + end + ) + break if q < t + output[out] = + punycode_encode_digit(t + (q - t) % (PUNYCODE_BASE - t)) + out += 1 + q = (q - t) / (PUNYCODE_BASE - t) + k += PUNYCODE_BASE + end + + output[out] = punycode_encode_digit(q) + out += 1 + bias = punycode_adapt(delta, h + 1, h == b) + delta = 0 + h += 1 + end + end + + delta += 1 + n += 1 + end + + output_length[0] = out + + outlen = out + outlen.times do |j| + c = output[j] + unless c >= 0 && c <= 127 + raise StandardError, "Invalid output char." + end + unless PUNYCODE_PRINT_ASCII[c] + raise PunycodeBadInput, "Input is invalid." + end + end + + output[0..outlen].map { |x| x.chr }.join("").sub(/\0+\z/, "") + end + private_class_method :punycode_encode + + def self.punycode_decode(punycode) + input = [] + output = [] + + if ACE_MAX_LENGTH * 2 < punycode.size + raise PunycodeBigOutput, "Output would exceed the space provided." + end + punycode.each_byte do |c| + unless c >= 0 && c <= 127 + raise PunycodeBadInput, "Input is invalid." + end + input.push(c) + end + + input_length = input.length + output_length = [UNICODE_MAX_LENGTH] + + # Initialize the state + n = PUNYCODE_INITIAL_N + + out = i = 0 + max_out = output_length[0] + bias = PUNYCODE_INITIAL_BIAS + + # Handle the basic code points: Let b be the number of input code + # points before the last delimiter, or 0 if there is none, then + # copy the first b code points to the output. + + b = 0 + input_length.times do |j| + b = j if punycode_delimiter?(input[j]) + end + if b > max_out + raise PunycodeBigOutput, "Output would exceed the space provided." + end + + b.times do |j| + unless punycode_basic?(input[j]) + raise PunycodeBadInput, "Input is invalid." + end + output[out] = input[j] + out+=1 + end + + # Main decoding loop: Start just after the last delimiter if any + # basic code points were copied; start at the beginning otherwise. + + in_ = b > 0 ? b + 1 : 0 + while in_ < input_length + + # in_ is the index of the next character to be consumed, and + # out is the number of code points in the output array. + + # Decode a generalized variable-length integer into delta, + # which gets added to i. The overflow checking is easier + # if we increase i as we go, then subtract off its starting + # value at the end to obtain delta. + + oldi = i; w = 1; k = PUNYCODE_BASE + while true + if in_ >= input_length + raise PunycodeBadInput, "Input is invalid." + end + digit = punycode_decode_digit(input[in_]) + in_+=1 + if digit >= PUNYCODE_BASE + raise PunycodeBadInput, "Input is invalid." + end + if digit > (PUNYCODE_MAXINT - i) / w + raise PunycodeOverflow, "Input needs wider integers to process." + end + i += digit * w + t = ( + if k <= bias + PUNYCODE_TMIN + elsif k >= bias + PUNYCODE_TMAX + PUNYCODE_TMAX + else + k - bias + end + ) + break if digit < t + if w > PUNYCODE_MAXINT / (PUNYCODE_BASE - t) + raise PunycodeOverflow, "Input needs wider integers to process." + end + w *= PUNYCODE_BASE - t + k += PUNYCODE_BASE + end + + bias = punycode_adapt(i - oldi, out + 1, oldi == 0) + + # I was supposed to wrap around from out + 1 to 0, + # incrementing n each time, so we'll fix that now: + + if i / (out + 1) > PUNYCODE_MAXINT - n + raise PunycodeOverflow, "Input needs wider integers to process." + end + n += i / (out + 1) + i %= out + 1 + + # Insert n at position i of the output: + + # not needed for Punycode: + # raise PUNYCODE_INVALID_INPUT if decode_digit(n) <= base + if out >= max_out + raise PunycodeBigOutput, "Output would exceed the space provided." + end + + #memmove(output + i + 1, output + i, (out - i) * sizeof *output) + output[i + 1, out - i] = output[i, out - i] + output[i] = n + i += 1 + + out += 1 + end + + output_length[0] = out + + output.pack("U*") + end + private_class_method :punycode_decode + + def self.punycode_basic?(codepoint) + codepoint < 0x80 + end + private_class_method :punycode_basic? + + def self.punycode_delimiter?(codepoint) + codepoint == PUNYCODE_DELIMITER + end + private_class_method :punycode_delimiter? + + def self.punycode_encode_digit(d) + d + 22 + 75 * ((d < 26) ? 1 : 0) + end + private_class_method :punycode_encode_digit + + # Returns the numeric value of a basic codepoint + # (for use in representing integers) in the range 0 to + # base - 1, or PUNYCODE_BASE if codepoint does not represent a value. + def self.punycode_decode_digit(codepoint) + if codepoint - 48 < 10 + codepoint - 22 + elsif codepoint - 65 < 26 + codepoint - 65 + elsif codepoint - 97 < 26 + codepoint - 97 + else + PUNYCODE_BASE + end + end + private_class_method :punycode_decode_digit + + # Bias adaptation method + def self.punycode_adapt(delta, numpoints, firsttime) + delta = firsttime ? delta / PUNYCODE_DAMP : delta >> 1 + # delta >> 1 is a faster way of doing delta / 2 + delta += delta / numpoints + difference = PUNYCODE_BASE - PUNYCODE_TMIN + + k = 0 + while delta > (difference * PUNYCODE_TMAX) / 2 + delta /= difference + k += PUNYCODE_BASE + end + + k + (difference + 1) * delta / (delta + PUNYCODE_SKEW) + end + private_class_method :punycode_adapt + end + # :startdoc: +end diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable/template.rb b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable/template.rb new file mode 100644 index 0000000..9e8174b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable/template.rb @@ -0,0 +1,1030 @@ +# frozen_string_literal: true + +#-- +# Copyright (C) Bob Aman +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#++ + + +require "addressable/version" +require "addressable/uri" + +module Addressable + ## + # This is an implementation of a URI template based on + # RFC 6570 (http://tools.ietf.org/html/rfc6570). + class Template + # Constants used throughout the template code. + anything = + Addressable::URI::CharacterClasses::RESERVED + + Addressable::URI::CharacterClasses::UNRESERVED + + + variable_char_class = + Addressable::URI::CharacterClasses::ALPHA + + Addressable::URI::CharacterClasses::DIGIT + '_' + + var_char = + "(?>(?:[#{variable_char_class}]|%[a-fA-F0-9][a-fA-F0-9])+)" + RESERVED = + "(?:[#{anything}]|%[a-fA-F0-9][a-fA-F0-9])" + UNRESERVED = + "(?:[#{ + Addressable::URI::CharacterClasses::UNRESERVED + }]|%[a-fA-F0-9][a-fA-F0-9])" + variable = + "(?:#{var_char}(?:\\.?#{var_char})*)" + varspec = + "(?:(#{variable})(\\*|:\\d+)?)" + VARNAME = + /^#{variable}$/ + VARSPEC = + /^#{varspec}$/ + VARIABLE_LIST = + /^#{varspec}(?:,#{varspec})*$/ + operator = + "+#./;?&=,!@|" + EXPRESSION = + /\{([#{operator}])?(#{varspec}(?:,#{varspec})*)\}/ + + + LEADERS = { + '?' => '?', + '/' => '/', + '#' => '#', + '.' => '.', + ';' => ';', + '&' => '&' + } + JOINERS = { + '?' => '&', + '.' => '.', + ';' => ';', + '&' => '&', + '/' => '/' + } + + ## + # Raised if an invalid template value is supplied. + class InvalidTemplateValueError < StandardError + end + + ## + # Raised if an invalid template operator is used in a pattern. + class InvalidTemplateOperatorError < StandardError + end + + ## + # Raised if an invalid template operator is used in a pattern. + class TemplateOperatorAbortedError < StandardError + end + + ## + # This class represents the data that is extracted when a Template + # is matched against a URI. + class MatchData + ## + # Creates a new MatchData object. + # MatchData objects should never be instantiated directly. + # + # @param [Addressable::URI] uri + # The URI that the template was matched against. + def initialize(uri, template, mapping) + @uri = uri.dup.freeze + @template = template + @mapping = mapping.dup.freeze + end + + ## + # @return [Addressable::URI] + # The URI that the Template was matched against. + attr_reader :uri + + ## + # @return [Addressable::Template] + # The Template used for the match. + attr_reader :template + + ## + # @return [Hash] + # The mapping that resulted from the match. + # Note that this mapping does not include keys or values for + # variables that appear in the Template, but are not present + # in the URI. + attr_reader :mapping + + ## + # @return [Array] + # The list of variables that were present in the Template. + # Note that this list will include variables which do not appear + # in the mapping because they were not present in URI. + def variables + self.template.variables + end + alias_method :keys, :variables + alias_method :names, :variables + + ## + # @return [Array] + # The list of values that were captured by the Template. + # Note that this list will include nils for any variables which + # were in the Template, but did not appear in the URI. + def values + @values ||= self.variables.inject([]) do |accu, key| + accu << self.mapping[key] + accu + end + end + alias_method :captures, :values + + ## + # Accesses captured values by name or by index. + # + # @param [String, Symbol, Fixnum] key + # Capture index or name. Note that when accessing by with index + # of 0, the full URI will be returned. The intention is to mimic + # the ::MatchData#[] behavior. + # + # @param [#to_int, nil] len + # If provided, an array of values will be returend with the given + # parameter used as length. + # + # @return [Array, String, nil] + # The captured value corresponding to the index or name. If the + # value was not provided or the key is unknown, nil will be + # returned. + # + # If the second parameter is provided, an array of that length will + # be returned instead. + def [](key, len = nil) + if len + to_a[key, len] + elsif String === key or Symbol === key + mapping[key.to_s] + else + to_a[key] + end + end + + ## + # @return [Array] + # Array with the matched URI as first element followed by the captured + # values. + def to_a + [to_s, *values] + end + + ## + # @return [String] + # The matched URI as String. + def to_s + uri.to_s + end + alias_method :string, :to_s + + # Returns multiple captured values at once. + # + # @param [String, Symbol, Fixnum] *indexes + # Indices of the captures to be returned + # + # @return [Array] + # Values corresponding to given indices. + # + # @see Addressable::Template::MatchData#[] + def values_at(*indexes) + indexes.map { |i| self[i] } + end + + ## + # Returns a String representation of the MatchData's state. + # + # @return [String] The MatchData's state, as a String. + def inspect + sprintf("#<%s:%#0x RESULT:%s>", + self.class.to_s, self.object_id, self.mapping.inspect) + end + + ## + # Dummy method for code expecting a ::MatchData instance + # + # @return [String] An empty string. + def pre_match + "" + end + alias_method :post_match, :pre_match + end + + ## + # Creates a new Addressable::Template object. + # + # @param [#to_str] pattern The URI Template pattern. + # + # @return [Addressable::Template] The initialized Template object. + def initialize(pattern) + if !pattern.respond_to?(:to_str) + raise TypeError, "Can't convert #{pattern.class} into String." + end + @pattern = pattern.to_str.dup.freeze + end + + ## + # Freeze URI, initializing instance variables. + # + # @return [Addressable::URI] The frozen URI object. + def freeze + self.variables + self.variable_defaults + self.named_captures + super + end + + ## + # @return [String] The Template object's pattern. + attr_reader :pattern + + ## + # Returns a String representation of the Template object's state. + # + # @return [String] The Template object's state, as a String. + def inspect + sprintf("#<%s:%#0x PATTERN:%s>", + self.class.to_s, self.object_id, self.pattern) + end + + ## + # Returns true if the Template objects are equal. This method + # does NOT normalize either Template before doing the comparison. + # + # @param [Object] template The Template to compare. + # + # @return [TrueClass, FalseClass] + # true if the Templates are equivalent, false + # otherwise. + def ==(template) + return false unless template.kind_of?(Template) + return self.pattern == template.pattern + end + + ## + # Addressable::Template makes no distinction between `==` and `eql?`. + # + # @see #== + alias_method :eql?, :== + + ## + # Extracts a mapping from the URI using a URI Template pattern. + # + # @param [Addressable::URI, #to_str] uri + # The URI to extract from. + # + # @param [#restore, #match] processor + # A template processor object may optionally be supplied. + # + # The object should respond to either the restore or + # match messages or both. The restore method should + # take two parameters: `[String] name` and `[String] value`. + # The restore method should reverse any transformations that + # have been performed on the value to ensure a valid URI. + # The match method should take a single + # parameter: `[String] name`. The match method should return + # a String containing a regular expression capture group for + # matching on that particular variable. The default value is `".*?"`. + # The match method has no effect on multivariate operator + # expansions. + # + # @return [Hash, NilClass] + # The Hash mapping that was extracted from the URI, or + # nil if the URI didn't match the template. + # + # @example + # class ExampleProcessor + # def self.restore(name, value) + # return value.gsub(/\+/, " ") if name == "query" + # return value + # end + # + # def self.match(name) + # return ".*?" if name == "first" + # return ".*" + # end + # end + # + # uri = Addressable::URI.parse( + # "http://example.com/search/an+example+search+query/" + # ) + # Addressable::Template.new( + # "http://example.com/search/{query}/" + # ).extract(uri, ExampleProcessor) + # #=> {"query" => "an example search query"} + # + # uri = Addressable::URI.parse("http://example.com/a/b/c/") + # Addressable::Template.new( + # "http://example.com/{first}/{second}/" + # ).extract(uri, ExampleProcessor) + # #=> {"first" => "a", "second" => "b/c"} + # + # uri = Addressable::URI.parse("http://example.com/a/b/c/") + # Addressable::Template.new( + # "http://example.com/{first}/{-list|/|second}/" + # ).extract(uri) + # #=> {"first" => "a", "second" => ["b", "c"]} + def extract(uri, processor=nil) + match_data = self.match(uri, processor) + return (match_data ? match_data.mapping : nil) + end + + ## + # Extracts match data from the URI using a URI Template pattern. + # + # @param [Addressable::URI, #to_str] uri + # The URI to extract from. + # + # @param [#restore, #match] processor + # A template processor object may optionally be supplied. + # + # The object should respond to either the restore or + # match messages or both. The restore method should + # take two parameters: `[String] name` and `[String] value`. + # The restore method should reverse any transformations that + # have been performed on the value to ensure a valid URI. + # The match method should take a single + # parameter: `[String] name`. The match method should return + # a String containing a regular expression capture group for + # matching on that particular variable. The default value is `".*?"`. + # The match method has no effect on multivariate operator + # expansions. + # + # @return [Hash, NilClass] + # The Hash mapping that was extracted from the URI, or + # nil if the URI didn't match the template. + # + # @example + # class ExampleProcessor + # def self.restore(name, value) + # return value.gsub(/\+/, " ") if name == "query" + # return value + # end + # + # def self.match(name) + # return ".*?" if name == "first" + # return ".*" + # end + # end + # + # uri = Addressable::URI.parse( + # "http://example.com/search/an+example+search+query/" + # ) + # match = Addressable::Template.new( + # "http://example.com/search/{query}/" + # ).match(uri, ExampleProcessor) + # match.variables + # #=> ["query"] + # match.captures + # #=> ["an example search query"] + # + # uri = Addressable::URI.parse("http://example.com/a/b/c/") + # match = Addressable::Template.new( + # "http://example.com/{first}/{+second}/" + # ).match(uri, ExampleProcessor) + # match.variables + # #=> ["first", "second"] + # match.captures + # #=> ["a", "b/c"] + # + # uri = Addressable::URI.parse("http://example.com/a/b/c/") + # match = Addressable::Template.new( + # "http://example.com/{first}{/second*}/" + # ).match(uri) + # match.variables + # #=> ["first", "second"] + # match.captures + # #=> ["a", ["b", "c"]] + def match(uri, processor=nil) + uri = Addressable::URI.parse(uri) unless uri.is_a?(Addressable::URI) + mapping = {} + + # First, we need to process the pattern, and extract the values. + expansions, expansion_regexp = + parse_template_pattern(pattern, processor) + + return nil unless uri.to_str.match(expansion_regexp) + unparsed_values = uri.to_str.scan(expansion_regexp).flatten + + if uri.to_str == pattern + return Addressable::Template::MatchData.new(uri, self, mapping) + elsif expansions.size > 0 + index = 0 + expansions.each do |expansion| + _, operator, varlist = *expansion.match(EXPRESSION) + varlist.split(',').each do |varspec| + _, name, modifier = *varspec.match(VARSPEC) + mapping[name] ||= nil + case operator + when nil, '+', '#', '/', '.' + unparsed_value = unparsed_values[index] + name = varspec[VARSPEC, 1] + value = unparsed_value + value = value.split(JOINERS[operator]) if value && modifier == '*' + when ';', '?', '&' + if modifier == '*' + if unparsed_values[index] + value = unparsed_values[index].split(JOINERS[operator]) + value = value.inject({}) do |acc, v| + key, val = v.split('=') + val = "" if val.nil? + acc[key] = val + acc + end + end + else + if (unparsed_values[index]) + name, value = unparsed_values[index].split('=') + value = "" if value.nil? + end + end + end + if processor != nil && processor.respond_to?(:restore) + value = processor.restore(name, value) + end + if processor == nil + if value.is_a?(Hash) + value = value.inject({}){|acc, (k, v)| + acc[Addressable::URI.unencode_component(k)] = + Addressable::URI.unencode_component(v) + acc + } + elsif value.is_a?(Array) + value = value.map{|v| Addressable::URI.unencode_component(v) } + else + value = Addressable::URI.unencode_component(value) + end + end + if !mapping.has_key?(name) || mapping[name].nil? + # Doesn't exist, set to value (even if value is nil) + mapping[name] = value + end + index = index + 1 + end + end + return Addressable::Template::MatchData.new(uri, self, mapping) + else + return nil + end + end + + ## + # Expands a URI template into another URI template. + # + # @param [Hash] mapping The mapping that corresponds to the pattern. + # @param [#validate, #transform] processor + # An optional processor object may be supplied. + # @param [Boolean] normalize_values + # Optional flag to enable/disable unicode normalization. Default: true + # + # The object should respond to either the validate or + # transform messages or both. Both the validate and + # transform methods should take two parameters: name and + # value. The validate method should return true + # or false; true if the value of the variable is valid, + # false otherwise. An InvalidTemplateValueError + # exception will be raised if the value is invalid. The transform + # method should return the transformed variable value as a String. + # If a transform method is used, the value will not be percent + # encoded automatically. Unicode normalization will be performed both + # before and after sending the value to the transform method. + # + # @return [Addressable::Template] The partially expanded URI template. + # + # @example + # Addressable::Template.new( + # "http://example.com/{one}/{two}/" + # ).partial_expand({"one" => "1"}).pattern + # #=> "http://example.com/1/{two}/" + # + # Addressable::Template.new( + # "http://example.com/{?one,two}/" + # ).partial_expand({"one" => "1"}).pattern + # #=> "http://example.com/?one=1{&two}/" + # + # Addressable::Template.new( + # "http://example.com/{?one,two,three}/" + # ).partial_expand({"one" => "1", "three" => 3}).pattern + # #=> "http://example.com/?one=1{&two}&three=3" + def partial_expand(mapping, processor=nil, normalize_values=true) + result = self.pattern.dup + mapping = normalize_keys(mapping) + result.gsub!( EXPRESSION ) do |capture| + transform_partial_capture(mapping, capture, processor, normalize_values) + end + return Addressable::Template.new(result) + end + + ## + # Expands a URI template into a full URI. + # + # @param [Hash] mapping The mapping that corresponds to the pattern. + # @param [#validate, #transform] processor + # An optional processor object may be supplied. + # @param [Boolean] normalize_values + # Optional flag to enable/disable unicode normalization. Default: true + # + # The object should respond to either the validate or + # transform messages or both. Both the validate and + # transform methods should take two parameters: name and + # value. The validate method should return true + # or false; true if the value of the variable is valid, + # false otherwise. An InvalidTemplateValueError + # exception will be raised if the value is invalid. The transform + # method should return the transformed variable value as a String. + # If a transform method is used, the value will not be percent + # encoded automatically. Unicode normalization will be performed both + # before and after sending the value to the transform method. + # + # @return [Addressable::URI] The expanded URI template. + # + # @example + # class ExampleProcessor + # def self.validate(name, value) + # return !!(value =~ /^[\w ]+$/) if name == "query" + # return true + # end + # + # def self.transform(name, value) + # return value.gsub(/ /, "+") if name == "query" + # return value + # end + # end + # + # Addressable::Template.new( + # "http://example.com/search/{query}/" + # ).expand( + # {"query" => "an example search query"}, + # ExampleProcessor + # ).to_str + # #=> "http://example.com/search/an+example+search+query/" + # + # Addressable::Template.new( + # "http://example.com/search/{query}/" + # ).expand( + # {"query" => "an example search query"} + # ).to_str + # #=> "http://example.com/search/an%20example%20search%20query/" + # + # Addressable::Template.new( + # "http://example.com/search/{query}/" + # ).expand( + # {"query" => "bogus!"}, + # ExampleProcessor + # ).to_str + # #=> Addressable::Template::InvalidTemplateValueError + def expand(mapping, processor=nil, normalize_values=true) + result = self.pattern.dup + mapping = normalize_keys(mapping) + result.gsub!( EXPRESSION ) do |capture| + transform_capture(mapping, capture, processor, normalize_values) + end + return Addressable::URI.parse(result) + end + + ## + # Returns an Array of variables used within the template pattern. + # The variables are listed in the Array in the order they appear within + # the pattern. Multiple occurrences of a variable within a pattern are + # not represented in this Array. + # + # @return [Array] The variables present in the template's pattern. + def variables + @variables ||= ordered_variable_defaults.map { |var, val| var }.uniq + end + alias_method :keys, :variables + alias_method :names, :variables + + ## + # Returns a mapping of variables to their default values specified + # in the template. Variables without defaults are not returned. + # + # @return [Hash] Mapping of template variables to their defaults + def variable_defaults + @variable_defaults ||= + Hash[*ordered_variable_defaults.reject { |k, v| v.nil? }.flatten] + end + + ## + # Coerces a template into a `Regexp` object. This regular expression will + # behave very similarly to the actual template, and should match the same + # URI values, but it cannot fully handle, for example, values that would + # extract to an `Array`. + # + # @return [Regexp] A regular expression which should match the template. + def to_regexp + _, source = parse_template_pattern(pattern) + Regexp.new(source) + end + + ## + # Returns the source of the coerced `Regexp`. + # + # @return [String] The source of the `Regexp` given by {#to_regexp}. + # + # @api private + def source + self.to_regexp.source + end + + ## + # Returns the named captures of the coerced `Regexp`. + # + # @return [Hash] The named captures of the `Regexp` given by {#to_regexp}. + # + # @api private + def named_captures + self.to_regexp.named_captures + end + + private + def ordered_variable_defaults + @ordered_variable_defaults ||= begin + expansions, _ = parse_template_pattern(pattern) + expansions.flat_map do |capture| + _, _, varlist = *capture.match(EXPRESSION) + varlist.split(',').map do |varspec| + varspec[VARSPEC, 1] + end + end + end + end + + + ## + # Loops through each capture and expands any values available in mapping + # + # @param [Hash] mapping + # Set of keys to expand + # @param [String] capture + # The expression to expand + # @param [#validate, #transform] processor + # An optional processor object may be supplied. + # @param [Boolean] normalize_values + # Optional flag to enable/disable unicode normalization. Default: true + # + # The object should respond to either the validate or + # transform messages or both. Both the validate and + # transform methods should take two parameters: name and + # value. The validate method should return true + # or false; true if the value of the variable is valid, + # false otherwise. An InvalidTemplateValueError exception + # will be raised if the value is invalid. The transform method + # should return the transformed variable value as a String. If a + # transform method is used, the value will not be percent encoded + # automatically. Unicode normalization will be performed both before and + # after sending the value to the transform method. + # + # @return [String] The expanded expression + def transform_partial_capture(mapping, capture, processor = nil, + normalize_values = true) + _, operator, varlist = *capture.match(EXPRESSION) + + vars = varlist.split(",") + + if operator == "?" + # partial expansion of form style query variables sometimes requires a + # slight reordering of the variables to produce a valid url. + first_to_expand = vars.find { |varspec| + _, name, _ = *varspec.match(VARSPEC) + mapping.key?(name) && !mapping[name].nil? + } + + vars = [first_to_expand] + vars.reject {|varspec| varspec == first_to_expand} if first_to_expand + end + + vars. + inject("".dup) do |acc, varspec| + _, name, _ = *varspec.match(VARSPEC) + next_val = if mapping.key? name + transform_capture(mapping, "{#{operator}#{varspec}}", + processor, normalize_values) + else + "{#{operator}#{varspec}}" + end + # If we've already expanded at least one '?' operator with non-empty + # value, change to '&' + operator = "&" if (operator == "?") && (next_val != "") + acc << next_val + end + end + + ## + # Transforms a mapped value so that values can be substituted into the + # template. + # + # @param [Hash] mapping The mapping to replace captures + # @param [String] capture + # The expression to replace + # @param [#validate, #transform] processor + # An optional processor object may be supplied. + # @param [Boolean] normalize_values + # Optional flag to enable/disable unicode normalization. Default: true + # + # + # The object should respond to either the validate or + # transform messages or both. Both the validate and + # transform methods should take two parameters: name and + # value. The validate method should return true + # or false; true if the value of the variable is valid, + # false otherwise. An InvalidTemplateValueError exception + # will be raised if the value is invalid. The transform method + # should return the transformed variable value as a String. If a + # transform method is used, the value will not be percent encoded + # automatically. Unicode normalization will be performed both before and + # after sending the value to the transform method. + # + # @return [String] The expanded expression + def transform_capture(mapping, capture, processor=nil, + normalize_values=true) + _, operator, varlist = *capture.match(EXPRESSION) + return_value = varlist.split(',').inject([]) do |acc, varspec| + _, name, modifier = *varspec.match(VARSPEC) + value = mapping[name] + unless value == nil || value == {} + allow_reserved = %w(+ #).include?(operator) + # Common primitives where the .to_s output is well-defined + if Numeric === value || Symbol === value || + value == true || value == false + value = value.to_s + end + length = modifier.gsub(':', '').to_i if modifier =~ /^:\d+/ + + unless (Hash === value) || + value.respond_to?(:to_ary) || value.respond_to?(:to_str) + raise TypeError, + "Can't convert #{value.class} into String or Array." + end + + value = normalize_value(value) if normalize_values + + if processor == nil || !processor.respond_to?(:transform) + # Handle percent escaping + if allow_reserved + encode_map = + Addressable::URI::CharacterClasses::RESERVED + + Addressable::URI::CharacterClasses::UNRESERVED + else + encode_map = Addressable::URI::CharacterClasses::UNRESERVED + end + if value.kind_of?(Array) + transformed_value = value.map do |val| + if length + Addressable::URI.encode_component(val[0...length], encode_map) + else + Addressable::URI.encode_component(val, encode_map) + end + end + unless modifier == "*" + transformed_value = transformed_value.join(',') + end + elsif value.kind_of?(Hash) + transformed_value = value.map do |key, val| + if modifier == "*" + "#{ + Addressable::URI.encode_component( key, encode_map) + }=#{ + Addressable::URI.encode_component( val, encode_map) + }" + else + "#{ + Addressable::URI.encode_component( key, encode_map) + },#{ + Addressable::URI.encode_component( val, encode_map) + }" + end + end + unless modifier == "*" + transformed_value = transformed_value.join(',') + end + else + if length + transformed_value = Addressable::URI.encode_component( + value[0...length], encode_map) + else + transformed_value = Addressable::URI.encode_component( + value, encode_map) + end + end + end + + # Process, if we've got a processor + if processor != nil + if processor.respond_to?(:validate) + if !processor.validate(name, value) + display_value = value.kind_of?(Array) ? value.inspect : value + raise InvalidTemplateValueError, + "#{name}=#{display_value} is an invalid template value." + end + end + if processor.respond_to?(:transform) + transformed_value = processor.transform(name, value) + if normalize_values + transformed_value = normalize_value(transformed_value) + end + end + end + acc << [name, transformed_value] + end + acc + end + return "" if return_value.empty? + join_values(operator, return_value) + end + + ## + # Takes a set of values, and joins them together based on the + # operator. + # + # @param [String, Nil] operator One of the operators from the set + # (?,&,+,#,;,/,.), or nil if there wasn't one. + # @param [Array] return_value + # The set of return values (as [variable_name, value] tuples) that will + # be joined together. + # + # @return [String] The transformed mapped value + def join_values(operator, return_value) + leader = LEADERS.fetch(operator, '') + joiner = JOINERS.fetch(operator, ',') + case operator + when '&', '?' + leader + return_value.map{|k,v| + if v.is_a?(Array) && v.first =~ /=/ + v.join(joiner) + elsif v.is_a?(Array) + v.map{|inner_value| "#{k}=#{inner_value}"}.join(joiner) + else + "#{k}=#{v}" + end + }.join(joiner) + when ';' + return_value.map{|k,v| + if v.is_a?(Array) && v.first =~ /=/ + ';' + v.join(";") + elsif v.is_a?(Array) + ';' + v.map{|inner_value| "#{k}=#{inner_value}"}.join(";") + else + v && v != '' ? ";#{k}=#{v}" : ";#{k}" + end + }.join + else + leader + return_value.map{|k,v| v}.join(joiner) + end + end + + ## + # Takes a set of values, and joins them together based on the + # operator. + # + # @param [Hash, Array, String] value + # Normalizes keys and values with IDNA#unicode_normalize_kc + # + # @return [Hash, Array, String] The normalized values + def normalize_value(value) + unless value.is_a?(Hash) + value = value.respond_to?(:to_ary) ? value.to_ary : value.to_str + end + + # Handle unicode normalization + if value.kind_of?(Array) + value.map! { |val| Addressable::IDNA.unicode_normalize_kc(val) } + elsif value.kind_of?(Hash) + value = value.inject({}) { |acc, (k, v)| + acc[Addressable::IDNA.unicode_normalize_kc(k)] = + Addressable::IDNA.unicode_normalize_kc(v) + acc + } + else + value = Addressable::IDNA.unicode_normalize_kc(value) + end + value + end + + ## + # Generates a hash with string keys + # + # @param [Hash] mapping A mapping hash to normalize + # + # @return [Hash] + # A hash with stringified keys + def normalize_keys(mapping) + return mapping.inject({}) do |accu, pair| + name, value = pair + if Symbol === name + name = name.to_s + elsif name.respond_to?(:to_str) + name = name.to_str + else + raise TypeError, + "Can't convert #{name.class} into String." + end + accu[name] = value + accu + end + end + + ## + # Generates the Regexp that parses a template pattern. Memoizes the + # value if template processor not set (processors may not be deterministic) + # + # @param [String] pattern The URI template pattern. + # @param [#match] processor The template processor to use. + # + # @return [Array, Regexp] + # An array of expansion variables nad a regular expression which may be + # used to parse a template pattern + def parse_template_pattern(pattern, processor = nil) + if processor.nil? && pattern == @pattern + @cached_template_parse ||= + parse_new_template_pattern(pattern, processor) + else + parse_new_template_pattern(pattern, processor) + end + end + + ## + # Generates the Regexp that parses a template pattern. + # + # @param [String] pattern The URI template pattern. + # @param [#match] processor The template processor to use. + # + # @return [Array, Regexp] + # An array of expansion variables nad a regular expression which may be + # used to parse a template pattern + def parse_new_template_pattern(pattern, processor = nil) + # Escape the pattern. The two gsubs restore the escaped curly braces + # back to their original form. Basically, escape everything that isn't + # within an expansion. + escaped_pattern = Regexp.escape( + pattern + ).gsub(/\\\{(.*?)\\\}/) do |escaped| + escaped.gsub(/\\(.)/, "\\1") + end + + expansions = [] + + # Create a regular expression that captures the values of the + # variables in the URI. + regexp_string = escaped_pattern.gsub( EXPRESSION ) do |expansion| + + expansions << expansion + _, operator, varlist = *expansion.match(EXPRESSION) + leader = Regexp.escape(LEADERS.fetch(operator, '')) + joiner = Regexp.escape(JOINERS.fetch(operator, ',')) + combined = varlist.split(',').map do |varspec| + _, name, modifier = *varspec.match(VARSPEC) + + result = processor && processor.respond_to?(:match) ? processor.match(name) : nil + if result + "(?<#{name}>#{ result })" + else + group = case operator + when '+' + "#{ RESERVED }*?" + when '#' + "#{ RESERVED }*?" + when '/' + "#{ UNRESERVED }*?" + when '.' + "#{ UNRESERVED.gsub('\.', '') }*?" + when ';' + "#{ UNRESERVED }*=?#{ UNRESERVED }*?" + when '?' + "#{ UNRESERVED }*=#{ UNRESERVED }*?" + when '&' + "#{ UNRESERVED }*=#{ UNRESERVED }*?" + else + "#{ UNRESERVED }*?" + end + if modifier == '*' + "(?<#{name}>#{group}(?:#{joiner}?#{group})*)?" + else + "(?<#{name}>#{group})?" + end + end + end.join("#{joiner}?") + "(?:|#{leader}#{combined})" + end + + # Ensure that the regular expression matches the whole URI. + regexp_string = "\\A#{regexp_string}\\z" + return expansions, Regexp.new(regexp_string) + end + + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable/uri.rb b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable/uri.rb new file mode 100644 index 0000000..14b9253 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable/uri.rb @@ -0,0 +1,2560 @@ +# frozen_string_literal: true + +#-- +# Copyright (C) Bob Aman +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#++ + + +require "addressable/version" +require "addressable/idna" +require "public_suffix" + +## +# Addressable is a library for processing links and URIs. +module Addressable + ## + # This is an implementation of a URI parser based on + # RFC 3986, + # RFC 3987. + class URI + ## + # Raised if something other than a uri is supplied. + class InvalidURIError < StandardError + end + + ## + # Container for the character classes specified in + # RFC 3986. + # + # Note: Concatenated and interpolated `String`s are not affected by the + # `frozen_string_literal` directive and must be frozen explicitly. + # + # Interpolated `String`s *were* frozen this way before Ruby 3.0: + # https://bugs.ruby-lang.org/issues/17104 + module CharacterClasses + ALPHA = "a-zA-Z" + DIGIT = "0-9" + GEN_DELIMS = "\\:\\/\\?\\#\\[\\]\\@" + SUB_DELIMS = "\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\=" + RESERVED = (GEN_DELIMS + SUB_DELIMS).freeze + UNRESERVED = (ALPHA + DIGIT + "\\-\\.\\_\\~").freeze + PCHAR = (UNRESERVED + SUB_DELIMS + "\\:\\@").freeze + SCHEME = (ALPHA + DIGIT + "\\-\\+\\.").freeze + HOST = (UNRESERVED + SUB_DELIMS + "\\[\\:\\]").freeze + AUTHORITY = (PCHAR + "\\[\\:\\]").freeze + PATH = (PCHAR + "\\/").freeze + QUERY = (PCHAR + "\\/\\?").freeze + FRAGMENT = (PCHAR + "\\/\\?").freeze + end + + module NormalizeCharacterClasses + HOST = /[^#{CharacterClasses::HOST}]/ + UNRESERVED = /[^#{CharacterClasses::UNRESERVED}]/ + PCHAR = /[^#{CharacterClasses::PCHAR}]/ + SCHEME = /[^#{CharacterClasses::SCHEME}]/ + FRAGMENT = /[^#{CharacterClasses::FRAGMENT}]/ + QUERY = %r{[^a-zA-Z0-9\-\.\_\~\!\$\'\(\)\*\+\,\=\:\@\/\?%]|%(?!2B|2b)} + end + + SLASH = '/' + EMPTY_STR = '' + + URIREGEX = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/ + + PORT_MAPPING = { + "http" => 80, + "https" => 443, + "ftp" => 21, + "tftp" => 69, + "sftp" => 22, + "ssh" => 22, + "svn+ssh" => 22, + "telnet" => 23, + "nntp" => 119, + "gopher" => 70, + "wais" => 210, + "ldap" => 389, + "prospero" => 1525 + }.freeze + + ## + # Returns a URI object based on the parsed string. + # + # @param [String, Addressable::URI, #to_str] uri + # The URI string to parse. + # No parsing is performed if the object is already an + # Addressable::URI. + # + # @return [Addressable::URI] The parsed URI. + def self.parse(uri) + # If we were given nil, return nil. + return nil unless uri + # If a URI object is passed, just return itself. + return uri.dup if uri.kind_of?(self) + + # If a URI object of the Ruby standard library variety is passed, + # convert it to a string, then parse the string. + # We do the check this way because we don't want to accidentally + # cause a missing constant exception to be thrown. + if uri.class.name =~ /^URI\b/ + uri = uri.to_s + end + + # Otherwise, convert to a String + begin + uri = uri.to_str + rescue TypeError, NoMethodError + raise TypeError, "Can't convert #{uri.class} into String." + end if not uri.is_a? String + + # This Regexp supplied as an example in RFC 3986, and it works great. + scan = uri.scan(URIREGEX) + fragments = scan[0] + scheme = fragments[1] + authority = fragments[3] + path = fragments[4] + query = fragments[6] + fragment = fragments[8] + user = nil + password = nil + host = nil + port = nil + if authority != nil + # The Regexp above doesn't split apart the authority. + userinfo = authority[/^([^\[\]]*)@/, 1] + if userinfo != nil + user = userinfo.strip[/^([^:]*):?/, 1] + password = userinfo.strip[/:(.*)$/, 1] + end + host = authority.sub( + /^([^\[\]]*)@/, EMPTY_STR + ).sub( + /:([^:@\[\]]*?)$/, EMPTY_STR + ) + port = authority[/:([^:@\[\]]*?)$/, 1] + end + if port == EMPTY_STR + port = nil + end + + return new( + :scheme => scheme, + :user => user, + :password => password, + :host => host, + :port => port, + :path => path, + :query => query, + :fragment => fragment + ) + end + + ## + # Converts an input to a URI. The input does not have to be a valid + # URI — the method will use heuristics to guess what URI was intended. + # This is not standards-compliant, merely user-friendly. + # + # @param [String, Addressable::URI, #to_str] uri + # The URI string to parse. + # No parsing is performed if the object is already an + # Addressable::URI. + # @param [Hash] hints + # A Hash of hints to the heuristic parser. + # Defaults to {:scheme => "http"}. + # + # @return [Addressable::URI] The parsed URI. + def self.heuristic_parse(uri, hints={}) + # If we were given nil, return nil. + return nil unless uri + # If a URI object is passed, just return itself. + return uri.dup if uri.kind_of?(self) + + # If a URI object of the Ruby standard library variety is passed, + # convert it to a string, then parse the string. + # We do the check this way because we don't want to accidentally + # cause a missing constant exception to be thrown. + if uri.class.name =~ /^URI\b/ + uri = uri.to_s + end + + if !uri.respond_to?(:to_str) + raise TypeError, "Can't convert #{uri.class} into String." + end + # Otherwise, convert to a String + uri = uri.to_str.dup.strip + hints = { + :scheme => "http" + }.merge(hints) + case uri + when /^http:\//i + uri.sub!(/^http:\/+/i, "http://") + when /^https:\//i + uri.sub!(/^https:\/+/i, "https://") + when /^feed:\/+http:\//i + uri.sub!(/^feed:\/+http:\/+/i, "feed:http://") + when /^feed:\//i + uri.sub!(/^feed:\/+/i, "feed://") + when %r[^file:/{4}]i + uri.sub!(%r[^file:/+]i, "file:////") + when %r[^file://localhost/]i + uri.sub!(%r[^file://localhost/+]i, "file:///") + when %r[^file:/+]i + uri.sub!(%r[^file:/+]i, "file:///") + when /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ + uri.sub!(/^/, hints[:scheme] + "://") + when /\A\d+\..*:\d+\z/ + uri = "#{hints[:scheme]}://#{uri}" + end + match = uri.match(URIREGEX) + fragments = match.captures + authority = fragments[3] + if authority && authority.length > 0 + new_authority = authority.tr("\\", "/").gsub(" ", "%20") + # NOTE: We want offset 4, not 3! + offset = match.offset(4) + uri = uri.dup + uri[offset[0]...offset[1]] = new_authority + end + parsed = self.parse(uri) + if parsed.scheme =~ /^[^\/?#\.]+\.[^\/?#]+$/ + parsed = self.parse(hints[:scheme] + "://" + uri) + end + if parsed.path.include?(".") + if parsed.path[/\b@\b/] + parsed.scheme = "mailto" unless parsed.scheme + elsif new_host = parsed.path[/^([^\/]+\.[^\/]*)/, 1] + parsed.defer_validation do + new_path = parsed.path.sub( + Regexp.new("^" + Regexp.escape(new_host)), EMPTY_STR) + parsed.host = new_host + parsed.path = new_path + parsed.scheme = hints[:scheme] unless parsed.scheme + end + end + end + return parsed + end + + ## + # Converts a path to a file scheme URI. If the path supplied is + # relative, it will be returned as a relative URI. If the path supplied + # is actually a non-file URI, it will parse the URI as if it had been + # parsed with Addressable::URI.parse. Handles all of the + # various Microsoft-specific formats for specifying paths. + # + # @param [String, Addressable::URI, #to_str] path + # Typically a String path to a file or directory, but + # will return a sensible return value if an absolute URI is supplied + # instead. + # + # @return [Addressable::URI] + # The parsed file scheme URI or the original URI if some other URI + # scheme was provided. + # + # @example + # base = Addressable::URI.convert_path("/absolute/path/") + # uri = Addressable::URI.convert_path("relative/path") + # (base + uri).to_s + # #=> "file:///absolute/path/relative/path" + # + # Addressable::URI.convert_path( + # "c:\\windows\\My Documents 100%20\\foo.txt" + # ).to_s + # #=> "file:///c:/windows/My%20Documents%20100%20/foo.txt" + # + # Addressable::URI.convert_path("http://example.com/").to_s + # #=> "http://example.com/" + def self.convert_path(path) + # If we were given nil, return nil. + return nil unless path + # If a URI object is passed, just return itself. + return path if path.kind_of?(self) + if !path.respond_to?(:to_str) + raise TypeError, "Can't convert #{path.class} into String." + end + # Otherwise, convert to a String + path = path.to_str.strip + + path.sub!(/^file:\/?\/?/, EMPTY_STR) if path =~ /^file:\/?\/?/ + path = SLASH + path if path =~ /^([a-zA-Z])[\|:]/ + uri = self.parse(path) + + if uri.scheme == nil + # Adjust windows-style uris + uri.path.sub!(/^\/?([a-zA-Z])[\|:][\\\/]/) do + "/#{$1.downcase}:/" + end + uri.path.tr!("\\", SLASH) + if File.exist?(uri.path) && + File.stat(uri.path).directory? + uri.path.chomp!(SLASH) + uri.path = uri.path + '/' + end + + # If the path is absolute, set the scheme and host. + if uri.path.start_with?(SLASH) + uri.scheme = "file" + uri.host = EMPTY_STR + end + uri.normalize! + end + + return uri + end + + ## + # Joins several URIs together. + # + # @param [String, Addressable::URI, #to_str] *uris + # The URIs to join. + # + # @return [Addressable::URI] The joined URI. + # + # @example + # base = "http://example.com/" + # uri = Addressable::URI.parse("relative/path") + # Addressable::URI.join(base, uri) + # #=> # + def self.join(*uris) + uri_objects = uris.collect do |uri| + if !uri.respond_to?(:to_str) + raise TypeError, "Can't convert #{uri.class} into String." + end + uri.kind_of?(self) ? uri : self.parse(uri.to_str) + end + result = uri_objects.shift.dup + for uri in uri_objects + result.join!(uri) + end + return result + end + + ## + # Tables used to optimize encoding operations in `self.encode_component` + # and `self.normalize_component` + SEQUENCE_ENCODING_TABLE = Hash.new do |hash, sequence| + hash[sequence] = sequence.unpack("C*").map do |c| + format("%02x", c) + end.join + end + + SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE = Hash.new do |hash, sequence| + hash[sequence] = sequence.unpack("C*").map do |c| + format("%%%02X", c) + end.join + end + + ## + # Percent encodes a URI component. + # + # @param [String, #to_str] component The URI component to encode. + # + # @param [String, Regexp] character_class + # The characters which are not percent encoded. If a String + # is passed, the String must be formatted as a regular + # expression character class. (Do not include the surrounding square + # brackets.) For example, "b-zB-Z0-9" would cause + # everything but the letters 'b' through 'z' and the numbers '0' through + # '9' to be percent encoded. If a Regexp is passed, the + # value /[^b-zB-Z0-9]/ would have the same effect. A set of + # useful String values may be found in the + # Addressable::URI::CharacterClasses module. The default + # value is the reserved plus unreserved character classes specified in + # RFC 3986. + # + # @param [Regexp] upcase_encoded + # A string of characters that may already be percent encoded, and whose + # encodings should be upcased. This allows normalization of percent + # encodings for characters not included in the + # character_class. + # + # @return [String] The encoded component. + # + # @example + # Addressable::URI.encode_component("simple/example", "b-zB-Z0-9") + # => "simple%2Fex%61mple" + # Addressable::URI.encode_component("simple/example", /[^b-zB-Z0-9]/) + # => "simple%2Fex%61mple" + # Addressable::URI.encode_component( + # "simple/example", Addressable::URI::CharacterClasses::UNRESERVED + # ) + # => "simple%2Fexample" + def self.encode_component(component, character_class= + CharacterClasses::RESERVED + CharacterClasses::UNRESERVED, + upcase_encoded='') + return nil if component.nil? + + begin + if component.kind_of?(Symbol) || + component.kind_of?(Numeric) || + component.kind_of?(TrueClass) || + component.kind_of?(FalseClass) + component = component.to_s + else + component = component.to_str + end + rescue TypeError, NoMethodError + raise TypeError, "Can't convert #{component.class} into String." + end if !component.is_a? String + + if ![String, Regexp].include?(character_class.class) + raise TypeError, + "Expected String or Regexp, got #{character_class.inspect}" + end + if character_class.kind_of?(String) + character_class = /[^#{character_class}]/ + end + # We can't perform regexps on invalid UTF sequences, but + # here we need to, so switch to ASCII. + component = component.dup + component.force_encoding(Encoding::ASCII_8BIT) + # Avoiding gsub! because there are edge cases with frozen strings + component = component.gsub(character_class) do |sequence| + SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE[sequence] + end + if upcase_encoded.length > 0 + upcase_encoded_chars = upcase_encoded.chars.map do |char| + SEQUENCE_ENCODING_TABLE[char] + end + component = component.gsub(/%(#{upcase_encoded_chars.join('|')})/, + &:upcase) + end + return component + end + + class << self + alias_method :escape_component, :encode_component + end + + ## + # Unencodes any percent encoded characters within a URI component. + # This method may be used for unencoding either components or full URIs, + # however, it is recommended to use the unencode_component + # alias when unencoding components. + # + # @param [String, Addressable::URI, #to_str] uri + # The URI or component to unencode. + # + # @param [Class] return_type + # The type of object to return. + # This value may only be set to String or + # Addressable::URI. All other values are invalid. Defaults + # to String. + # + # @param [String] leave_encoded + # A string of characters to leave encoded. If a percent encoded character + # in this list is encountered then it will remain percent encoded. + # + # @return [String, Addressable::URI] + # The unencoded component or URI. + # The return type is determined by the return_type + # parameter. + def self.unencode(uri, return_type=String, leave_encoded='') + return nil if uri.nil? + + begin + uri = uri.to_str + rescue NoMethodError, TypeError + raise TypeError, "Can't convert #{uri.class} into String." + end if !uri.is_a? String + if ![String, ::Addressable::URI].include?(return_type) + raise TypeError, + "Expected Class (String or Addressable::URI), " + + "got #{return_type.inspect}" + end + + result = uri.gsub(/%[0-9a-f]{2}/i) do |sequence| + c = sequence[1..3].to_i(16).chr + c.force_encoding(sequence.encoding) + leave_encoded.include?(c) ? sequence : c + end + + result.force_encoding("utf-8") + if return_type == String + return result + elsif return_type == ::Addressable::URI + return ::Addressable::URI.parse(result) + end + end + + class << self + alias_method :unescape, :unencode + alias_method :unencode_component, :unencode + alias_method :unescape_component, :unencode + end + + + ## + # Normalizes the encoding of a URI component. + # + # @param [String, #to_str] component The URI component to encode. + # + # @param [String, Regexp] character_class + # The characters which are not percent encoded. If a String + # is passed, the String must be formatted as a regular + # expression character class. (Do not include the surrounding square + # brackets.) For example, "b-zB-Z0-9" would cause + # everything but the letters 'b' through 'z' and the numbers '0' + # through '9' to be percent encoded. If a Regexp is passed, + # the value /[^b-zB-Z0-9]/ would have the same effect. A + # set of useful String values may be found in the + # Addressable::URI::CharacterClasses module. The default + # value is the reserved plus unreserved character classes specified in + # RFC 3986. + # + # @param [String] leave_encoded + # When character_class is a String then + # leave_encoded is a string of characters that should remain + # percent encoded while normalizing the component; if they appear percent + # encoded in the original component, then they will be upcased ("%2f" + # normalized to "%2F") but otherwise left alone. + # + # @return [String] The normalized component. + # + # @example + # Addressable::URI.normalize_component("simpl%65/%65xampl%65", "b-zB-Z") + # => "simple%2Fex%61mple" + # Addressable::URI.normalize_component( + # "simpl%65/%65xampl%65", /[^b-zB-Z]/ + # ) + # => "simple%2Fex%61mple" + # Addressable::URI.normalize_component( + # "simpl%65/%65xampl%65", + # Addressable::URI::CharacterClasses::UNRESERVED + # ) + # => "simple%2Fexample" + # Addressable::URI.normalize_component( + # "one%20two%2fthree%26four", + # "0-9a-zA-Z &/", + # "/" + # ) + # => "one two%2Fthree&four" + def self.normalize_component(component, character_class= + CharacterClasses::RESERVED + CharacterClasses::UNRESERVED, + leave_encoded='') + return nil if component.nil? + + begin + component = component.to_str + rescue NoMethodError, TypeError + raise TypeError, "Can't convert #{component.class} into String." + end if !component.is_a? String + + if ![String, Regexp].include?(character_class.class) + raise TypeError, + "Expected String or Regexp, got #{character_class.inspect}" + end + if character_class.kind_of?(String) + leave_re = if leave_encoded.length > 0 + character_class = "#{character_class}%" unless character_class.include?('%') + + "|%(?!#{leave_encoded.chars.flat_map do |char| + seq = SEQUENCE_ENCODING_TABLE[char] + [seq.upcase, seq.downcase] + end.join('|')})" + end + + character_class = if leave_re + /[^#{character_class}]#{leave_re}/ + else + /[^#{character_class}]/ + end + end + # We can't perform regexps on invalid UTF sequences, but + # here we need to, so switch to ASCII. + component = component.dup + component.force_encoding(Encoding::ASCII_8BIT) + unencoded = self.unencode_component(component, String, leave_encoded) + begin + encoded = self.encode_component( + Addressable::IDNA.unicode_normalize_kc(unencoded), + character_class, + leave_encoded + ) + rescue ArgumentError + encoded = self.encode_component(unencoded) + end + encoded.force_encoding(Encoding::UTF_8) + return encoded + end + + ## + # Percent encodes any special characters in the URI. + # + # @param [String, Addressable::URI, #to_str] uri + # The URI to encode. + # + # @param [Class] return_type + # The type of object to return. + # This value may only be set to String or + # Addressable::URI. All other values are invalid. Defaults + # to String. + # + # @return [String, Addressable::URI] + # The encoded URI. + # The return type is determined by the return_type + # parameter. + def self.encode(uri, return_type=String) + return nil if uri.nil? + + begin + uri = uri.to_str + rescue NoMethodError, TypeError + raise TypeError, "Can't convert #{uri.class} into String." + end if !uri.is_a? String + + if ![String, ::Addressable::URI].include?(return_type) + raise TypeError, + "Expected Class (String or Addressable::URI), " + + "got #{return_type.inspect}" + end + uri_object = uri.kind_of?(self) ? uri : self.parse(uri) + encoded_uri = Addressable::URI.new( + :scheme => self.encode_component(uri_object.scheme, + Addressable::URI::CharacterClasses::SCHEME), + :authority => self.encode_component(uri_object.authority, + Addressable::URI::CharacterClasses::AUTHORITY), + :path => self.encode_component(uri_object.path, + Addressable::URI::CharacterClasses::PATH), + :query => self.encode_component(uri_object.query, + Addressable::URI::CharacterClasses::QUERY), + :fragment => self.encode_component(uri_object.fragment, + Addressable::URI::CharacterClasses::FRAGMENT) + ) + if return_type == String + return encoded_uri.to_s + elsif return_type == ::Addressable::URI + return encoded_uri + end + end + + class << self + alias_method :escape, :encode + end + + ## + # Normalizes the encoding of a URI. Characters within a hostname are + # not percent encoded to allow for internationalized domain names. + # + # @param [String, Addressable::URI, #to_str] uri + # The URI to encode. + # + # @param [Class] return_type + # The type of object to return. + # This value may only be set to String or + # Addressable::URI. All other values are invalid. Defaults + # to String. + # + # @return [String, Addressable::URI] + # The encoded URI. + # The return type is determined by the return_type + # parameter. + def self.normalized_encode(uri, return_type=String) + begin + uri = uri.to_str + rescue NoMethodError, TypeError + raise TypeError, "Can't convert #{uri.class} into String." + end if !uri.is_a? String + + if ![String, ::Addressable::URI].include?(return_type) + raise TypeError, + "Expected Class (String or Addressable::URI), " + + "got #{return_type.inspect}" + end + uri_object = uri.kind_of?(self) ? uri : self.parse(uri) + components = { + :scheme => self.unencode_component(uri_object.scheme), + :user => self.unencode_component(uri_object.user), + :password => self.unencode_component(uri_object.password), + :host => self.unencode_component(uri_object.host), + :port => (uri_object.port.nil? ? nil : uri_object.port.to_s), + :path => self.unencode_component(uri_object.path), + :query => self.unencode_component(uri_object.query), + :fragment => self.unencode_component(uri_object.fragment) + } + components.each do |key, value| + if value != nil + begin + components[key] = + Addressable::IDNA.unicode_normalize_kc(value.to_str) + rescue ArgumentError + # Likely a malformed UTF-8 character, skip unicode normalization + components[key] = value.to_str + end + end + end + encoded_uri = Addressable::URI.new( + :scheme => self.encode_component(components[:scheme], + Addressable::URI::CharacterClasses::SCHEME), + :user => self.encode_component(components[:user], + Addressable::URI::CharacterClasses::UNRESERVED), + :password => self.encode_component(components[:password], + Addressable::URI::CharacterClasses::UNRESERVED), + :host => components[:host], + :port => components[:port], + :path => self.encode_component(components[:path], + Addressable::URI::CharacterClasses::PATH), + :query => self.encode_component(components[:query], + Addressable::URI::CharacterClasses::QUERY), + :fragment => self.encode_component(components[:fragment], + Addressable::URI::CharacterClasses::FRAGMENT) + ) + if return_type == String + return encoded_uri.to_s + elsif return_type == ::Addressable::URI + return encoded_uri + end + end + + ## + # Encodes a set of key/value pairs according to the rules for the + # application/x-www-form-urlencoded MIME type. + # + # @param [#to_hash, #to_ary] form_values + # The form values to encode. + # + # @param [TrueClass, FalseClass] sort + # Sort the key/value pairs prior to encoding. + # Defaults to false. + # + # @return [String] + # The encoded value. + def self.form_encode(form_values, sort=false) + if form_values.respond_to?(:to_hash) + form_values = form_values.to_hash.to_a + elsif form_values.respond_to?(:to_ary) + form_values = form_values.to_ary + else + raise TypeError, "Can't convert #{form_values.class} into Array." + end + + form_values = form_values.inject([]) do |accu, (key, value)| + if value.kind_of?(Array) + value.each do |v| + accu << [key.to_s, v.to_s] + end + else + accu << [key.to_s, value.to_s] + end + accu + end + + if sort + # Useful for OAuth and optimizing caching systems + form_values = form_values.sort + end + escaped_form_values = form_values.map do |(key, value)| + # Line breaks are CRLF pairs + [ + self.encode_component( + key.gsub(/(\r\n|\n|\r)/, "\r\n"), + CharacterClasses::UNRESERVED + ).gsub("%20", "+"), + self.encode_component( + value.gsub(/(\r\n|\n|\r)/, "\r\n"), + CharacterClasses::UNRESERVED + ).gsub("%20", "+") + ] + end + return escaped_form_values.map do |(key, value)| + "#{key}=#{value}" + end.join("&") + end + + ## + # Decodes a String according to the rules for the + # application/x-www-form-urlencoded MIME type. + # + # @param [String, #to_str] encoded_value + # The form values to decode. + # + # @return [Array] + # The decoded values. + # This is not a Hash because of the possibility for + # duplicate keys. + def self.form_unencode(encoded_value) + if !encoded_value.respond_to?(:to_str) + raise TypeError, "Can't convert #{encoded_value.class} into String." + end + encoded_value = encoded_value.to_str + split_values = encoded_value.split("&").map do |pair| + pair.split("=", 2) + end + return split_values.map do |(key, value)| + [ + key ? self.unencode_component( + key.gsub("+", "%20")).gsub(/(\r\n|\n|\r)/, "\n") : nil, + value ? (self.unencode_component( + value.gsub("+", "%20")).gsub(/(\r\n|\n|\r)/, "\n")) : nil + ] + end + end + + ## + # Creates a new uri object from component parts. + # + # @option [String, #to_str] scheme The scheme component. + # @option [String, #to_str] user The user component. + # @option [String, #to_str] password The password component. + # @option [String, #to_str] userinfo + # The userinfo component. If this is supplied, the user and password + # components must be omitted. + # @option [String, #to_str] host The host component. + # @option [String, #to_str] port The port component. + # @option [String, #to_str] authority + # The authority component. If this is supplied, the user, password, + # userinfo, host, and port components must be omitted. + # @option [String, #to_str] path The path component. + # @option [String, #to_str] query The query component. + # @option [String, #to_str] fragment The fragment component. + # + # @return [Addressable::URI] The constructed URI object. + def initialize(options={}) + if options.has_key?(:authority) + if (options.keys & [:userinfo, :user, :password, :host, :port]).any? + raise ArgumentError, + "Cannot specify both an authority and any of the components " + + "within the authority." + end + end + if options.has_key?(:userinfo) + if (options.keys & [:user, :password]).any? + raise ArgumentError, + "Cannot specify both a userinfo and either the user or password." + end + end + + self.defer_validation do + # Bunch of crazy logic required because of the composite components + # like userinfo and authority. + self.scheme = options[:scheme] if options[:scheme] + self.user = options[:user] if options[:user] + self.password = options[:password] if options[:password] + self.userinfo = options[:userinfo] if options[:userinfo] + self.host = options[:host] if options[:host] + self.port = options[:port] if options[:port] + self.authority = options[:authority] if options[:authority] + self.path = options[:path] if options[:path] + self.query = options[:query] if options[:query] + self.query_values = options[:query_values] if options[:query_values] + self.fragment = options[:fragment] if options[:fragment] + end + self.to_s + end + + ## + # Freeze URI, initializing instance variables. + # + # @return [Addressable::URI] The frozen URI object. + def freeze + self.normalized_scheme + self.normalized_user + self.normalized_password + self.normalized_userinfo + self.normalized_host + self.normalized_port + self.normalized_authority + self.normalized_site + self.normalized_path + self.normalized_query + self.normalized_fragment + self.hash + super + end + + ## + # The scheme component for this URI. + # + # @return [String] The scheme component. + def scheme + return defined?(@scheme) ? @scheme : nil + end + + ## + # The scheme component for this URI, normalized. + # + # @return [String] The scheme component, normalized. + def normalized_scheme + return nil unless self.scheme + @normalized_scheme ||= begin + if self.scheme =~ /^\s*ssh\+svn\s*$/i + "svn+ssh".dup + else + Addressable::URI.normalize_component( + self.scheme.strip.downcase, + Addressable::URI::NormalizeCharacterClasses::SCHEME + ) + end + end + # All normalized values should be UTF-8 + force_utf8_encoding_if_needed(@normalized_scheme) + @normalized_scheme + end + + ## + # Sets the scheme component for this URI. + # + # @param [String, #to_str] new_scheme The new scheme component. + def scheme=(new_scheme) + if new_scheme && !new_scheme.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_scheme.class} into String." + elsif new_scheme + new_scheme = new_scheme.to_str + end + if new_scheme && new_scheme !~ /\A[a-z][a-z0-9\.\+\-]*\z/i + raise InvalidURIError, "Invalid scheme format: '#{new_scheme}'" + end + @scheme = new_scheme + @scheme = nil if @scheme.to_s.strip.empty? + + # Reset dependent values + remove_instance_variable(:@normalized_scheme) if defined?(@normalized_scheme) + remove_composite_values + + # Ensure we haven't created an invalid URI + validate() + end + + ## + # The user component for this URI. + # + # @return [String] The user component. + def user + return defined?(@user) ? @user : nil + end + + ## + # The user component for this URI, normalized. + # + # @return [String] The user component, normalized. + def normalized_user + return nil unless self.user + return @normalized_user if defined?(@normalized_user) + @normalized_user ||= begin + if normalized_scheme =~ /https?/ && self.user.strip.empty? && + (!self.password || self.password.strip.empty?) + nil + else + Addressable::URI.normalize_component( + self.user.strip, + Addressable::URI::NormalizeCharacterClasses::UNRESERVED + ) + end + end + # All normalized values should be UTF-8 + force_utf8_encoding_if_needed(@normalized_user) + @normalized_user + end + + ## + # Sets the user component for this URI. + # + # @param [String, #to_str] new_user The new user component. + def user=(new_user) + if new_user && !new_user.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_user.class} into String." + end + @user = new_user ? new_user.to_str : nil + + # You can't have a nil user with a non-nil password + if password != nil + @user = EMPTY_STR if @user.nil? + end + + # Reset dependent values + remove_instance_variable(:@userinfo) if defined?(@userinfo) + remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo) + remove_instance_variable(:@authority) if defined?(@authority) + remove_instance_variable(:@normalized_user) if defined?(@normalized_user) + remove_composite_values + + # Ensure we haven't created an invalid URI + validate() + end + + ## + # The password component for this URI. + # + # @return [String] The password component. + def password + return defined?(@password) ? @password : nil + end + + ## + # The password component for this URI, normalized. + # + # @return [String] The password component, normalized. + def normalized_password + return nil unless self.password + return @normalized_password if defined?(@normalized_password) + @normalized_password ||= begin + if self.normalized_scheme =~ /https?/ && self.password.strip.empty? && + (!self.user || self.user.strip.empty?) + nil + else + Addressable::URI.normalize_component( + self.password.strip, + Addressable::URI::NormalizeCharacterClasses::UNRESERVED + ) + end + end + # All normalized values should be UTF-8 + force_utf8_encoding_if_needed(@normalized_password) + @normalized_password + end + + ## + # Sets the password component for this URI. + # + # @param [String, #to_str] new_password The new password component. + def password=(new_password) + if new_password && !new_password.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_password.class} into String." + end + @password = new_password ? new_password.to_str : nil + + # You can't have a nil user with a non-nil password + @password ||= nil + @user ||= nil + if @password != nil + @user = EMPTY_STR if @user.nil? + end + + # Reset dependent values + remove_instance_variable(:@userinfo) if defined?(@userinfo) + remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo) + remove_instance_variable(:@authority) if defined?(@authority) + remove_instance_variable(:@normalized_password) if defined?(@normalized_password) + remove_composite_values + + # Ensure we haven't created an invalid URI + validate() + end + + ## + # The userinfo component for this URI. + # Combines the user and password components. + # + # @return [String] The userinfo component. + def userinfo + current_user = self.user + current_password = self.password + (current_user || current_password) && @userinfo ||= begin + if current_user && current_password + "#{current_user}:#{current_password}" + elsif current_user && !current_password + "#{current_user}" + end + end + end + + ## + # The userinfo component for this URI, normalized. + # + # @return [String] The userinfo component, normalized. + def normalized_userinfo + return nil unless self.userinfo + return @normalized_userinfo if defined?(@normalized_userinfo) + @normalized_userinfo ||= begin + current_user = self.normalized_user + current_password = self.normalized_password + if !current_user && !current_password + nil + elsif current_user && current_password + "#{current_user}:#{current_password}".dup + elsif current_user && !current_password + "#{current_user}".dup + end + end + # All normalized values should be UTF-8 + force_utf8_encoding_if_needed(@normalized_userinfo) + @normalized_userinfo + end + + ## + # Sets the userinfo component for this URI. + # + # @param [String, #to_str] new_userinfo The new userinfo component. + def userinfo=(new_userinfo) + if new_userinfo && !new_userinfo.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_userinfo.class} into String." + end + new_user, new_password = if new_userinfo + [ + new_userinfo.to_str.strip[/^(.*):/, 1], + new_userinfo.to_str.strip[/:(.*)$/, 1] + ] + else + [nil, nil] + end + + # Password assigned first to ensure validity in case of nil + self.password = new_password + self.user = new_user + + # Reset dependent values + remove_instance_variable(:@authority) if defined?(@authority) + remove_composite_values + + # Ensure we haven't created an invalid URI + validate() + end + + ## + # The host component for this URI. + # + # @return [String] The host component. + def host + return defined?(@host) ? @host : nil + end + + ## + # The host component for this URI, normalized. + # + # @return [String] The host component, normalized. + def normalized_host + return nil unless self.host + + @normalized_host ||= begin + if !self.host.strip.empty? + result = ::Addressable::IDNA.to_ascii( + URI.unencode_component(self.host.strip.downcase) + ) + if result =~ /[^\.]\.$/ + # Single trailing dots are unnecessary. + result = result[0...-1] + end + result = Addressable::URI.normalize_component( + result, + NormalizeCharacterClasses::HOST + ) + result + else + EMPTY_STR.dup + end + end + # All normalized values should be UTF-8 + force_utf8_encoding_if_needed(@normalized_host) + @normalized_host + end + + ## + # Sets the host component for this URI. + # + # @param [String, #to_str] new_host The new host component. + def host=(new_host) + if new_host && !new_host.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_host.class} into String." + end + @host = new_host ? new_host.to_str : nil + + # Reset dependent values + remove_instance_variable(:@authority) if defined?(@authority) + remove_instance_variable(:@normalized_host) if defined?(@normalized_host) + remove_composite_values + + # Ensure we haven't created an invalid URI + validate() + end + + ## + # This method is same as URI::Generic#host except + # brackets for IPv6 (and 'IPvFuture') addresses are removed. + # + # @see Addressable::URI#host + # + # @return [String] The hostname for this URI. + def hostname + v = self.host + /\A\[(.*)\]\z/ =~ v ? $1 : v + end + + ## + # This method is same as URI::Generic#host= except + # the argument can be a bare IPv6 address (or 'IPvFuture'). + # + # @see Addressable::URI#host= + # + # @param [String, #to_str] new_hostname The new hostname for this URI. + def hostname=(new_hostname) + if new_hostname && + (new_hostname.respond_to?(:ipv4?) || new_hostname.respond_to?(:ipv6?)) + new_hostname = new_hostname.to_s + elsif new_hostname && !new_hostname.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_hostname.class} into String." + end + v = new_hostname ? new_hostname.to_str : nil + v = "[#{v}]" if /\A\[.*\]\z/ !~ v && /:/ =~ v + self.host = v + end + + ## + # Returns the top-level domain for this host. + # + # @example + # Addressable::URI.parse("http://www.example.co.uk").tld # => "co.uk" + def tld + PublicSuffix.parse(self.host, ignore_private: true).tld + end + + ## + # Sets the top-level domain for this URI. + # + # @param [String, #to_str] new_tld The new top-level domain. + def tld=(new_tld) + replaced_tld = host.sub(/#{tld}\z/, new_tld) + self.host = PublicSuffix::Domain.new(replaced_tld).to_s + end + + ## + # Returns the public suffix domain for this host. + # + # @example + # Addressable::URI.parse("http://www.example.co.uk").domain # => "example.co.uk" + def domain + PublicSuffix.domain(self.host, ignore_private: true) + end + + ## + # The authority component for this URI. + # Combines the user, password, host, and port components. + # + # @return [String] The authority component. + def authority + self.host && @authority ||= begin + authority = String.new + if self.userinfo != nil + authority << "#{self.userinfo}@" + end + authority << self.host + if self.port != nil + authority << ":#{self.port}" + end + authority + end + end + + ## + # The authority component for this URI, normalized. + # + # @return [String] The authority component, normalized. + def normalized_authority + return nil unless self.authority + @normalized_authority ||= begin + authority = String.new + if self.normalized_userinfo != nil + authority << "#{self.normalized_userinfo}@" + end + authority << self.normalized_host + if self.normalized_port != nil + authority << ":#{self.normalized_port}" + end + authority + end + # All normalized values should be UTF-8 + force_utf8_encoding_if_needed(@normalized_authority) + @normalized_authority + end + + ## + # Sets the authority component for this URI. + # + # @param [String, #to_str] new_authority The new authority component. + def authority=(new_authority) + if new_authority + if !new_authority.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_authority.class} into String." + end + new_authority = new_authority.to_str + new_userinfo = new_authority[/^([^\[\]]*)@/, 1] + if new_userinfo + new_user = new_userinfo.strip[/^([^:]*):?/, 1] + new_password = new_userinfo.strip[/:(.*)$/, 1] + end + new_host = new_authority.sub( + /^([^\[\]]*)@/, EMPTY_STR + ).sub( + /:([^:@\[\]]*?)$/, EMPTY_STR + ) + new_port = + new_authority[/:([^:@\[\]]*?)$/, 1] + end + + # Password assigned first to ensure validity in case of nil + self.password = defined?(new_password) ? new_password : nil + self.user = defined?(new_user) ? new_user : nil + self.host = defined?(new_host) ? new_host : nil + self.port = defined?(new_port) ? new_port : nil + + # Reset dependent values + remove_instance_variable(:@userinfo) if defined?(@userinfo) + remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo) + remove_composite_values + + # Ensure we haven't created an invalid URI + validate() + end + + ## + # The origin for this URI, serialized to ASCII, as per + # RFC 6454, section 6.2. + # + # @return [String] The serialized origin. + def origin + if self.scheme && self.authority + if self.normalized_port + "#{self.normalized_scheme}://#{self.normalized_host}" + + ":#{self.normalized_port}" + else + "#{self.normalized_scheme}://#{self.normalized_host}" + end + else + "null" + end + end + + ## + # Sets the origin for this URI, serialized to ASCII, as per + # RFC 6454, section 6.2. This assignment will reset the `userinfo` + # component. + # + # @param [String, #to_str] new_origin The new origin component. + def origin=(new_origin) + if new_origin + if !new_origin.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_origin.class} into String." + end + new_origin = new_origin.to_str + new_scheme = new_origin[/^([^:\/?#]+):\/\//, 1] + unless new_scheme + raise InvalidURIError, 'An origin cannot omit the scheme.' + end + new_host = new_origin[/:\/\/([^\/?#:]+)/, 1] + unless new_host + raise InvalidURIError, 'An origin cannot omit the host.' + end + new_port = new_origin[/:([^:@\[\]\/]*?)$/, 1] + end + + self.scheme = defined?(new_scheme) ? new_scheme : nil + self.host = defined?(new_host) ? new_host : nil + self.port = defined?(new_port) ? new_port : nil + self.userinfo = nil + + # Reset dependent values + remove_instance_variable(:@userinfo) if defined?(@userinfo) + remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo) + remove_instance_variable(:@authority) if defined?(@authority) + remove_instance_variable(:@normalized_authority) if defined?(@normalized_authority) + remove_composite_values + + # Ensure we haven't created an invalid URI + validate() + end + + # Returns an array of known ip-based schemes. These schemes typically + # use a similar URI form: + # //:@:/ + def self.ip_based_schemes + return self.port_mapping.keys + end + + # Returns a hash of common IP-based schemes and their default port + # numbers. Adding new schemes to this hash, as necessary, will allow + # for better URI normalization. + def self.port_mapping + PORT_MAPPING + end + + ## + # The port component for this URI. + # This is the port number actually given in the URI. This does not + # infer port numbers from default values. + # + # @return [Integer] The port component. + def port + return defined?(@port) ? @port : nil + end + + ## + # The port component for this URI, normalized. + # + # @return [Integer] The port component, normalized. + def normalized_port + return nil unless self.port + return @normalized_port if defined?(@normalized_port) + @normalized_port ||= begin + if URI.port_mapping[self.normalized_scheme] == self.port + nil + else + self.port + end + end + end + + ## + # Sets the port component for this URI. + # + # @param [String, Integer, #to_s] new_port The new port component. + def port=(new_port) + if new_port != nil && new_port.respond_to?(:to_str) + new_port = Addressable::URI.unencode_component(new_port.to_str) + end + + if new_port.respond_to?(:valid_encoding?) && !new_port.valid_encoding? + raise InvalidURIError, "Invalid encoding in port" + end + + if new_port != nil && !(new_port.to_s =~ /^\d+$/) + raise InvalidURIError, + "Invalid port number: #{new_port.inspect}" + end + + @port = new_port.to_s.to_i + @port = nil if @port == 0 + + # Reset dependent values + remove_instance_variable(:@authority) if defined?(@authority) + remove_instance_variable(:@normalized_port) if defined?(@normalized_port) + remove_composite_values + + # Ensure we haven't created an invalid URI + validate() + end + + ## + # The inferred port component for this URI. + # This method will normalize to the default port for the URI's scheme if + # the port isn't explicitly specified in the URI. + # + # @return [Integer] The inferred port component. + def inferred_port + if self.port.to_i == 0 + self.default_port + else + self.port.to_i + end + end + + ## + # The default port for this URI's scheme. + # This method will always returns the default port for the URI's scheme + # regardless of the presence of an explicit port in the URI. + # + # @return [Integer] The default port. + def default_port + URI.port_mapping[self.scheme.strip.downcase] if self.scheme + end + + ## + # The combination of components that represent a site. + # Combines the scheme, user, password, host, and port components. + # Primarily useful for HTTP and HTTPS. + # + # For example, "http://example.com/path?query" would have a + # site value of "http://example.com". + # + # @return [String] The components that identify a site. + def site + (self.scheme || self.authority) && @site ||= begin + site_string = "".dup + site_string << "#{self.scheme}:" if self.scheme != nil + site_string << "//#{self.authority}" if self.authority != nil + site_string + end + end + + ## + # The normalized combination of components that represent a site. + # Combines the scheme, user, password, host, and port components. + # Primarily useful for HTTP and HTTPS. + # + # For example, "http://example.com/path?query" would have a + # site value of "http://example.com". + # + # @return [String] The normalized components that identify a site. + def normalized_site + return nil unless self.site + @normalized_site ||= begin + site_string = "".dup + if self.normalized_scheme != nil + site_string << "#{self.normalized_scheme}:" + end + if self.normalized_authority != nil + site_string << "//#{self.normalized_authority}" + end + site_string + end + # All normalized values should be UTF-8 + force_utf8_encoding_if_needed(@normalized_site) + @normalized_site + end + + ## + # Sets the site value for this URI. + # + # @param [String, #to_str] new_site The new site value. + def site=(new_site) + if new_site + if !new_site.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_site.class} into String." + end + new_site = new_site.to_str + # These two regular expressions derived from the primary parsing + # expression + self.scheme = new_site[/^(?:([^:\/?#]+):)?(?:\/\/(?:[^\/?#]*))?$/, 1] + self.authority = new_site[ + /^(?:(?:[^:\/?#]+):)?(?:\/\/([^\/?#]*))?$/, 1 + ] + else + self.scheme = nil + self.authority = nil + end + end + + ## + # The path component for this URI. + # + # @return [String] The path component. + def path + return defined?(@path) ? @path : EMPTY_STR + end + + NORMPATH = /^(?!\/)[^\/:]*:.*$/ + ## + # The path component for this URI, normalized. + # + # @return [String] The path component, normalized. + def normalized_path + @normalized_path ||= begin + path = self.path.to_s + if self.scheme == nil && path =~ NORMPATH + # Relative paths with colons in the first segment are ambiguous. + path = path.sub(":", "%2F") + end + # String#split(delimeter, -1) uses the more strict splitting behavior + # found by default in Python. + result = path.strip.split(SLASH, -1).map do |segment| + Addressable::URI.normalize_component( + segment, + Addressable::URI::NormalizeCharacterClasses::PCHAR + ) + end.join(SLASH) + + result = URI.normalize_path(result) + if result.empty? && + ["http", "https", "ftp", "tftp"].include?(self.normalized_scheme) + result = SLASH.dup + end + result + end + # All normalized values should be UTF-8 + force_utf8_encoding_if_needed(@normalized_path) + @normalized_path + end + + ## + # Sets the path component for this URI. + # + # @param [String, #to_str] new_path The new path component. + def path=(new_path) + if new_path && !new_path.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_path.class} into String." + end + @path = (new_path || EMPTY_STR).to_str + if !@path.empty? && @path[0..0] != SLASH && host != nil + @path = "/#{@path}" + end + + # Reset dependent values + remove_instance_variable(:@normalized_path) if defined?(@normalized_path) + remove_composite_values + + # Ensure we haven't created an invalid URI + validate() + end + + ## + # The basename, if any, of the file in the path component. + # + # @return [String] The path's basename. + def basename + # Path cannot be nil + return File.basename(self.path).sub(/;[^\/]*$/, EMPTY_STR) + end + + ## + # The extname, if any, of the file in the path component. + # Empty string if there is no extension. + # + # @return [String] The path's extname. + def extname + return nil unless self.path + return File.extname(self.basename) + end + + ## + # The query component for this URI. + # + # @return [String] The query component. + def query + return defined?(@query) ? @query : nil + end + + ## + # The query component for this URI, normalized. + # + # @return [String] The query component, normalized. + def normalized_query(*flags) + return nil unless self.query + return @normalized_query if defined?(@normalized_query) + @normalized_query ||= begin + modified_query_class = Addressable::URI::CharacterClasses::QUERY.dup + # Make sure possible key-value pair delimiters are escaped. + modified_query_class.sub!("\\&", "").sub!("\\;", "") + pairs = (query || "").split("&", -1) + pairs.delete_if(&:empty?).uniq! if flags.include?(:compacted) + pairs.sort! if flags.include?(:sorted) + component = pairs.map do |pair| + Addressable::URI.normalize_component( + pair, + Addressable::URI::NormalizeCharacterClasses::QUERY, + "+" + ) + end.join("&") + component == "" ? nil : component + end + # All normalized values should be UTF-8 + force_utf8_encoding_if_needed(@normalized_query) + @normalized_query + end + + ## + # Sets the query component for this URI. + # + # @param [String, #to_str] new_query The new query component. + def query=(new_query) + if new_query && !new_query.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_query.class} into String." + end + @query = new_query ? new_query.to_str : nil + + # Reset dependent values + remove_instance_variable(:@normalized_query) if defined?(@normalized_query) + remove_composite_values + end + + ## + # Converts the query component to a Hash value. + # + # @param [Class] return_type The return type desired. Value must be either + # `Hash` or `Array`. + # + # @return [Hash, Array, nil] The query string parsed as a Hash or Array + # or nil if the query string is blank. + # + # @example + # Addressable::URI.parse("?one=1&two=2&three=3").query_values + # #=> {"one" => "1", "two" => "2", "three" => "3"} + # Addressable::URI.parse("?one=two&one=three").query_values(Array) + # #=> [["one", "two"], ["one", "three"]] + # Addressable::URI.parse("?one=two&one=three").query_values(Hash) + # #=> {"one" => "three"} + # Addressable::URI.parse("?").query_values + # #=> {} + # Addressable::URI.parse("").query_values + # #=> nil + def query_values(return_type=Hash) + empty_accumulator = Array == return_type ? [] : {} + if return_type != Hash && return_type != Array + raise ArgumentError, "Invalid return type. Must be Hash or Array." + end + return nil if self.query == nil + split_query = self.query.split("&").map do |pair| + pair.split("=", 2) if pair && !pair.empty? + end.compact + return split_query.inject(empty_accumulator.dup) do |accu, pair| + # I'd rather use key/value identifiers instead of array lookups, + # but in this case I really want to maintain the exact pair structure, + # so it's best to make all changes in-place. + pair[0] = URI.unencode_component(pair[0]) + if pair[1].respond_to?(:to_str) + value = pair[1].to_str + # I loathe the fact that I have to do this. Stupid HTML 4.01. + # Treating '+' as a space was just an unbelievably bad idea. + # There was nothing wrong with '%20'! + # If it ain't broke, don't fix it! + value = value.tr("+", " ") if ["http", "https", nil].include?(scheme) + pair[1] = URI.unencode_component(value) + end + if return_type == Hash + accu[pair[0]] = pair[1] + else + accu << pair + end + accu + end + end + + ## + # Sets the query component for this URI from a Hash object. + # An empty Hash or Array will result in an empty query string. + # + # @param [Hash, #to_hash, Array] new_query_values The new query values. + # + # @example + # uri.query_values = {:a => "a", :b => ["c", "d", "e"]} + # uri.query + # # => "a=a&b=c&b=d&b=e" + # uri.query_values = [['a', 'a'], ['b', 'c'], ['b', 'd'], ['b', 'e']] + # uri.query + # # => "a=a&b=c&b=d&b=e" + # uri.query_values = [['a', 'a'], ['b', ['c', 'd', 'e']]] + # uri.query + # # => "a=a&b=c&b=d&b=e" + # uri.query_values = [['flag'], ['key', 'value']] + # uri.query + # # => "flag&key=value" + def query_values=(new_query_values) + if new_query_values == nil + self.query = nil + return nil + end + + if !new_query_values.is_a?(Array) + if !new_query_values.respond_to?(:to_hash) + raise TypeError, + "Can't convert #{new_query_values.class} into Hash." + end + new_query_values = new_query_values.to_hash + new_query_values = new_query_values.map do |key, value| + key = key.to_s if key.kind_of?(Symbol) + [key, value] + end + # Useful default for OAuth and caching. + # Only to be used for non-Array inputs. Arrays should preserve order. + new_query_values.sort! + end + + # new_query_values have form [['key1', 'value1'], ['key2', 'value2']] + buffer = "".dup + new_query_values.each do |key, value| + encoded_key = URI.encode_component( + key, CharacterClasses::UNRESERVED + ) + if value == nil + buffer << "#{encoded_key}&" + elsif value.kind_of?(Array) + value.each do |sub_value| + encoded_value = URI.encode_component( + sub_value, CharacterClasses::UNRESERVED + ) + buffer << "#{encoded_key}=#{encoded_value}&" + end + else + encoded_value = URI.encode_component( + value, CharacterClasses::UNRESERVED + ) + buffer << "#{encoded_key}=#{encoded_value}&" + end + end + self.query = buffer.chop + end + + ## + # The HTTP request URI for this URI. This is the path and the + # query string. + # + # @return [String] The request URI required for an HTTP request. + def request_uri + return nil if self.absolute? && self.scheme !~ /^https?$/i + return ( + (!self.path.empty? ? self.path : SLASH) + + (self.query ? "?#{self.query}" : EMPTY_STR) + ) + end + + ## + # Sets the HTTP request URI for this URI. + # + # @param [String, #to_str] new_request_uri The new HTTP request URI. + def request_uri=(new_request_uri) + if !new_request_uri.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_request_uri.class} into String." + end + if self.absolute? && self.scheme !~ /^https?$/i + raise InvalidURIError, + "Cannot set an HTTP request URI for a non-HTTP URI." + end + new_request_uri = new_request_uri.to_str + path_component = new_request_uri[/^([^\?]*)\??(?:.*)$/, 1] + query_component = new_request_uri[/^(?:[^\?]*)\?(.*)$/, 1] + path_component = path_component.to_s + path_component = (!path_component.empty? ? path_component : SLASH) + self.path = path_component + self.query = query_component + + # Reset dependent values + remove_composite_values + end + + ## + # The fragment component for this URI. + # + # @return [String] The fragment component. + def fragment + return defined?(@fragment) ? @fragment : nil + end + + ## + # The fragment component for this URI, normalized. + # + # @return [String] The fragment component, normalized. + def normalized_fragment + return nil unless self.fragment + return @normalized_fragment if defined?(@normalized_fragment) + @normalized_fragment ||= begin + component = Addressable::URI.normalize_component( + self.fragment, + Addressable::URI::NormalizeCharacterClasses::FRAGMENT + ) + component == "" ? nil : component + end + # All normalized values should be UTF-8 + force_utf8_encoding_if_needed(@normalized_fragment) + @normalized_fragment + end + + ## + # Sets the fragment component for this URI. + # + # @param [String, #to_str] new_fragment The new fragment component. + def fragment=(new_fragment) + if new_fragment && !new_fragment.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_fragment.class} into String." + end + @fragment = new_fragment ? new_fragment.to_str : nil + + # Reset dependent values + remove_instance_variable(:@normalized_fragment) if defined?(@normalized_fragment) + remove_composite_values + + # Ensure we haven't created an invalid URI + validate() + end + + ## + # Determines if the scheme indicates an IP-based protocol. + # + # @return [TrueClass, FalseClass] + # true if the scheme indicates an IP-based protocol. + # false otherwise. + def ip_based? + if self.scheme + return URI.ip_based_schemes.include?( + self.scheme.strip.downcase) + end + return false + end + + ## + # Determines if the URI is relative. + # + # @return [TrueClass, FalseClass] + # true if the URI is relative. false + # otherwise. + def relative? + return self.scheme.nil? + end + + ## + # Determines if the URI is absolute. + # + # @return [TrueClass, FalseClass] + # true if the URI is absolute. false + # otherwise. + def absolute? + return !relative? + end + + ## + # Joins two URIs together. + # + # @param [String, Addressable::URI, #to_str] The URI to join with. + # + # @return [Addressable::URI] The joined URI. + def join(uri) + if !uri.respond_to?(:to_str) + raise TypeError, "Can't convert #{uri.class} into String." + end + if !uri.kind_of?(URI) + # Otherwise, convert to a String, then parse. + uri = URI.parse(uri.to_str) + end + if uri.to_s.empty? + return self.dup + end + + joined_scheme = nil + joined_user = nil + joined_password = nil + joined_host = nil + joined_port = nil + joined_path = nil + joined_query = nil + joined_fragment = nil + + # Section 5.2.2 of RFC 3986 + if uri.scheme != nil + joined_scheme = uri.scheme + joined_user = uri.user + joined_password = uri.password + joined_host = uri.host + joined_port = uri.port + joined_path = URI.normalize_path(uri.path) + joined_query = uri.query + else + if uri.authority != nil + joined_user = uri.user + joined_password = uri.password + joined_host = uri.host + joined_port = uri.port + joined_path = URI.normalize_path(uri.path) + joined_query = uri.query + else + if uri.path == nil || uri.path.empty? + joined_path = self.path + if uri.query != nil + joined_query = uri.query + else + joined_query = self.query + end + else + if uri.path[0..0] == SLASH + joined_path = URI.normalize_path(uri.path) + else + base_path = self.path.dup + base_path = EMPTY_STR if base_path == nil + base_path = URI.normalize_path(base_path) + + # Section 5.2.3 of RFC 3986 + # + # Removes the right-most path segment from the base path. + if base_path.include?(SLASH) + base_path.sub!(/\/[^\/]+$/, SLASH) + else + base_path = EMPTY_STR + end + + # If the base path is empty and an authority segment has been + # defined, use a base path of SLASH + if base_path.empty? && self.authority != nil + base_path = SLASH + end + + joined_path = URI.normalize_path(base_path + uri.path) + end + joined_query = uri.query + end + joined_user = self.user + joined_password = self.password + joined_host = self.host + joined_port = self.port + end + joined_scheme = self.scheme + end + joined_fragment = uri.fragment + + return self.class.new( + :scheme => joined_scheme, + :user => joined_user, + :password => joined_password, + :host => joined_host, + :port => joined_port, + :path => joined_path, + :query => joined_query, + :fragment => joined_fragment + ) + end + alias_method :+, :join + + ## + # Destructive form of join. + # + # @param [String, Addressable::URI, #to_str] The URI to join with. + # + # @return [Addressable::URI] The joined URI. + # + # @see Addressable::URI#join + def join!(uri) + replace_self(self.join(uri)) + end + + ## + # Merges a URI with a Hash of components. + # This method has different behavior from join. Any + # components present in the hash parameter will override the + # original components. The path component is not treated specially. + # + # @param [Hash, Addressable::URI, #to_hash] The components to merge with. + # + # @return [Addressable::URI] The merged URI. + # + # @see Hash#merge + def merge(hash) + if !hash.respond_to?(:to_hash) + raise TypeError, "Can't convert #{hash.class} into Hash." + end + hash = hash.to_hash + + if hash.has_key?(:authority) + if (hash.keys & [:userinfo, :user, :password, :host, :port]).any? + raise ArgumentError, + "Cannot specify both an authority and any of the components " + + "within the authority." + end + end + if hash.has_key?(:userinfo) + if (hash.keys & [:user, :password]).any? + raise ArgumentError, + "Cannot specify both a userinfo and either the user or password." + end + end + + uri = self.class.new + uri.defer_validation do + # Bunch of crazy logic required because of the composite components + # like userinfo and authority. + uri.scheme = + hash.has_key?(:scheme) ? hash[:scheme] : self.scheme + if hash.has_key?(:authority) + uri.authority = + hash.has_key?(:authority) ? hash[:authority] : self.authority + end + if hash.has_key?(:userinfo) + uri.userinfo = + hash.has_key?(:userinfo) ? hash[:userinfo] : self.userinfo + end + if !hash.has_key?(:userinfo) && !hash.has_key?(:authority) + uri.user = + hash.has_key?(:user) ? hash[:user] : self.user + uri.password = + hash.has_key?(:password) ? hash[:password] : self.password + end + if !hash.has_key?(:authority) + uri.host = + hash.has_key?(:host) ? hash[:host] : self.host + uri.port = + hash.has_key?(:port) ? hash[:port] : self.port + end + uri.path = + hash.has_key?(:path) ? hash[:path] : self.path + uri.query = + hash.has_key?(:query) ? hash[:query] : self.query + uri.fragment = + hash.has_key?(:fragment) ? hash[:fragment] : self.fragment + end + + return uri + end + + ## + # Destructive form of merge. + # + # @param [Hash, Addressable::URI, #to_hash] The components to merge with. + # + # @return [Addressable::URI] The merged URI. + # + # @see Addressable::URI#merge + def merge!(uri) + replace_self(self.merge(uri)) + end + + ## + # Returns the shortest normalized relative form of this URI that uses the + # supplied URI as a base for resolution. Returns an absolute URI if + # necessary. This is effectively the opposite of route_to. + # + # @param [String, Addressable::URI, #to_str] uri The URI to route from. + # + # @return [Addressable::URI] + # The normalized relative URI that is equivalent to the original URI. + def route_from(uri) + uri = URI.parse(uri).normalize + normalized_self = self.normalize + if normalized_self.relative? + raise ArgumentError, "Expected absolute URI, got: #{self.to_s}" + end + if uri.relative? + raise ArgumentError, "Expected absolute URI, got: #{uri.to_s}" + end + if normalized_self == uri + return Addressable::URI.parse("##{normalized_self.fragment}") + end + components = normalized_self.to_hash + if normalized_self.scheme == uri.scheme + components[:scheme] = nil + if normalized_self.authority == uri.authority + components[:user] = nil + components[:password] = nil + components[:host] = nil + components[:port] = nil + if normalized_self.path == uri.path + components[:path] = nil + if normalized_self.query == uri.query + components[:query] = nil + end + else + if uri.path != SLASH and components[:path] + self_splitted_path = split_path(components[:path]) + uri_splitted_path = split_path(uri.path) + self_dir = self_splitted_path.shift + uri_dir = uri_splitted_path.shift + while !self_splitted_path.empty? && !uri_splitted_path.empty? and self_dir == uri_dir + self_dir = self_splitted_path.shift + uri_dir = uri_splitted_path.shift + end + components[:path] = (uri_splitted_path.fill('..') + [self_dir] + self_splitted_path).join(SLASH) + end + end + end + end + # Avoid network-path references. + if components[:host] != nil + components[:scheme] = normalized_self.scheme + end + return Addressable::URI.new( + :scheme => components[:scheme], + :user => components[:user], + :password => components[:password], + :host => components[:host], + :port => components[:port], + :path => components[:path], + :query => components[:query], + :fragment => components[:fragment] + ) + end + + ## + # Returns the shortest normalized relative form of the supplied URI that + # uses this URI as a base for resolution. Returns an absolute URI if + # necessary. This is effectively the opposite of route_from. + # + # @param [String, Addressable::URI, #to_str] uri The URI to route to. + # + # @return [Addressable::URI] + # The normalized relative URI that is equivalent to the supplied URI. + def route_to(uri) + return URI.parse(uri).route_from(self) + end + + ## + # Returns a normalized URI object. + # + # NOTE: This method does not attempt to fully conform to specifications. + # It exists largely to correct other people's failures to read the + # specifications, and also to deal with caching issues since several + # different URIs may represent the same resource and should not be + # cached multiple times. + # + # @return [Addressable::URI] The normalized URI. + def normalize + # This is a special exception for the frequently misused feed + # URI scheme. + if normalized_scheme == "feed" + if self.to_s =~ /^feed:\/*http:\/*/ + return URI.parse( + self.to_s[/^feed:\/*(http:\/*.*)/, 1] + ).normalize + end + end + + return self.class.new( + :scheme => normalized_scheme, + :authority => normalized_authority, + :path => normalized_path, + :query => normalized_query, + :fragment => normalized_fragment + ) + end + + ## + # Destructively normalizes this URI object. + # + # @return [Addressable::URI] The normalized URI. + # + # @see Addressable::URI#normalize + def normalize! + replace_self(self.normalize) + end + + ## + # Creates a URI suitable for display to users. If semantic attacks are + # likely, the application should try to detect these and warn the user. + # See RFC 3986, + # section 7.6 for more information. + # + # @return [Addressable::URI] A URI suitable for display purposes. + def display_uri + display_uri = self.normalize + display_uri.host = ::Addressable::IDNA.to_unicode(display_uri.host) + return display_uri + end + + ## + # Returns true if the URI objects are equal. This method + # normalizes both URIs before doing the comparison, and allows comparison + # against Strings. + # + # @param [Object] uri The URI to compare. + # + # @return [TrueClass, FalseClass] + # true if the URIs are equivalent, false + # otherwise. + def ===(uri) + if uri.respond_to?(:normalize) + uri_string = uri.normalize.to_s + else + begin + uri_string = ::Addressable::URI.parse(uri).normalize.to_s + rescue InvalidURIError, TypeError + return false + end + end + return self.normalize.to_s == uri_string + end + + ## + # Returns true if the URI objects are equal. This method + # normalizes both URIs before doing the comparison. + # + # @param [Object] uri The URI to compare. + # + # @return [TrueClass, FalseClass] + # true if the URIs are equivalent, false + # otherwise. + def ==(uri) + return false unless uri.kind_of?(URI) + return self.normalize.to_s == uri.normalize.to_s + end + + ## + # Returns true if the URI objects are equal. This method + # does NOT normalize either URI before doing the comparison. + # + # @param [Object] uri The URI to compare. + # + # @return [TrueClass, FalseClass] + # true if the URIs are equivalent, false + # otherwise. + def eql?(uri) + return false unless uri.kind_of?(URI) + return self.to_s == uri.to_s + end + + ## + # A hash value that will make a URI equivalent to its normalized + # form. + # + # @return [Integer] A hash of the URI. + def hash + @hash ||= self.to_s.hash * -1 + end + + ## + # Clones the URI object. + # + # @return [Addressable::URI] The cloned URI. + def dup + duplicated_uri = self.class.new( + :scheme => self.scheme ? self.scheme.dup : nil, + :user => self.user ? self.user.dup : nil, + :password => self.password ? self.password.dup : nil, + :host => self.host ? self.host.dup : nil, + :port => self.port, + :path => self.path ? self.path.dup : nil, + :query => self.query ? self.query.dup : nil, + :fragment => self.fragment ? self.fragment.dup : nil + ) + return duplicated_uri + end + + ## + # Omits components from a URI. + # + # @param [Symbol] *components The components to be omitted. + # + # @return [Addressable::URI] The URI with components omitted. + # + # @example + # uri = Addressable::URI.parse("http://example.com/path?query") + # #=> # + # uri.omit(:scheme, :authority) + # #=> # + def omit(*components) + invalid_components = components - [ + :scheme, :user, :password, :userinfo, :host, :port, :authority, + :path, :query, :fragment + ] + unless invalid_components.empty? + raise ArgumentError, + "Invalid component names: #{invalid_components.inspect}." + end + duplicated_uri = self.dup + duplicated_uri.defer_validation do + components.each do |component| + duplicated_uri.send((component.to_s + "=").to_sym, nil) + end + duplicated_uri.user = duplicated_uri.normalized_user + end + duplicated_uri + end + + ## + # Destructive form of omit. + # + # @param [Symbol] *components The components to be omitted. + # + # @return [Addressable::URI] The URI with components omitted. + # + # @see Addressable::URI#omit + def omit!(*components) + replace_self(self.omit(*components)) + end + + ## + # Determines if the URI is an empty string. + # + # @return [TrueClass, FalseClass] + # Returns true if empty, false otherwise. + def empty? + return self.to_s.empty? + end + + ## + # Converts the URI to a String. + # + # @return [String] The URI's String representation. + def to_s + if self.scheme == nil && self.path != nil && !self.path.empty? && + self.path =~ NORMPATH + raise InvalidURIError, + "Cannot assemble URI string with ambiguous path: '#{self.path}'" + end + @uri_string ||= begin + uri_string = String.new + uri_string << "#{self.scheme}:" if self.scheme != nil + uri_string << "//#{self.authority}" if self.authority != nil + uri_string << self.path.to_s + uri_string << "?#{self.query}" if self.query != nil + uri_string << "##{self.fragment}" if self.fragment != nil + uri_string.force_encoding(Encoding::UTF_8) + uri_string + end + end + + ## + # URI's are glorified Strings. Allow implicit conversion. + alias_method :to_str, :to_s + + ## + # Returns a Hash of the URI components. + # + # @return [Hash] The URI as a Hash of components. + def to_hash + return { + :scheme => self.scheme, + :user => self.user, + :password => self.password, + :host => self.host, + :port => self.port, + :path => self.path, + :query => self.query, + :fragment => self.fragment + } + end + + ## + # Returns a String representation of the URI object's state. + # + # @return [String] The URI object's state, as a String. + def inspect + sprintf("#<%s:%#0x URI:%s>", URI.to_s, self.object_id, self.to_s) + end + + ## + # This method allows you to make several changes to a URI simultaneously, + # which separately would cause validation errors, but in conjunction, + # are valid. The URI will be revalidated as soon as the entire block has + # been executed. + # + # @param [Proc] block + # A set of operations to perform on a given URI. + def defer_validation + raise LocalJumpError, "No block given." unless block_given? + @validation_deferred = true + yield + @validation_deferred = false + validate + return nil + end + + protected + SELF_REF = '.' + PARENT = '..' + + RULE_2A = /\/\.\/|\/\.$/ + RULE_2B_2C = /\/([^\/]*)\/\.\.\/|\/([^\/]*)\/\.\.$/ + RULE_2D = /^\.\.?\/?/ + RULE_PREFIXED_PARENT = /^\/\.\.?\/|^(\/\.\.?)+\/?$/ + + ## + # Resolves paths to their simplest form. + # + # @param [String] path The path to normalize. + # + # @return [String] The normalized path. + def self.normalize_path(path) + # Section 5.2.4 of RFC 3986 + + return if path.nil? + normalized_path = path.dup + loop do + mod ||= normalized_path.gsub!(RULE_2A, SLASH) + + pair = normalized_path.match(RULE_2B_2C) + if pair + parent = pair[1] + current = pair[2] + else + parent = nil + current = nil + end + + regexp = "/#{Regexp.escape(parent.to_s)}/\\.\\./|" + regexp += "(/#{Regexp.escape(current.to_s)}/\\.\\.$)" + + if pair && ((parent != SELF_REF && parent != PARENT) || + (current != SELF_REF && current != PARENT)) + mod ||= normalized_path.gsub!(Regexp.new(regexp), SLASH) + end + + mod ||= normalized_path.gsub!(RULE_2D, EMPTY_STR) + # Non-standard, removes prefixed dotted segments from path. + mod ||= normalized_path.gsub!(RULE_PREFIXED_PARENT, SLASH) + break if mod.nil? + end + + normalized_path + end + + ## + # Ensures that the URI is valid. + def validate + return if !!@validation_deferred + if self.scheme != nil && self.ip_based? && + (self.host == nil || self.host.empty?) && + (self.path == nil || self.path.empty?) + raise InvalidURIError, + "Absolute URI missing hierarchical segment: '#{self.to_s}'" + end + if self.host == nil + if self.port != nil || + self.user != nil || + self.password != nil + raise InvalidURIError, "Hostname not supplied: '#{self.to_s}'" + end + end + if self.path != nil && !self.path.empty? && self.path[0..0] != SLASH && + self.authority != nil + raise InvalidURIError, + "Cannot have a relative path with an authority set: '#{self.to_s}'" + end + if self.path != nil && !self.path.empty? && + self.path[0..1] == SLASH + SLASH && self.authority == nil + raise InvalidURIError, + "Cannot have a path with two leading slashes " + + "without an authority set: '#{self.to_s}'" + end + unreserved = CharacterClasses::UNRESERVED + sub_delims = CharacterClasses::SUB_DELIMS + if !self.host.nil? && (self.host =~ /[<>{}\/\\\?\#\@"[[:space:]]]/ || + (self.host[/^\[(.*)\]$/, 1] != nil && self.host[/^\[(.*)\]$/, 1] !~ + Regexp.new("^[#{unreserved}#{sub_delims}:]*$"))) + raise InvalidURIError, "Invalid character in host: '#{self.host.to_s}'" + end + return nil + end + + ## + # Replaces the internal state of self with the specified URI's state. + # Used in destructive operations to avoid massive code repetition. + # + # @param [Addressable::URI] uri The URI to replace self with. + # + # @return [Addressable::URI] self. + def replace_self(uri) + # Reset dependent values + instance_variables.each do |var| + if instance_variable_defined?(var) && var != :@validation_deferred + remove_instance_variable(var) + end + end + + @scheme = uri.scheme + @user = uri.user + @password = uri.password + @host = uri.host + @port = uri.port + @path = uri.path + @query = uri.query + @fragment = uri.fragment + return self + end + + ## + # Splits path string with "/" (slash). + # It is considered that there is empty string after last slash when + # path ends with slash. + # + # @param [String] path The path to split. + # + # @return [Array] An array of parts of path. + def split_path(path) + splitted = path.split(SLASH) + splitted << EMPTY_STR if path.end_with? SLASH + splitted + end + + ## + # Resets composite values for the entire URI + # + # @api private + def remove_composite_values + remove_instance_variable(:@uri_string) if defined?(@uri_string) + remove_instance_variable(:@hash) if defined?(@hash) + end + + ## + # Converts the string to be UTF-8 if it is not already UTF-8 + # + # @api private + def force_utf8_encoding_if_needed(str) + if str && str.encoding != Encoding::UTF_8 + str.force_encoding(Encoding::UTF_8) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable/version.rb b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable/version.rb new file mode 100644 index 0000000..d8e1644 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable/version.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +#-- +# Copyright (C) Bob Aman +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#++ + + +# Used to prevent the class/module from being loaded more than once +if !defined?(Addressable::VERSION) + module Addressable + module VERSION + MAJOR = 2 + MINOR = 8 + TINY = 1 + + STRING = [MAJOR, MINOR, TINY].join('.') + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/spec/addressable/idna_spec.rb b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/spec/addressable/idna_spec.rb new file mode 100644 index 0000000..b1509d2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/spec/addressable/idna_spec.rb @@ -0,0 +1,301 @@ +# frozen_string_literal: true + +# Copyright (C) Bob Aman +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +require "spec_helper" + +# Have to use RubyGems to load the idn gem. +require "rubygems" + +require "addressable/idna" + +shared_examples_for "converting from unicode to ASCII" do + it "should convert 'www.google.com' correctly" do + expect(Addressable::IDNA.to_ascii("www.google.com")).to eq("www.google.com") + end + + long = 'AcinusFallumTrompetumNullunCreditumVisumEstAtCuadLongumEtCefallum.com' + it "should convert '#{long}' correctly" do + expect(Addressable::IDNA.to_ascii(long)).to eq(long) + end + + it "should convert 'www.詹姆斯.com' correctly" do + expect(Addressable::IDNA.to_ascii( + "www.詹姆斯.com" + )).to eq("www.xn--8ws00zhy3a.com") + end + + it "should convert 'www.Iñtërnâtiônàlizætiøn.com' correctly" do + "www.Iñtërnâtiônàlizætiøn.com" + expect(Addressable::IDNA.to_ascii( + "www.I\xC3\xB1t\xC3\xABrn\xC3\xA2ti\xC3\xB4" + + "n\xC3\xA0liz\xC3\xA6ti\xC3\xB8n.com" + )).to eq("www.xn--itrntinliztin-vdb0a5exd8ewcye.com") + end + + it "should convert 'www.Iñtërnâtiônàlizætiøn.com' correctly" do + expect(Addressable::IDNA.to_ascii( + "www.In\xCC\x83te\xCC\x88rna\xCC\x82tio\xCC\x82n" + + "a\xCC\x80liz\xC3\xA6ti\xC3\xB8n.com" + )).to eq("www.xn--itrntinliztin-vdb0a5exd8ewcye.com") + end + + it "should convert " + + "'www.ほんとうにながいわけのわからないどめいんめいのらべるまだながくしないとたりない.w3.mag.keio.ac.jp' " + + "correctly" do + expect(Addressable::IDNA.to_ascii( + "www.\343\201\273\343\202\223\343\201\250\343\201\206\343\201\253\343" + + "\201\252\343\201\214\343\201\204\343\202\217\343\201\221\343\201\256" + + "\343\202\217\343\201\213\343\202\211\343\201\252\343\201\204\343\201" + + "\251\343\202\201\343\201\204\343\202\223\343\202\201\343\201\204\343" + + "\201\256\343\202\211\343\201\271\343\202\213\343\201\276\343\201\240" + + "\343\201\252\343\201\214\343\201\217\343\201\227\343\201\252\343\201" + + "\204\343\201\250\343\201\237\343\202\212\343\201\252\343\201\204." + + "w3.mag.keio.ac.jp" + )).to eq( + "www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3" + + "fg11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp" + ) + end + + it "should convert " + + "'www.ほんとうにながいわけのわからないどめいんめいのらべるまだながくしないとたりない.w3.mag.keio.ac.jp' " + + "correctly" do + expect(Addressable::IDNA.to_ascii( + "www.\343\201\273\343\202\223\343\201\250\343\201\206\343\201\253\343" + + "\201\252\343\201\213\343\202\231\343\201\204\343\202\217\343\201\221" + + "\343\201\256\343\202\217\343\201\213\343\202\211\343\201\252\343\201" + + "\204\343\201\250\343\202\231\343\202\201\343\201\204\343\202\223\343" + + "\202\201\343\201\204\343\201\256\343\202\211\343\201\270\343\202\231" + + "\343\202\213\343\201\276\343\201\237\343\202\231\343\201\252\343\201" + + "\213\343\202\231\343\201\217\343\201\227\343\201\252\343\201\204\343" + + "\201\250\343\201\237\343\202\212\343\201\252\343\201\204." + + "w3.mag.keio.ac.jp" + )).to eq( + "www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3" + + "fg11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp" + ) + end + + it "should convert '点心和烤鸭.w3.mag.keio.ac.jp' correctly" do + expect(Addressable::IDNA.to_ascii( + "点心和烤鸭.w3.mag.keio.ac.jp" + )).to eq("xn--0trv4xfvn8el34t.w3.mag.keio.ac.jp") + end + + it "should convert '가각갂갃간갅갆갇갈갉힢힣.com' correctly" do + expect(Addressable::IDNA.to_ascii( + "가각갂갃간갅갆갇갈갉힢힣.com" + )).to eq("xn--o39acdefghijk5883jma.com") + end + + it "should convert " + + "'\347\242\274\346\250\231\346\272\226\350" + + "\220\254\345\234\213\347\242\274.com' correctly" do + expect(Addressable::IDNA.to_ascii( + "\347\242\274\346\250\231\346\272\226\350" + + "\220\254\345\234\213\347\242\274.com" + )).to eq("xn--9cs565brid46mda086o.com") + end + + it "should convert 'リ宠퐱〹.com' correctly" do + expect(Addressable::IDNA.to_ascii( + "\357\276\230\345\256\240\355\220\261\343\200\271.com" + )).to eq("xn--eek174hoxfpr4k.com") + end + + it "should convert 'リ宠퐱卄.com' correctly" do + expect(Addressable::IDNA.to_ascii( + "\343\203\252\345\256\240\355\220\261\345\215\204.com" + )).to eq("xn--eek174hoxfpr4k.com") + end + + it "should convert 'ᆵ' correctly" do + expect(Addressable::IDNA.to_ascii( + "\341\206\265" + )).to eq("xn--4ud") + end + + it "should convert 'ᆵ' correctly" do + expect(Addressable::IDNA.to_ascii( + "\357\276\257" + )).to eq("xn--4ud") + end + + it "should convert '🌹🌹🌹.ws' correctly" do + expect(Addressable::IDNA.to_ascii( + "\360\237\214\271\360\237\214\271\360\237\214\271.ws" + )).to eq("xn--2h8haa.ws") + end + + it "should handle two adjacent '.'s correctly" do + expect(Addressable::IDNA.to_ascii( + "example..host" + )).to eq("example..host") + end +end + +shared_examples_for "converting from ASCII to unicode" do + long = 'AcinusFallumTrompetumNullunCreditumVisumEstAtCuadLongumEtCefallum.com' + it "should convert '#{long}' correctly" do + expect(Addressable::IDNA.to_unicode(long)).to eq(long) + end + + it "should return the identity conversion when punycode decode fails" do + expect(Addressable::IDNA.to_unicode("xn--zckp1cyg1.sblo.jp")).to eq( + "xn--zckp1cyg1.sblo.jp") + end + + it "should return the identity conversion when the ACE prefix has no suffix" do + expect(Addressable::IDNA.to_unicode("xn--...-")).to eq("xn--...-") + end + + it "should convert 'www.google.com' correctly" do + expect(Addressable::IDNA.to_unicode("www.google.com")).to eq( + "www.google.com") + end + + it "should convert 'www.詹姆斯.com' correctly" do + expect(Addressable::IDNA.to_unicode( + "www.xn--8ws00zhy3a.com" + )).to eq("www.詹姆斯.com") + end + + it "should convert '詹姆斯.com' correctly" do + expect(Addressable::IDNA.to_unicode( + "xn--8ws00zhy3a.com" + )).to eq("詹姆斯.com") + end + + it "should convert 'www.iñtërnâtiônàlizætiøn.com' correctly" do + expect(Addressable::IDNA.to_unicode( + "www.xn--itrntinliztin-vdb0a5exd8ewcye.com" + )).to eq("www.iñtërnâtiônàlizætiøn.com") + end + + it "should convert 'iñtërnâtiônàlizætiøn.com' correctly" do + expect(Addressable::IDNA.to_unicode( + "xn--itrntinliztin-vdb0a5exd8ewcye.com" + )).to eq("iñtërnâtiônàlizætiøn.com") + end + + it "should convert " + + "'www.ほんとうにながいわけのわからないどめいんめいのらべるまだながくしないとたりない.w3.mag.keio.ac.jp' " + + "correctly" do + expect(Addressable::IDNA.to_unicode( + "www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3" + + "fg11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp" + )).to eq( + "www.ほんとうにながいわけのわからないどめいんめいのらべるまだながくしないとたりない.w3.mag.keio.ac.jp" + ) + end + + it "should convert '点心和烤鸭.w3.mag.keio.ac.jp' correctly" do + expect(Addressable::IDNA.to_unicode( + "xn--0trv4xfvn8el34t.w3.mag.keio.ac.jp" + )).to eq("点心和烤鸭.w3.mag.keio.ac.jp") + end + + it "should convert '가각갂갃간갅갆갇갈갉힢힣.com' correctly" do + expect(Addressable::IDNA.to_unicode( + "xn--o39acdefghijk5883jma.com" + )).to eq("가각갂갃간갅갆갇갈갉힢힣.com") + end + + it "should convert " + + "'\347\242\274\346\250\231\346\272\226\350" + + "\220\254\345\234\213\347\242\274.com' correctly" do + expect(Addressable::IDNA.to_unicode( + "xn--9cs565brid46mda086o.com" + )).to eq( + "\347\242\274\346\250\231\346\272\226\350" + + "\220\254\345\234\213\347\242\274.com" + ) + end + + it "should convert 'リ宠퐱卄.com' correctly" do + expect(Addressable::IDNA.to_unicode( + "xn--eek174hoxfpr4k.com" + )).to eq("\343\203\252\345\256\240\355\220\261\345\215\204.com") + end + + it "should convert 'ᆵ' correctly" do + expect(Addressable::IDNA.to_unicode( + "xn--4ud" + )).to eq("\341\206\265") + end + + it "should convert '🌹🌹🌹.ws' correctly" do + expect(Addressable::IDNA.to_unicode( + "xn--2h8haa.ws" + )).to eq("\360\237\214\271\360\237\214\271\360\237\214\271.ws") + end + + it "should handle two adjacent '.'s correctly" do + expect(Addressable::IDNA.to_unicode( + "example..host" + )).to eq("example..host") + end + + it "should normalize 'string' correctly" do + expect(Addressable::IDNA.unicode_normalize_kc(:'string')).to eq("string") + expect(Addressable::IDNA.unicode_normalize_kc("string")).to eq("string") + end +end + +describe Addressable::IDNA, "when using the pure-Ruby implementation" do + before do + Addressable.send(:remove_const, :IDNA) + load "addressable/idna/pure.rb" + end + + it_should_behave_like "converting from unicode to ASCII" + it_should_behave_like "converting from ASCII to unicode" + + begin + require "fiber" + + it "should not blow up inside fibers" do + f = Fiber.new do + Addressable.send(:remove_const, :IDNA) + load "addressable/idna/pure.rb" + end + f.resume + end + rescue LoadError + # Fibers aren't supported in this version of Ruby, skip this test. + warn('Fibers unsupported.') + end +end + +begin + require "idn" + + describe Addressable::IDNA, "when using the native-code implementation" do + before do + Addressable.send(:remove_const, :IDNA) + load "addressable/idna/native.rb" + end + + it_should_behave_like "converting from unicode to ASCII" + it_should_behave_like "converting from ASCII to unicode" + end +rescue LoadError => error + raise error if ENV["CI"] && TestHelper.native_supported? + + # Cannot test the native implementation without libidn support. + warn('Could not load native IDN implementation.') +end diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/spec/addressable/net_http_compat_spec.rb b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/spec/addressable/net_http_compat_spec.rb new file mode 100644 index 0000000..d07a43e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/spec/addressable/net_http_compat_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +# Copyright (C) Bob Aman +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +require "spec_helper" + +require "addressable/uri" +require "net/http" + +describe Net::HTTP do + it "should be compatible with Addressable" do + response_body = + Net::HTTP.get(Addressable::URI.parse('http://www.google.com/')) + expect(response_body).not_to be_nil + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/spec/addressable/security_spec.rb b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/spec/addressable/security_spec.rb new file mode 100644 index 0000000..3bf90a2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/spec/addressable/security_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +# Copyright (C) Bob Aman +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +require "spec_helper" + +require "addressable/uri" + +describe Addressable::URI, "when created with a URI known to cause crashes " + + "in certain browsers" do + it "should parse correctly" do + uri = Addressable::URI.parse('%%30%30') + expect(uri.path).to eq('%%30%30') + expect(uri.normalize.path).to eq('%2500') + end + + it "should parse correctly as a full URI" do + uri = Addressable::URI.parse('http://www.example.com/%%30%30') + expect(uri.path).to eq('/%%30%30') + expect(uri.normalize.path).to eq('/%2500') + end +end + +describe Addressable::URI, "when created with a URI known to cause crashes " + + "in certain browsers" do + it "should parse correctly" do + uri = Addressable::URI.parse('لُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ 冗') + expect(uri.path).to eq('لُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ 冗') + expect(uri.normalize.path).to eq( + '%D9%84%D9%8F%D8%B5%D9%91%D8%A8%D9%8F%D9%84%D9%8F%D9%84%D8%B5%D9%91' + + '%D8%A8%D9%8F%D8%B1%D8%B1%D9%8B%20%E0%A5%A3%20%E0%A5%A3h%20%E0%A5' + + '%A3%20%E0%A5%A3%20%E5%86%97' + ) + end + + it "should parse correctly as a full URI" do + uri = Addressable::URI.parse('http://www.example.com/لُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ 冗') + expect(uri.path).to eq('/لُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ 冗') + expect(uri.normalize.path).to eq( + '/%D9%84%D9%8F%D8%B5%D9%91%D8%A8%D9%8F%D9%84%D9%8F%D9%84%D8%B5%D9%91' + + '%D8%A8%D9%8F%D8%B1%D8%B1%D9%8B%20%E0%A5%A3%20%E0%A5%A3h%20%E0%A5' + + '%A3%20%E0%A5%A3%20%E5%86%97' + ) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/spec/addressable/template_spec.rb b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/spec/addressable/template_spec.rb new file mode 100644 index 0000000..f7b0994 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/spec/addressable/template_spec.rb @@ -0,0 +1,1468 @@ +# frozen_string_literal: true + +# Copyright (C) Bob Aman +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +require "spec_helper" + +require "bigdecimal" +require "timeout" +require "addressable/template" + +shared_examples_for 'expands' do |tests| + tests.each do |template, expansion| + exp = expansion.is_a?(Array) ? expansion.first : expansion + it "#{template} to #{exp}" do + tmpl = Addressable::Template.new(template).expand(subject) + if expansion.is_a?(Array) + expect(expansion.any?{|i| i == tmpl.to_str}).to be true + else + expect(tmpl.to_str).to eq(expansion) + end + end + end +end + +describe "eql?" do + let(:template) { Addressable::Template.new('https://www.example.com/{foo}') } + it 'is equal when the pattern matches' do + other_template = Addressable::Template.new('https://www.example.com/{foo}') + expect(template).to be_eql(other_template) + expect(other_template).to be_eql(template) + end + it 'is not equal when the pattern differs' do + other_template = Addressable::Template.new('https://www.example.com/{bar}') + expect(template).to_not be_eql(other_template) + expect(other_template).to_not be_eql(template) + end + it 'is not equal to non-templates' do + uri = 'https://www.example.com/foo/bar' + addressable_template = Addressable::Template.new uri + addressable_uri = Addressable::URI.parse uri + expect(addressable_template).to_not be_eql(addressable_uri) + expect(addressable_uri).to_not be_eql(addressable_template) + end +end + +describe "==" do + let(:template) { Addressable::Template.new('https://www.example.com/{foo}') } + it 'is equal when the pattern matches' do + other_template = Addressable::Template.new('https://www.example.com/{foo}') + expect(template).to eq other_template + expect(other_template).to eq template + end + it 'is not equal when the pattern differs' do + other_template = Addressable::Template.new('https://www.example.com/{bar}') + expect(template).not_to eq other_template + expect(other_template).not_to eq template + end + it 'is not equal to non-templates' do + uri = 'https://www.example.com/foo/bar' + addressable_template = Addressable::Template.new uri + addressable_uri = Addressable::URI.parse uri + expect(addressable_template).not_to eq addressable_uri + expect(addressable_uri).not_to eq addressable_template + end +end + +describe "#to_regexp" do + it "does not match the first line of multiline strings" do + uri = "https://www.example.com/bar" + template = Addressable::Template.new(uri) + expect(template.match(uri)).not_to be_nil + expect(template.match("#{uri}\ngarbage")).to be_nil + end +end + +describe "Type conversion" do + subject { + { + :var => true, + :hello => 1234, + :nothing => nil, + :sym => :symbolic, + :decimal => BigDecimal('1') + } + } + + it_behaves_like 'expands', { + '{var}' => 'true', + '{hello}' => '1234', + '{nothing}' => '', + '{sym}' => 'symbolic', + '{decimal}' => RUBY_VERSION < '2.4.0' ? '0.1E1' : '0.1e1' + } +end + +describe "Level 1:" do + subject { + {:var => "value", :hello => "Hello World!"} + } + it_behaves_like 'expands', { + '{var}' => 'value', + '{hello}' => 'Hello%20World%21' + } +end + +describe "Level 2" do + subject { + { + :var => "value", + :hello => "Hello World!", + :path => "/foo/bar" + } + } + context "Operator +:" do + it_behaves_like 'expands', { + '{+var}' => 'value', + '{+hello}' => 'Hello%20World!', + '{+path}/here' => '/foo/bar/here', + 'here?ref={+path}' => 'here?ref=/foo/bar' + } + end + context "Operator #:" do + it_behaves_like 'expands', { + 'X{#var}' => 'X#value', + 'X{#hello}' => 'X#Hello%20World!' + } + end +end + +describe "Level 3" do + subject { + { + :var => "value", + :hello => "Hello World!", + :empty => "", + :path => "/foo/bar", + :x => "1024", + :y => "768" + } + } + context "Operator nil (multiple vars):" do + it_behaves_like 'expands', { + 'map?{x,y}' => 'map?1024,768', + '{x,hello,y}' => '1024,Hello%20World%21,768' + } + end + context "Operator + (multiple vars):" do + it_behaves_like 'expands', { + '{+x,hello,y}' => '1024,Hello%20World!,768', + '{+path,x}/here' => '/foo/bar,1024/here' + } + end + context "Operator # (multiple vars):" do + it_behaves_like 'expands', { + '{#x,hello,y}' => '#1024,Hello%20World!,768', + '{#path,x}/here' => '#/foo/bar,1024/here' + } + end + context "Operator ." do + it_behaves_like 'expands', { + 'X{.var}' => 'X.value', + 'X{.x,y}' => 'X.1024.768' + } + end + context "Operator /" do + it_behaves_like 'expands', { + '{/var}' => '/value', + '{/var,x}/here' => '/value/1024/here' + } + end + context "Operator ;" do + it_behaves_like 'expands', { + '{;x,y}' => ';x=1024;y=768', + '{;x,y,empty}' => ';x=1024;y=768;empty' + } + end + context "Operator ?" do + it_behaves_like 'expands', { + '{?x,y}' => '?x=1024&y=768', + '{?x,y,empty}' => '?x=1024&y=768&empty=' + } + end + context "Operator &" do + it_behaves_like 'expands', { + '?fixed=yes{&x}' => '?fixed=yes&x=1024', + '{&x,y,empty}' => '&x=1024&y=768&empty=' + } + end +end + +describe "Level 4" do + subject { + { + :var => "value", + :hello => "Hello World!", + :path => "/foo/bar", + :semi => ";", + :list => %w(red green blue), + :keys => {"semi" => ';', "dot" => '.', "comma" => ','} + } + } + context "Expansion with value modifiers" do + it_behaves_like 'expands', { + '{var:3}' => 'val', + '{var:30}' => 'value', + '{list}' => 'red,green,blue', + '{list*}' => 'red,green,blue', + '{keys}' => [ + 'semi,%3B,dot,.,comma,%2C', + 'dot,.,semi,%3B,comma,%2C', + 'comma,%2C,semi,%3B,dot,.', + 'semi,%3B,comma,%2C,dot,.', + 'dot,.,comma,%2C,semi,%3B', + 'comma,%2C,dot,.,semi,%3B' + ], + '{keys*}' => [ + 'semi=%3B,dot=.,comma=%2C', + 'dot=.,semi=%3B,comma=%2C', + 'comma=%2C,semi=%3B,dot=.', + 'semi=%3B,comma=%2C,dot=.', + 'dot=.,comma=%2C,semi=%3B', + 'comma=%2C,dot=.,semi=%3B' + ] + } + end + context "Operator + with value modifiers" do + it_behaves_like 'expands', { + '{+path:6}/here' => '/foo/b/here', + '{+list}' => 'red,green,blue', + '{+list*}' => 'red,green,blue', + '{+keys}' => [ + 'semi,;,dot,.,comma,,', + 'dot,.,semi,;,comma,,', + 'comma,,,semi,;,dot,.', + 'semi,;,comma,,,dot,.', + 'dot,.,comma,,,semi,;', + 'comma,,,dot,.,semi,;' + ], + '{+keys*}' => [ + 'semi=;,dot=.,comma=,', + 'dot=.,semi=;,comma=,', + 'comma=,,semi=;,dot=.', + 'semi=;,comma=,,dot=.', + 'dot=.,comma=,,semi=;', + 'comma=,,dot=.,semi=;' + ] + } + end + context "Operator # with value modifiers" do + it_behaves_like 'expands', { + '{#path:6}/here' => '#/foo/b/here', + '{#list}' => '#red,green,blue', + '{#list*}' => '#red,green,blue', + '{#keys}' => [ + '#semi,;,dot,.,comma,,', + '#dot,.,semi,;,comma,,', + '#comma,,,semi,;,dot,.', + '#semi,;,comma,,,dot,.', + '#dot,.,comma,,,semi,;', + '#comma,,,dot,.,semi,;' + ], + '{#keys*}' => [ + '#semi=;,dot=.,comma=,', + '#dot=.,semi=;,comma=,', + '#comma=,,semi=;,dot=.', + '#semi=;,comma=,,dot=.', + '#dot=.,comma=,,semi=;', + '#comma=,,dot=.,semi=;' + ] + } + end + context "Operator . with value modifiers" do + it_behaves_like 'expands', { + 'X{.var:3}' => 'X.val', + 'X{.list}' => 'X.red,green,blue', + 'X{.list*}' => 'X.red.green.blue', + 'X{.keys}' => [ + 'X.semi,%3B,dot,.,comma,%2C', + 'X.dot,.,semi,%3B,comma,%2C', + 'X.comma,%2C,semi,%3B,dot,.', + 'X.semi,%3B,comma,%2C,dot,.', + 'X.dot,.,comma,%2C,semi,%3B', + 'X.comma,%2C,dot,.,semi,%3B' + ], + 'X{.keys*}' => [ + 'X.semi=%3B.dot=..comma=%2C', + 'X.dot=..semi=%3B.comma=%2C', + 'X.comma=%2C.semi=%3B.dot=.', + 'X.semi=%3B.comma=%2C.dot=.', + 'X.dot=..comma=%2C.semi=%3B', + 'X.comma=%2C.dot=..semi=%3B' + ] + } + end + context "Operator / with value modifiers" do + it_behaves_like 'expands', { + '{/var:1,var}' => '/v/value', + '{/list}' => '/red,green,blue', + '{/list*}' => '/red/green/blue', + '{/list*,path:4}' => '/red/green/blue/%2Ffoo', + '{/keys}' => [ + '/semi,%3B,dot,.,comma,%2C', + '/dot,.,semi,%3B,comma,%2C', + '/comma,%2C,semi,%3B,dot,.', + '/semi,%3B,comma,%2C,dot,.', + '/dot,.,comma,%2C,semi,%3B', + '/comma,%2C,dot,.,semi,%3B' + ], + '{/keys*}' => [ + '/semi=%3B/dot=./comma=%2C', + '/dot=./semi=%3B/comma=%2C', + '/comma=%2C/semi=%3B/dot=.', + '/semi=%3B/comma=%2C/dot=.', + '/dot=./comma=%2C/semi=%3B', + '/comma=%2C/dot=./semi=%3B' + ] + } + end + context "Operator ; with value modifiers" do + it_behaves_like 'expands', { + '{;hello:5}' => ';hello=Hello', + '{;list}' => ';list=red,green,blue', + '{;list*}' => ';list=red;list=green;list=blue', + '{;keys}' => [ + ';keys=semi,%3B,dot,.,comma,%2C', + ';keys=dot,.,semi,%3B,comma,%2C', + ';keys=comma,%2C,semi,%3B,dot,.', + ';keys=semi,%3B,comma,%2C,dot,.', + ';keys=dot,.,comma,%2C,semi,%3B', + ';keys=comma,%2C,dot,.,semi,%3B' + ], + '{;keys*}' => [ + ';semi=%3B;dot=.;comma=%2C', + ';dot=.;semi=%3B;comma=%2C', + ';comma=%2C;semi=%3B;dot=.', + ';semi=%3B;comma=%2C;dot=.', + ';dot=.;comma=%2C;semi=%3B', + ';comma=%2C;dot=.;semi=%3B' + ] + } + end + context "Operator ? with value modifiers" do + it_behaves_like 'expands', { + '{?var:3}' => '?var=val', + '{?list}' => '?list=red,green,blue', + '{?list*}' => '?list=red&list=green&list=blue', + '{?keys}' => [ + '?keys=semi,%3B,dot,.,comma,%2C', + '?keys=dot,.,semi,%3B,comma,%2C', + '?keys=comma,%2C,semi,%3B,dot,.', + '?keys=semi,%3B,comma,%2C,dot,.', + '?keys=dot,.,comma,%2C,semi,%3B', + '?keys=comma,%2C,dot,.,semi,%3B' + ], + '{?keys*}' => [ + '?semi=%3B&dot=.&comma=%2C', + '?dot=.&semi=%3B&comma=%2C', + '?comma=%2C&semi=%3B&dot=.', + '?semi=%3B&comma=%2C&dot=.', + '?dot=.&comma=%2C&semi=%3B', + '?comma=%2C&dot=.&semi=%3B' + ] + } + end + context "Operator & with value modifiers" do + it_behaves_like 'expands', { + '{&var:3}' => '&var=val', + '{&list}' => '&list=red,green,blue', + '{&list*}' => '&list=red&list=green&list=blue', + '{&keys}' => [ + '&keys=semi,%3B,dot,.,comma,%2C', + '&keys=dot,.,semi,%3B,comma,%2C', + '&keys=comma,%2C,semi,%3B,dot,.', + '&keys=semi,%3B,comma,%2C,dot,.', + '&keys=dot,.,comma,%2C,semi,%3B', + '&keys=comma,%2C,dot,.,semi,%3B' + ], + '{&keys*}' => [ + '&semi=%3B&dot=.&comma=%2C', + '&dot=.&semi=%3B&comma=%2C', + '&comma=%2C&semi=%3B&dot=.', + '&semi=%3B&comma=%2C&dot=.', + '&dot=.&comma=%2C&semi=%3B', + '&comma=%2C&dot=.&semi=%3B' + ] + } + end +end +describe "Modifiers" do + subject { + { + :var => "value", + :semi => ";", + :year => %w(1965 2000 2012), + :dom => %w(example com) + } + } + context "length" do + it_behaves_like 'expands', { + '{var:3}' => 'val', + '{var:30}' => 'value', + '{var}' => 'value', + '{semi}' => '%3B', + '{semi:2}' => '%3B' + } + end + context "explode" do + it_behaves_like 'expands', { + 'find{?year*}' => 'find?year=1965&year=2000&year=2012', + 'www{.dom*}' => 'www.example.com', + } + end +end +describe "Expansion" do + subject { + { + :count => ["one", "two", "three"], + :dom => ["example", "com"], + :dub => "me/too", + :hello => "Hello World!", + :half => "50%", + :var => "value", + :who => "fred", + :base => "http://example.com/home/", + :path => "/foo/bar", + :list => ["red", "green", "blue"], + :keys => {"semi" => ";","dot" => ".","comma" => ","}, + :v => "6", + :x => "1024", + :y => "768", + :empty => "", + :empty_keys => {}, + :undef => nil + } + } + context "concatenation" do + it_behaves_like 'expands', { + '{count}' => 'one,two,three', + '{count*}' => 'one,two,three', + '{/count}' => '/one,two,three', + '{/count*}' => '/one/two/three', + '{;count}' => ';count=one,two,three', + '{;count*}' => ';count=one;count=two;count=three', + '{?count}' => '?count=one,two,three', + '{?count*}' => '?count=one&count=two&count=three', + '{&count*}' => '&count=one&count=two&count=three' + } + end + context "simple expansion" do + it_behaves_like 'expands', { + '{var}' => 'value', + '{hello}' => 'Hello%20World%21', + '{half}' => '50%25', + 'O{empty}X' => 'OX', + 'O{undef}X' => 'OX', + '{x,y}' => '1024,768', + '{x,hello,y}' => '1024,Hello%20World%21,768', + '?{x,empty}' => '?1024,', + '?{x,undef}' => '?1024', + '?{undef,y}' => '?768', + '{var:3}' => 'val', + '{var:30}' => 'value', + '{list}' => 'red,green,blue', + '{list*}' => 'red,green,blue', + '{keys}' => [ + 'semi,%3B,dot,.,comma,%2C', + 'dot,.,semi,%3B,comma,%2C', + 'comma,%2C,semi,%3B,dot,.', + 'semi,%3B,comma,%2C,dot,.', + 'dot,.,comma,%2C,semi,%3B', + 'comma,%2C,dot,.,semi,%3B' + ], + '{keys*}' => [ + 'semi=%3B,dot=.,comma=%2C', + 'dot=.,semi=%3B,comma=%2C', + 'comma=%2C,semi=%3B,dot=.', + 'semi=%3B,comma=%2C,dot=.', + 'dot=.,comma=%2C,semi=%3B', + 'comma=%2C,dot=.,semi=%3B' + ] + } + end + context "reserved expansion (+)" do + it_behaves_like 'expands', { + '{+var}' => 'value', + '{+hello}' => 'Hello%20World!', + '{+half}' => '50%25', + '{base}index' => 'http%3A%2F%2Fexample.com%2Fhome%2Findex', + '{+base}index' => 'http://example.com/home/index', + 'O{+empty}X' => 'OX', + 'O{+undef}X' => 'OX', + '{+path}/here' => '/foo/bar/here', + 'here?ref={+path}' => 'here?ref=/foo/bar', + 'up{+path}{var}/here' => 'up/foo/barvalue/here', + '{+x,hello,y}' => '1024,Hello%20World!,768', + '{+path,x}/here' => '/foo/bar,1024/here', + '{+path:6}/here' => '/foo/b/here', + '{+list}' => 'red,green,blue', + '{+list*}' => 'red,green,blue', + '{+keys}' => [ + 'semi,;,dot,.,comma,,', + 'dot,.,semi,;,comma,,', + 'comma,,,semi,;,dot,.', + 'semi,;,comma,,,dot,.', + 'dot,.,comma,,,semi,;', + 'comma,,,dot,.,semi,;' + ], + '{+keys*}' => [ + 'semi=;,dot=.,comma=,', + 'dot=.,semi=;,comma=,', + 'comma=,,semi=;,dot=.', + 'semi=;,comma=,,dot=.', + 'dot=.,comma=,,semi=;', + 'comma=,,dot=.,semi=;' + ] + } + end + context "fragment expansion (#)" do + it_behaves_like 'expands', { + '{#var}' => '#value', + '{#hello}' => '#Hello%20World!', + '{#half}' => '#50%25', + 'foo{#empty}' => 'foo#', + 'foo{#undef}' => 'foo', + '{#x,hello,y}' => '#1024,Hello%20World!,768', + '{#path,x}/here' => '#/foo/bar,1024/here', + '{#path:6}/here' => '#/foo/b/here', + '{#list}' => '#red,green,blue', + '{#list*}' => '#red,green,blue', + '{#keys}' => [ + '#semi,;,dot,.,comma,,', + '#dot,.,semi,;,comma,,', + '#comma,,,semi,;,dot,.', + '#semi,;,comma,,,dot,.', + '#dot,.,comma,,,semi,;', + '#comma,,,dot,.,semi,;' + ], + '{#keys*}' => [ + '#semi=;,dot=.,comma=,', + '#dot=.,semi=;,comma=,', + '#comma=,,semi=;,dot=.', + '#semi=;,comma=,,dot=.', + '#dot=.,comma=,,semi=;', + '#comma=,,dot=.,semi=;' + ] + } + end + context "label expansion (.)" do + it_behaves_like 'expands', { + '{.who}' => '.fred', + '{.who,who}' => '.fred.fred', + '{.half,who}' => '.50%25.fred', + 'www{.dom*}' => 'www.example.com', + 'X{.var}' => 'X.value', + 'X{.empty}' => 'X.', + 'X{.undef}' => 'X', + 'X{.var:3}' => 'X.val', + 'X{.list}' => 'X.red,green,blue', + 'X{.list*}' => 'X.red.green.blue', + 'X{.keys}' => [ + 'X.semi,%3B,dot,.,comma,%2C', + 'X.dot,.,semi,%3B,comma,%2C', + 'X.comma,%2C,semi,%3B,dot,.', + 'X.semi,%3B,comma,%2C,dot,.', + 'X.dot,.,comma,%2C,semi,%3B', + 'X.comma,%2C,dot,.,semi,%3B' + ], + 'X{.keys*}' => [ + 'X.semi=%3B.dot=..comma=%2C', + 'X.dot=..semi=%3B.comma=%2C', + 'X.comma=%2C.semi=%3B.dot=.', + 'X.semi=%3B.comma=%2C.dot=.', + 'X.dot=..comma=%2C.semi=%3B', + 'X.comma=%2C.dot=..semi=%3B' + ], + 'X{.empty_keys}' => 'X', + 'X{.empty_keys*}' => 'X' + } + end + context "path expansion (/)" do + it_behaves_like 'expands', { + '{/who}' => '/fred', + '{/who,who}' => '/fred/fred', + '{/half,who}' => '/50%25/fred', + '{/who,dub}' => '/fred/me%2Ftoo', + '{/var}' => '/value', + '{/var,empty}' => '/value/', + '{/var,undef}' => '/value', + '{/var,x}/here' => '/value/1024/here', + '{/var:1,var}' => '/v/value', + '{/list}' => '/red,green,blue', + '{/list*}' => '/red/green/blue', + '{/list*,path:4}' => '/red/green/blue/%2Ffoo', + '{/keys}' => [ + '/semi,%3B,dot,.,comma,%2C', + '/dot,.,semi,%3B,comma,%2C', + '/comma,%2C,semi,%3B,dot,.', + '/semi,%3B,comma,%2C,dot,.', + '/dot,.,comma,%2C,semi,%3B', + '/comma,%2C,dot,.,semi,%3B' + ], + '{/keys*}' => [ + '/semi=%3B/dot=./comma=%2C', + '/dot=./semi=%3B/comma=%2C', + '/comma=%2C/semi=%3B/dot=.', + '/semi=%3B/comma=%2C/dot=.', + '/dot=./comma=%2C/semi=%3B', + '/comma=%2C/dot=./semi=%3B' + ] + } + end + context "path-style expansion (;)" do + it_behaves_like 'expands', { + '{;who}' => ';who=fred', + '{;half}' => ';half=50%25', + '{;empty}' => ';empty', + '{;v,empty,who}' => ';v=6;empty;who=fred', + '{;v,bar,who}' => ';v=6;who=fred', + '{;x,y}' => ';x=1024;y=768', + '{;x,y,empty}' => ';x=1024;y=768;empty', + '{;x,y,undef}' => ';x=1024;y=768', + '{;hello:5}' => ';hello=Hello', + '{;list}' => ';list=red,green,blue', + '{;list*}' => ';list=red;list=green;list=blue', + '{;keys}' => [ + ';keys=semi,%3B,dot,.,comma,%2C', + ';keys=dot,.,semi,%3B,comma,%2C', + ';keys=comma,%2C,semi,%3B,dot,.', + ';keys=semi,%3B,comma,%2C,dot,.', + ';keys=dot,.,comma,%2C,semi,%3B', + ';keys=comma,%2C,dot,.,semi,%3B' + ], + '{;keys*}' => [ + ';semi=%3B;dot=.;comma=%2C', + ';dot=.;semi=%3B;comma=%2C', + ';comma=%2C;semi=%3B;dot=.', + ';semi=%3B;comma=%2C;dot=.', + ';dot=.;comma=%2C;semi=%3B', + ';comma=%2C;dot=.;semi=%3B' + ] + } + end + context "form query expansion (?)" do + it_behaves_like 'expands', { + '{?who}' => '?who=fred', + '{?half}' => '?half=50%25', + '{?x,y}' => '?x=1024&y=768', + '{?x,y,empty}' => '?x=1024&y=768&empty=', + '{?x,y,undef}' => '?x=1024&y=768', + '{?var:3}' => '?var=val', + '{?list}' => '?list=red,green,blue', + '{?list*}' => '?list=red&list=green&list=blue', + '{?keys}' => [ + '?keys=semi,%3B,dot,.,comma,%2C', + '?keys=dot,.,semi,%3B,comma,%2C', + '?keys=comma,%2C,semi,%3B,dot,.', + '?keys=semi,%3B,comma,%2C,dot,.', + '?keys=dot,.,comma,%2C,semi,%3B', + '?keys=comma,%2C,dot,.,semi,%3B' + ], + '{?keys*}' => [ + '?semi=%3B&dot=.&comma=%2C', + '?dot=.&semi=%3B&comma=%2C', + '?comma=%2C&semi=%3B&dot=.', + '?semi=%3B&comma=%2C&dot=.', + '?dot=.&comma=%2C&semi=%3B', + '?comma=%2C&dot=.&semi=%3B' + ] + } + end + context "form query expansion (&)" do + it_behaves_like 'expands', { + '{&who}' => '&who=fred', + '{&half}' => '&half=50%25', + '?fixed=yes{&x}' => '?fixed=yes&x=1024', + '{&x,y,empty}' => '&x=1024&y=768&empty=', + '{&x,y,undef}' => '&x=1024&y=768', + '{&var:3}' => '&var=val', + '{&list}' => '&list=red,green,blue', + '{&list*}' => '&list=red&list=green&list=blue', + '{&keys}' => [ + '&keys=semi,%3B,dot,.,comma,%2C', + '&keys=dot,.,semi,%3B,comma,%2C', + '&keys=comma,%2C,semi,%3B,dot,.', + '&keys=semi,%3B,comma,%2C,dot,.', + '&keys=dot,.,comma,%2C,semi,%3B', + '&keys=comma,%2C,dot,.,semi,%3B' + ], + '{&keys*}' => [ + '&semi=%3B&dot=.&comma=%2C', + '&dot=.&semi=%3B&comma=%2C', + '&comma=%2C&semi=%3B&dot=.', + '&semi=%3B&comma=%2C&dot=.', + '&dot=.&comma=%2C&semi=%3B', + '&comma=%2C&dot=.&semi=%3B' + ] + } + end + context "non-string key in match data" do + subject {Addressable::Template.new("http://example.com/{one}")} + + it "raises TypeError" do + expect { subject.expand(Object.new => "1") }.to raise_error TypeError + end + end +end + +class ExampleTwoProcessor + def self.restore(name, value) + return value.gsub(/-/, " ") if name == "query" + return value + end + + def self.match(name) + return ".*?" if name == "first" + return ".*" + end + def self.validate(name, value) + return !!(value =~ /^[\w ]+$/) if name == "query" + return true + end + + def self.transform(name, value) + return value.gsub(/ /, "+") if name == "query" + return value + end +end + +class DumbProcessor + def self.match(name) + return ".*?" if name == "first" + end +end + +describe Addressable::Template do + describe 'initialize' do + context 'with a non-string' do + it 'raises a TypeError' do + expect { Addressable::Template.new(nil) }.to raise_error(TypeError) + end + end + end + + describe 'freeze' do + subject { Addressable::Template.new("http://example.com/{first}/{+second}/") } + it 'freezes the template' do + expect(subject.freeze).to be_frozen + end + end + + describe "Matching" do + let(:uri){ + Addressable::URI.parse( + "http://example.com/search/an-example-search-query/" + ) + } + let(:uri2){ + Addressable::URI.parse("http://example.com/a/b/c/") + } + let(:uri3){ + Addressable::URI.parse("http://example.com/;a=1;b=2;c=3;first=foo") + } + let(:uri4){ + Addressable::URI.parse("http://example.com/?a=1&b=2&c=3&first=foo") + } + let(:uri5){ + "http://example.com/foo" + } + context "first uri with ExampleTwoProcessor" do + subject { + Addressable::Template.new( + "http://example.com/search/{query}/" + ).match(uri, ExampleTwoProcessor) + } + its(:variables){ should == ["query"] } + its(:captures){ should == ["an example search query"] } + end + + context "second uri with ExampleTwoProcessor" do + subject { + Addressable::Template.new( + "http://example.com/{first}/{+second}/" + ).match(uri2, ExampleTwoProcessor) + } + its(:variables){ should == ["first", "second"] } + its(:captures){ should == ["a", "b/c"] } + end + + context "second uri with DumbProcessor" do + subject { + Addressable::Template.new( + "http://example.com/{first}/{+second}/" + ).match(uri2, DumbProcessor) + } + its(:variables){ should == ["first", "second"] } + its(:captures){ should == ["a", "b/c"] } + end + + context "second uri" do + subject { + Addressable::Template.new( + "http://example.com/{first}{/second*}/" + ).match(uri2) + } + its(:variables){ should == ["first", "second"] } + its(:captures){ should == ["a", ["b","c"]] } + end + context "third uri" do + subject { + Addressable::Template.new( + "http://example.com/{;hash*,first}" + ).match(uri3) + } + its(:variables){ should == ["hash", "first"] } + its(:captures){ should == [ + {"a" => "1", "b" => "2", "c" => "3", "first" => "foo"}, nil] } + end + # Note that this expansion is impossible to revert deterministically - the + # * operator means first could have been a key of hash or a separate key. + # Semantically, a separate key is more likely, but both are possible. + context "fourth uri" do + subject { + Addressable::Template.new( + "http://example.com/{?hash*,first}" + ).match(uri4) + } + its(:variables){ should == ["hash", "first"] } + its(:captures){ should == [ + {"a" => "1", "b" => "2", "c" => "3", "first"=> "foo"}, nil] } + end + context "fifth uri" do + subject { + Addressable::Template.new( + "http://example.com/{path}{?hash*,first}" + ).match(uri5) + } + its(:variables){ should == ["path", "hash", "first"] } + its(:captures){ should == ["foo", nil, nil] } + end + end + + describe 'match' do + subject { Addressable::Template.new('http://example.com/first/second/') } + context 'when the URI is the same as the template' do + it 'returns the match data itself with an empty mapping' do + uri = Addressable::URI.parse('http://example.com/first/second/') + match_data = subject.match(uri) + expect(match_data).to be_an Addressable::Template::MatchData + expect(match_data.uri).to eq(uri) + expect(match_data.template).to eq(subject) + expect(match_data.mapping).to be_empty + expect(match_data.inspect).to be_an String + end + end + end + + describe "extract" do + let(:template) { + Addressable::Template.new( + "http://{host}{/segments*}/{?one,two,bogus}{#fragment}" + ) + } + let(:uri){ "http://example.com/a/b/c/?one=1&two=2#foo" } + let(:uri2){ "http://example.com/a/b/c/#foo" } + it "should be able to extract with queries" do + expect(template.extract(uri)).to eq({ + "host" => "example.com", + "segments" => %w(a b c), + "one" => "1", + "bogus" => nil, + "two" => "2", + "fragment" => "foo" + }) + end + it "should be able to extract without queries" do + expect(template.extract(uri2)).to eq({ + "host" => "example.com", + "segments" => %w(a b c), + "one" => nil, + "bogus" => nil, + "two" => nil, + "fragment" => "foo" + }) + end + + context "issue #137" do + subject { Addressable::Template.new('/path{?page,per_page}') } + + it "can match empty" do + data = subject.extract("/path") + expect(data["page"]).to eq(nil) + expect(data["per_page"]).to eq(nil) + expect(data.keys.sort).to eq(['page', 'per_page']) + end + + it "can match first var" do + data = subject.extract("/path?page=1") + expect(data["page"]).to eq("1") + expect(data["per_page"]).to eq(nil) + expect(data.keys.sort).to eq(['page', 'per_page']) + end + + it "can match second var" do + data = subject.extract("/path?per_page=1") + expect(data["page"]).to eq(nil) + expect(data["per_page"]).to eq("1") + expect(data.keys.sort).to eq(['page', 'per_page']) + end + + it "can match both vars" do + data = subject.extract("/path?page=2&per_page=1") + expect(data["page"]).to eq("2") + expect(data["per_page"]).to eq("1") + expect(data.keys.sort).to eq(['page', 'per_page']) + end + end + end + + describe "Partial expand with symbols" do + context "partial_expand with two simple values" do + subject { + Addressable::Template.new("http://example.com/{one}/{two}/") + } + it "builds a new pattern" do + expect(subject.partial_expand(:one => "1").pattern).to eq( + "http://example.com/1/{two}/" + ) + end + end + context "partial_expand query with missing param in middle" do + subject { + Addressable::Template.new("http://example.com/{?one,two,three}/") + } + it "builds a new pattern" do + expect(subject.partial_expand(:one => "1", :three => "3").pattern).to eq( + "http://example.com/?one=1{&two}&three=3/" + ) + end + end + context "partial_expand form style query with missing param at beginning" do + subject { + Addressable::Template.new("http://example.com/{?one,two}/") + } + it "builds a new pattern" do + expect(subject.partial_expand(:two => "2").pattern).to eq( + "http://example.com/?two=2{&one}/" + ) + end + end + context "issue #307 - partial_expand form query with nil params" do + subject do + Addressable::Template.new("http://example.com/{?one,two,three}/") + end + it "builds a new pattern with two=nil" do + expect(subject.partial_expand(two: nil).pattern).to eq( + "http://example.com/{?one}{&three}/" + ) + end + it "builds a new pattern with one=nil and two=nil" do + expect(subject.partial_expand(one: nil, two: nil).pattern).to eq( + "http://example.com/{?three}/" + ) + end + it "builds a new pattern with one=1 and two=nil" do + expect(subject.partial_expand(one: 1, two: nil).pattern).to eq( + "http://example.com/?one=1{&three}/" + ) + end + it "builds a new pattern with one=nil and two=2" do + expect(subject.partial_expand(one: nil, two: 2).pattern).to eq( + "http://example.com/?two=2{&three}/" + ) + end + it "builds a new pattern with one=nil" do + expect(subject.partial_expand(one: nil).pattern).to eq( + "http://example.com/{?two}{&three}/" + ) + end + end + context "partial_expand with query string" do + subject { + Addressable::Template.new("http://example.com/{?two,one}/") + } + it "builds a new pattern" do + expect(subject.partial_expand(:one => "1").pattern).to eq( + "http://example.com/?one=1{&two}/" + ) + end + end + context "partial_expand with path operator" do + subject { + Addressable::Template.new("http://example.com{/one,two}/") + } + it "builds a new pattern" do + expect(subject.partial_expand(:one => "1").pattern).to eq( + "http://example.com/1{/two}/" + ) + end + end + context "partial expand with unicode values" do + subject do + Addressable::Template.new("http://example.com/{resource}/{query}/") + end + it "normalizes unicode by default" do + template = subject.partial_expand("query" => "Cafe\u0301") + expect(template.pattern).to eq( + "http://example.com/{resource}/Caf%C3%A9/" + ) + end + + it "does not normalize unicode when byte semantics requested" do + template = subject.partial_expand({"query" => "Cafe\u0301"}, nil, false) + expect(template.pattern).to eq( + "http://example.com/{resource}/Cafe%CC%81/" + ) + end + end + end + describe "Partial expand with strings" do + context "partial_expand with two simple values" do + subject { + Addressable::Template.new("http://example.com/{one}/{two}/") + } + it "builds a new pattern" do + expect(subject.partial_expand("one" => "1").pattern).to eq( + "http://example.com/1/{two}/" + ) + end + end + context "partial_expand query with missing param in middle" do + subject { + Addressable::Template.new("http://example.com/{?one,two,three}/") + } + it "builds a new pattern" do + expect(subject.partial_expand("one" => "1", "three" => "3").pattern).to eq( + "http://example.com/?one=1{&two}&three=3/" + ) + end + end + context "partial_expand with query string" do + subject { + Addressable::Template.new("http://example.com/{?two,one}/") + } + it "builds a new pattern" do + expect(subject.partial_expand("one" => "1").pattern).to eq( + "http://example.com/?one=1{&two}/" + ) + end + end + context "partial_expand with path operator" do + subject { + Addressable::Template.new("http://example.com{/one,two}/") + } + it "builds a new pattern" do + expect(subject.partial_expand("one" => "1").pattern).to eq( + "http://example.com/1{/two}/" + ) + end + end + end + describe "Expand" do + context "expand with unicode values" do + subject do + Addressable::Template.new("http://example.com/search/{query}/") + end + it "normalizes unicode by default" do + uri = subject.expand("query" => "Cafe\u0301").to_str + expect(uri).to eq("http://example.com/search/Caf%C3%A9/") + end + + it "does not normalize unicode when byte semantics requested" do + uri = subject.expand({ "query" => "Cafe\u0301" }, nil, false).to_str + expect(uri).to eq("http://example.com/search/Cafe%CC%81/") + end + end + context "expand with a processor" do + subject { + Addressable::Template.new("http://example.com/search/{query}/") + } + it "processes spaces" do + expect(subject.expand({"query" => "an example search query"}, + ExampleTwoProcessor).to_str).to eq( + "http://example.com/search/an+example+search+query/" + ) + end + it "validates" do + expect{ + subject.expand({"query" => "Bogus!"}, + ExampleTwoProcessor).to_str + }.to raise_error(Addressable::Template::InvalidTemplateValueError) + end + end + context "partial_expand query with missing param in middle" do + subject { + Addressable::Template.new("http://example.com/{?one,two,three}/") + } + it "builds a new pattern" do + expect(subject.partial_expand("one" => "1", "three" => "3").pattern).to eq( + "http://example.com/?one=1{&two}&three=3/" + ) + end + end + context "partial_expand with query string" do + subject { + Addressable::Template.new("http://example.com/{?two,one}/") + } + it "builds a new pattern" do + expect(subject.partial_expand("one" => "1").pattern).to eq( + "http://example.com/?one=1{&two}/" + ) + end + end + context "partial_expand with path operator" do + subject { + Addressable::Template.new("http://example.com{/one,two}/") + } + it "builds a new pattern" do + expect(subject.partial_expand("one" => "1").pattern).to eq( + "http://example.com/1{/two}/" + ) + end + end + end + context "Matching with operators" do + describe "Level 1:" do + subject { Addressable::Template.new("foo{foo}/{bar}baz") } + it "can match" do + data = subject.match("foofoo/bananabaz") + expect(data.mapping["foo"]).to eq("foo") + expect(data.mapping["bar"]).to eq("banana") + end + it "can fail" do + expect(subject.match("bar/foo")).to be_nil + expect(subject.match("foobaz")).to be_nil + end + it "can match empty" do + data = subject.match("foo/baz") + expect(data.mapping["foo"]).to eq(nil) + expect(data.mapping["bar"]).to eq(nil) + end + it "lists vars" do + expect(subject.variables).to eq(["foo", "bar"]) + end + end + + describe "Level 2:" do + subject { Addressable::Template.new("foo{+foo}{#bar}baz") } + it "can match" do + data = subject.match("foo/test/banana#bazbaz") + expect(data.mapping["foo"]).to eq("/test/banana") + expect(data.mapping["bar"]).to eq("baz") + end + it "can match empty level 2 #" do + data = subject.match("foo/test/bananabaz") + expect(data.mapping["foo"]).to eq("/test/banana") + expect(data.mapping["bar"]).to eq(nil) + data = subject.match("foo/test/banana#baz") + expect(data.mapping["foo"]).to eq("/test/banana") + expect(data.mapping["bar"]).to eq("") + end + it "can match empty level 2 +" do + data = subject.match("foobaz") + expect(data.mapping["foo"]).to eq(nil) + expect(data.mapping["bar"]).to eq(nil) + data = subject.match("foo#barbaz") + expect(data.mapping["foo"]).to eq(nil) + expect(data.mapping["bar"]).to eq("bar") + end + it "lists vars" do + expect(subject.variables).to eq(["foo", "bar"]) + end + end + + describe "Level 3:" do + context "no operator" do + subject { Addressable::Template.new("foo{foo,bar}baz") } + it "can match" do + data = subject.match("foofoo,barbaz") + expect(data.mapping["foo"]).to eq("foo") + expect(data.mapping["bar"]).to eq("bar") + end + it "lists vars" do + expect(subject.variables).to eq(["foo", "bar"]) + end + end + context "+ operator" do + subject { Addressable::Template.new("foo{+foo,bar}baz") } + it "can match" do + data = subject.match("foofoo/bar,barbaz") + expect(data.mapping["bar"]).to eq("foo/bar,bar") + expect(data.mapping["foo"]).to eq("") + end + it "lists vars" do + expect(subject.variables).to eq(["foo", "bar"]) + end + end + context ". operator" do + subject { Addressable::Template.new("foo{.foo,bar}baz") } + it "can match" do + data = subject.match("foo.foo.barbaz") + expect(data.mapping["foo"]).to eq("foo") + expect(data.mapping["bar"]).to eq("bar") + end + it "lists vars" do + expect(subject.variables).to eq(["foo", "bar"]) + end + end + context "/ operator" do + subject { Addressable::Template.new("foo{/foo,bar}baz") } + it "can match" do + data = subject.match("foo/foo/barbaz") + expect(data.mapping["foo"]).to eq("foo") + expect(data.mapping["bar"]).to eq("bar") + end + it "lists vars" do + expect(subject.variables).to eq(["foo", "bar"]) + end + end + context "; operator" do + subject { Addressable::Template.new("foo{;foo,bar,baz}baz") } + it "can match" do + data = subject.match("foo;foo=bar%20baz;bar=foo;bazbaz") + expect(data.mapping["foo"]).to eq("bar baz") + expect(data.mapping["bar"]).to eq("foo") + expect(data.mapping["baz"]).to eq("") + end + it "lists vars" do + expect(subject.variables).to eq(%w(foo bar baz)) + end + end + context "? operator" do + context "test" do + subject { Addressable::Template.new("foo{?foo,bar}baz") } + it "can match" do + data = subject.match("foo?foo=bar%20baz&bar=foobaz") + expect(data.mapping["foo"]).to eq("bar baz") + expect(data.mapping["bar"]).to eq("foo") + end + it "lists vars" do + expect(subject.variables).to eq(%w(foo bar)) + end + end + + context "issue #137" do + subject { Addressable::Template.new('/path{?page,per_page}') } + + it "can match empty" do + data = subject.match("/path") + expect(data.mapping["page"]).to eq(nil) + expect(data.mapping["per_page"]).to eq(nil) + expect(data.mapping.keys.sort).to eq(['page', 'per_page']) + end + + it "can match first var" do + data = subject.match("/path?page=1") + expect(data.mapping["page"]).to eq("1") + expect(data.mapping["per_page"]).to eq(nil) + expect(data.mapping.keys.sort).to eq(['page', 'per_page']) + end + + it "can match second var" do + data = subject.match("/path?per_page=1") + expect(data.mapping["page"]).to eq(nil) + expect(data.mapping["per_page"]).to eq("1") + expect(data.mapping.keys.sort).to eq(['page', 'per_page']) + end + + it "can match both vars" do + data = subject.match("/path?page=2&per_page=1") + expect(data.mapping["page"]).to eq("2") + expect(data.mapping["per_page"]).to eq("1") + expect(data.mapping.keys.sort).to eq(['page', 'per_page']) + end + end + + context "issue #71" do + subject { Addressable::Template.new("http://cyberscore.dev/api/users{?username}") } + it "can match" do + data = subject.match("http://cyberscore.dev/api/users?username=foobaz") + expect(data.mapping["username"]).to eq("foobaz") + end + it "lists vars" do + expect(subject.variables).to eq(%w(username)) + expect(subject.keys).to eq(%w(username)) + end + end + end + context "& operator" do + subject { Addressable::Template.new("foo{&foo,bar}baz") } + it "can match" do + data = subject.match("foo&foo=bar%20baz&bar=foobaz") + expect(data.mapping["foo"]).to eq("bar baz") + expect(data.mapping["bar"]).to eq("foo") + end + it "lists vars" do + expect(subject.variables).to eq(%w(foo bar)) + end + end + end + end + + context "support regexes:" do + context "EXPRESSION" do + subject { Addressable::Template::EXPRESSION } + it "should be able to match an expression" do + expect(subject).to match("{foo}") + expect(subject).to match("{foo,9}") + expect(subject).to match("{foo.bar,baz}") + expect(subject).to match("{+foo.bar,baz}") + expect(subject).to match("{foo,foo%20bar}") + expect(subject).to match("{#foo:20,baz*}") + expect(subject).to match("stuff{#foo:20,baz*}things") + end + it "should fail on non vars" do + expect(subject).not_to match("!{foo") + expect(subject).not_to match("{foo.bar.}") + expect(subject).not_to match("!{}") + end + end + context "VARNAME" do + subject { Addressable::Template::VARNAME } + it "should be able to match a variable" do + expect(subject).to match("foo") + expect(subject).to match("9") + expect(subject).to match("foo.bar") + expect(subject).to match("foo_bar") + expect(subject).to match("foo_bar.baz") + expect(subject).to match("foo%20bar") + expect(subject).to match("foo%20bar.baz") + end + it "should fail on non vars" do + expect(subject).not_to match("!foo") + expect(subject).not_to match("foo.bar.") + expect(subject).not_to match("foo%2%00bar") + expect(subject).not_to match("foo_ba%r") + expect(subject).not_to match("foo_bar*") + expect(subject).not_to match("foo_bar:20") + end + + it 'should parse in a reasonable time' do + expect do + Timeout.timeout(0.1) do + expect(subject).not_to match("0"*25 + "!") + end + end.not_to raise_error + end + end + context "VARIABLE_LIST" do + subject { Addressable::Template::VARIABLE_LIST } + it "should be able to match a variable list" do + expect(subject).to match("foo,bar") + expect(subject).to match("foo") + expect(subject).to match("foo,bar*,baz") + expect(subject).to match("foo.bar,bar_baz*,baz:12") + end + it "should fail on non vars" do + expect(subject).not_to match(",foo,bar*,baz") + expect(subject).not_to match("foo,*bar,baz") + expect(subject).not_to match("foo,,bar*,baz") + end + end + context "VARSPEC" do + subject { Addressable::Template::VARSPEC } + it "should be able to match a variable with modifier" do + expect(subject).to match("9:8") + expect(subject).to match("foo.bar*") + expect(subject).to match("foo_bar:12") + expect(subject).to match("foo_bar.baz*") + expect(subject).to match("foo%20bar:12") + expect(subject).to match("foo%20bar.baz*") + end + it "should fail on non vars" do + expect(subject).not_to match("!foo") + expect(subject).not_to match("*foo") + expect(subject).not_to match("fo*o") + expect(subject).not_to match("fo:o") + expect(subject).not_to match("foo:") + end + end + end +end + +describe Addressable::Template::MatchData do + let(:template) { Addressable::Template.new('{foo}/{bar}') } + subject(:its) { template.match('ab/cd') } + its(:uri) { should == Addressable::URI.parse('ab/cd') } + its(:template) { should == template } + its(:mapping) { should == { 'foo' => 'ab', 'bar' => 'cd' } } + its(:variables) { should == ['foo', 'bar'] } + its(:keys) { should == ['foo', 'bar'] } + its(:names) { should == ['foo', 'bar'] } + its(:values) { should == ['ab', 'cd'] } + its(:captures) { should == ['ab', 'cd'] } + its(:to_a) { should == ['ab/cd', 'ab', 'cd'] } + its(:to_s) { should == 'ab/cd' } + its(:string) { should == its.to_s } + its(:pre_match) { should == "" } + its(:post_match) { should == "" } + + describe 'values_at' do + it 'returns an array with the values' do + expect(its.values_at(0, 2)).to eq(['ab/cd', 'cd']) + end + it 'allows mixing integer an string keys' do + expect(its.values_at('foo', 1)).to eq(['ab', 'ab']) + end + it 'accepts unknown keys' do + expect(its.values_at('baz', 'foo')).to eq([nil, 'ab']) + end + end + + describe '[]' do + context 'string key' do + it 'returns the corresponding capture' do + expect(its['foo']).to eq('ab') + expect(its['bar']).to eq('cd') + end + it 'returns nil for unknown keys' do + expect(its['baz']).to be_nil + end + end + context 'symbol key' do + it 'returns the corresponding capture' do + expect(its[:foo]).to eq('ab') + expect(its[:bar]).to eq('cd') + end + it 'returns nil for unknown keys' do + expect(its[:baz]).to be_nil + end + end + context 'integer key' do + it 'returns the full URI for index 0' do + expect(its[0]).to eq('ab/cd') + end + it 'returns the corresponding capture' do + expect(its[1]).to eq('ab') + expect(its[2]).to eq('cd') + end + it 'returns nil for unknown keys' do + expect(its[3]).to be_nil + end + end + context 'other key' do + it 'raises an exception' do + expect { its[Object.new] }.to raise_error(TypeError) + end + end + context 'with length' do + it 'returns an array starting at index with given length' do + expect(its[0, 2]).to eq(['ab/cd', 'ab']) + expect(its[2, 1]).to eq(['cd']) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/spec/addressable/uri_spec.rb b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/spec/addressable/uri_spec.rb new file mode 100644 index 0000000..b1f9541 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/spec/addressable/uri_spec.rb @@ -0,0 +1,6745 @@ +# frozen_string_literal: true + +# Copyright (C) Bob Aman +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +require "spec_helper" + +require "addressable/uri" +require "uri" +require "ipaddr" + +if !"".respond_to?("force_encoding") + class String + def force_encoding(encoding) + @encoding = encoding + end + + def encoding + @encoding ||= Encoding::ASCII_8BIT + end + end + + class Encoding + def initialize(name) + @name = name + end + + def to_s + return @name + end + + UTF_8 = Encoding.new("UTF-8") + ASCII_8BIT = Encoding.new("US-ASCII") + end +end + +module Fake + module URI + class HTTP + def initialize(uri) + @uri = uri + end + + def to_s + return @uri.to_s + end + + alias :to_str :to_s + end + end +end + +describe Addressable::URI, "when created with a non-numeric port number" do + it "should raise an error" do + expect do + Addressable::URI.new(:port => "bogus") + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when created with a invalid encoded port number" do + it "should raise an error" do + expect do + Addressable::URI.new(:port => "%eb") + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when created with a non-string scheme" do + it "should raise an error" do + expect do + Addressable::URI.new(:scheme => :bogus) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when created with a non-string user" do + it "should raise an error" do + expect do + Addressable::URI.new(:user => :bogus) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when created with a non-string password" do + it "should raise an error" do + expect do + Addressable::URI.new(:password => :bogus) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when created with a non-string userinfo" do + it "should raise an error" do + expect do + Addressable::URI.new(:userinfo => :bogus) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when created with a non-string host" do + it "should raise an error" do + expect do + Addressable::URI.new(:host => :bogus) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when created with a non-string authority" do + it "should raise an error" do + expect do + Addressable::URI.new(:authority => :bogus) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when created with a non-string path" do + it "should raise an error" do + expect do + Addressable::URI.new(:path => :bogus) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when created with a non-string query" do + it "should raise an error" do + expect do + Addressable::URI.new(:query => :bogus) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when created with a non-string fragment" do + it "should raise an error" do + expect do + Addressable::URI.new(:fragment => :bogus) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when created with a scheme but no hierarchical " + + "segment" do + it "should raise an error" do + expect do + Addressable::URI.parse("http:") + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "quote handling" do + describe 'in host name' do + it "should raise an error for single quote" do + expect do + Addressable::URI.parse("http://local\"host/") + end.to raise_error(Addressable::URI::InvalidURIError) + end + end +end + +describe Addressable::URI, "newline normalization" do + it "should not accept newlines in scheme" do + expect do + Addressable::URI.parse("ht%0atp://localhost/") + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should not unescape newline in path" do + uri = Addressable::URI.parse("http://localhost/%0a").normalize + expect(uri.to_s).to eq("http://localhost/%0A") + end + + it "should not unescape newline in hostname" do + uri = Addressable::URI.parse("http://local%0ahost/").normalize + expect(uri.to_s).to eq("http://local%0Ahost/") + end + + it "should not unescape newline in username" do + uri = Addressable::URI.parse("http://foo%0abar@localhost/").normalize + expect(uri.to_s).to eq("http://foo%0Abar@localhost/") + end + + it "should not unescape newline in username" do + uri = Addressable::URI.parse("http://example:foo%0abar@example/").normalize + expect(uri.to_s).to eq("http://example:foo%0Abar@example/") + end + + it "should not accept newline in hostname" do + uri = Addressable::URI.parse("http://localhost/") + expect do + uri.host = "local\nhost" + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when created with ambiguous path" do + it "should raise an error" do + expect do + Addressable::URI.parse("::http") + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when created with an invalid host" do + it "should raise an error" do + expect do + Addressable::URI.new(:host => "") + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when created with a host consisting of " + + "sub-delims characters" do + it "should not raise an error" do + expect do + Addressable::URI.new( + :host => Addressable::URI::CharacterClasses::SUB_DELIMS.gsub(/\\/, '') + ) + end.not_to raise_error + end +end + +describe Addressable::URI, "when created with a host consisting of " + + "unreserved characters" do + it "should not raise an error" do + expect do + Addressable::URI.new( + :host => Addressable::URI::CharacterClasses::UNRESERVED.gsub(/\\/, '') + ) + end.not_to raise_error + end +end + +describe Addressable::URI, "when created from nil components" do + before do + @uri = Addressable::URI.new + end + + it "should have a nil site value" do + expect(@uri.site).to eq(nil) + end + + it "should have an empty path" do + expect(@uri.path).to eq("") + end + + it "should be an empty uri" do + expect(@uri.to_s).to eq("") + end + + it "should have a nil default port" do + expect(@uri.default_port).to eq(nil) + end + + it "should be empty" do + expect(@uri).to be_empty + end + + it "should raise an error if the scheme is set to whitespace" do + expect do + @uri.scheme = "\t \n" + end.to raise_error(Addressable::URI::InvalidURIError, /'\t \n'/) + end + + it "should raise an error if the scheme is set to all digits" do + expect do + @uri.scheme = "123" + end.to raise_error(Addressable::URI::InvalidURIError, /'123'/) + end + + it "should raise an error if the scheme begins with a digit" do + expect do + @uri.scheme = "1scheme" + end.to raise_error(Addressable::URI::InvalidURIError, /'1scheme'/) + end + + it "should raise an error if the scheme begins with a plus" do + expect do + @uri.scheme = "+scheme" + end.to raise_error(Addressable::URI::InvalidURIError, /'\+scheme'/) + end + + it "should raise an error if the scheme begins with a dot" do + expect do + @uri.scheme = ".scheme" + end.to raise_error(Addressable::URI::InvalidURIError, /'\.scheme'/) + end + + it "should raise an error if the scheme begins with a dash" do + expect do + @uri.scheme = "-scheme" + end.to raise_error(Addressable::URI::InvalidURIError, /'-scheme'/) + end + + it "should raise an error if the scheme contains an illegal character" do + expect do + @uri.scheme = "scheme!" + end.to raise_error(Addressable::URI::InvalidURIError, /'scheme!'/) + end + + it "should raise an error if the scheme contains whitespace" do + expect do + @uri.scheme = "sch eme" + end.to raise_error(Addressable::URI::InvalidURIError, /'sch eme'/) + end + + it "should raise an error if the scheme contains a newline" do + expect do + @uri.scheme = "sch\neme" + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should raise an error if set into an invalid state" do + expect do + @uri.user = "user" + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should raise an error if set into an invalid state" do + expect do + @uri.password = "pass" + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should raise an error if set into an invalid state" do + expect do + @uri.scheme = "http" + @uri.fragment = "fragment" + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should raise an error if set into an invalid state" do + expect do + @uri.fragment = "fragment" + @uri.scheme = "http" + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when initialized from individual components" do + before do + @uri = Addressable::URI.new( + :scheme => "http", + :user => "user", + :password => "password", + :host => "example.com", + :port => 8080, + :path => "/path", + :query => "query=value", + :fragment => "fragment" + ) + end + + it "returns 'http' for #scheme" do + expect(@uri.scheme).to eq("http") + end + + it "returns 'http' for #normalized_scheme" do + expect(@uri.normalized_scheme).to eq("http") + end + + it "returns 'user' for #user" do + expect(@uri.user).to eq("user") + end + + it "returns 'user' for #normalized_user" do + expect(@uri.normalized_user).to eq("user") + end + + it "returns 'password' for #password" do + expect(@uri.password).to eq("password") + end + + it "returns 'password' for #normalized_password" do + expect(@uri.normalized_password).to eq("password") + end + + it "returns 'user:password' for #userinfo" do + expect(@uri.userinfo).to eq("user:password") + end + + it "returns 'user:password' for #normalized_userinfo" do + expect(@uri.normalized_userinfo).to eq("user:password") + end + + it "returns 'example.com' for #host" do + expect(@uri.host).to eq("example.com") + end + + it "returns 'example.com' for #normalized_host" do + expect(@uri.normalized_host).to eq("example.com") + end + + it "returns 'com' for #tld" do + expect(@uri.tld).to eq("com") + end + + it "returns 'user:password@example.com:8080' for #authority" do + expect(@uri.authority).to eq("user:password@example.com:8080") + end + + it "returns 'user:password@example.com:8080' for #normalized_authority" do + expect(@uri.normalized_authority).to eq("user:password@example.com:8080") + end + + it "returns 8080 for #port" do + expect(@uri.port).to eq(8080) + end + + it "returns 8080 for #normalized_port" do + expect(@uri.normalized_port).to eq(8080) + end + + it "returns 80 for #default_port" do + expect(@uri.default_port).to eq(80) + end + + it "returns 'http://user:password@example.com:8080' for #site" do + expect(@uri.site).to eq("http://user:password@example.com:8080") + end + + it "returns 'http://user:password@example.com:8080' for #normalized_site" do + expect(@uri.normalized_site).to eq("http://user:password@example.com:8080") + end + + it "returns '/path' for #path" do + expect(@uri.path).to eq("/path") + end + + it "returns '/path' for #normalized_path" do + expect(@uri.normalized_path).to eq("/path") + end + + it "returns 'query=value' for #query" do + expect(@uri.query).to eq("query=value") + end + + it "returns 'query=value' for #normalized_query" do + expect(@uri.normalized_query).to eq("query=value") + end + + it "returns 'fragment' for #fragment" do + expect(@uri.fragment).to eq("fragment") + end + + it "returns 'fragment' for #normalized_fragment" do + expect(@uri.normalized_fragment).to eq("fragment") + end + + it "returns #hash" do + expect(@uri.hash).not_to be nil + end + + it "returns #to_s" do + expect(@uri.to_s).to eq( + "http://user:password@example.com:8080/path?query=value#fragment" + ) + end + + it "should not be empty" do + expect(@uri).not_to be_empty + end + + it "should not be frozen" do + expect(@uri).not_to be_frozen + end + + it "should allow destructive operations" do + expect { @uri.normalize! }.not_to raise_error + end +end + +describe Addressable::URI, "when initialized from " + + "frozen individual components" do + before do + @uri = Addressable::URI.new( + :scheme => "http".freeze, + :user => "user".freeze, + :password => "password".freeze, + :host => "example.com".freeze, + :port => "8080".freeze, + :path => "/path".freeze, + :query => "query=value".freeze, + :fragment => "fragment".freeze + ) + end + + it "returns 'http' for #scheme" do + expect(@uri.scheme).to eq("http") + end + + it "returns 'http' for #normalized_scheme" do + expect(@uri.normalized_scheme).to eq("http") + end + + it "returns 'user' for #user" do + expect(@uri.user).to eq("user") + end + + it "returns 'user' for #normalized_user" do + expect(@uri.normalized_user).to eq("user") + end + + it "returns 'password' for #password" do + expect(@uri.password).to eq("password") + end + + it "returns 'password' for #normalized_password" do + expect(@uri.normalized_password).to eq("password") + end + + it "returns 'user:password' for #userinfo" do + expect(@uri.userinfo).to eq("user:password") + end + + it "returns 'user:password' for #normalized_userinfo" do + expect(@uri.normalized_userinfo).to eq("user:password") + end + + it "returns 'example.com' for #host" do + expect(@uri.host).to eq("example.com") + end + + it "returns 'example.com' for #normalized_host" do + expect(@uri.normalized_host).to eq("example.com") + end + + it "returns 'user:password@example.com:8080' for #authority" do + expect(@uri.authority).to eq("user:password@example.com:8080") + end + + it "returns 'user:password@example.com:8080' for #normalized_authority" do + expect(@uri.normalized_authority).to eq("user:password@example.com:8080") + end + + it "returns 8080 for #port" do + expect(@uri.port).to eq(8080) + end + + it "returns 8080 for #normalized_port" do + expect(@uri.normalized_port).to eq(8080) + end + + it "returns 80 for #default_port" do + expect(@uri.default_port).to eq(80) + end + + it "returns 'http://user:password@example.com:8080' for #site" do + expect(@uri.site).to eq("http://user:password@example.com:8080") + end + + it "returns 'http://user:password@example.com:8080' for #normalized_site" do + expect(@uri.normalized_site).to eq("http://user:password@example.com:8080") + end + + it "returns '/path' for #path" do + expect(@uri.path).to eq("/path") + end + + it "returns '/path' for #normalized_path" do + expect(@uri.normalized_path).to eq("/path") + end + + it "returns 'query=value' for #query" do + expect(@uri.query).to eq("query=value") + end + + it "returns 'query=value' for #normalized_query" do + expect(@uri.normalized_query).to eq("query=value") + end + + it "returns 'fragment' for #fragment" do + expect(@uri.fragment).to eq("fragment") + end + + it "returns 'fragment' for #normalized_fragment" do + expect(@uri.normalized_fragment).to eq("fragment") + end + + it "returns #hash" do + expect(@uri.hash).not_to be nil + end + + it "returns #to_s" do + expect(@uri.to_s).to eq( + "http://user:password@example.com:8080/path?query=value#fragment" + ) + end + + it "should not be empty" do + expect(@uri).not_to be_empty + end + + it "should not be frozen" do + expect(@uri).not_to be_frozen + end + + it "should allow destructive operations" do + expect { @uri.normalize! }.not_to raise_error + end +end + +describe Addressable::URI, "when parsed from a frozen string" do + before do + @uri = Addressable::URI.parse( + "http://user:password@example.com:8080/path?query=value#fragment".freeze + ) + end + + it "returns 'http' for #scheme" do + expect(@uri.scheme).to eq("http") + end + + it "returns 'http' for #normalized_scheme" do + expect(@uri.normalized_scheme).to eq("http") + end + + it "returns 'user' for #user" do + expect(@uri.user).to eq("user") + end + + it "returns 'user' for #normalized_user" do + expect(@uri.normalized_user).to eq("user") + end + + it "returns 'password' for #password" do + expect(@uri.password).to eq("password") + end + + it "returns 'password' for #normalized_password" do + expect(@uri.normalized_password).to eq("password") + end + + it "returns 'user:password' for #userinfo" do + expect(@uri.userinfo).to eq("user:password") + end + + it "returns 'user:password' for #normalized_userinfo" do + expect(@uri.normalized_userinfo).to eq("user:password") + end + + it "returns 'example.com' for #host" do + expect(@uri.host).to eq("example.com") + end + + it "returns 'example.com' for #normalized_host" do + expect(@uri.normalized_host).to eq("example.com") + end + + it "returns 'user:password@example.com:8080' for #authority" do + expect(@uri.authority).to eq("user:password@example.com:8080") + end + + it "returns 'user:password@example.com:8080' for #normalized_authority" do + expect(@uri.normalized_authority).to eq("user:password@example.com:8080") + end + + it "returns 8080 for #port" do + expect(@uri.port).to eq(8080) + end + + it "returns 8080 for #normalized_port" do + expect(@uri.normalized_port).to eq(8080) + end + + it "returns 80 for #default_port" do + expect(@uri.default_port).to eq(80) + end + + it "returns 'http://user:password@example.com:8080' for #site" do + expect(@uri.site).to eq("http://user:password@example.com:8080") + end + + it "returns 'http://user:password@example.com:8080' for #normalized_site" do + expect(@uri.normalized_site).to eq("http://user:password@example.com:8080") + end + + it "returns '/path' for #path" do + expect(@uri.path).to eq("/path") + end + + it "returns '/path' for #normalized_path" do + expect(@uri.normalized_path).to eq("/path") + end + + it "returns 'query=value' for #query" do + expect(@uri.query).to eq("query=value") + end + + it "returns 'query=value' for #normalized_query" do + expect(@uri.normalized_query).to eq("query=value") + end + + it "returns 'fragment' for #fragment" do + expect(@uri.fragment).to eq("fragment") + end + + it "returns 'fragment' for #normalized_fragment" do + expect(@uri.normalized_fragment).to eq("fragment") + end + + it "returns #hash" do + expect(@uri.hash).not_to be nil + end + + it "returns #to_s" do + expect(@uri.to_s).to eq( + "http://user:password@example.com:8080/path?query=value#fragment" + ) + end + + it "should not be empty" do + expect(@uri).not_to be_empty + end + + it "should not be frozen" do + expect(@uri).not_to be_frozen + end + + it "should allow destructive operations" do + expect { @uri.normalize! }.not_to raise_error + end +end + +describe Addressable::URI, "when frozen" do + before do + @uri = Addressable::URI.new.freeze + end + + it "returns nil for #scheme" do + expect(@uri.scheme).to eq(nil) + end + + it "returns nil for #normalized_scheme" do + expect(@uri.normalized_scheme).to eq(nil) + end + + it "returns nil for #user" do + expect(@uri.user).to eq(nil) + end + + it "returns nil for #normalized_user" do + expect(@uri.normalized_user).to eq(nil) + end + + it "returns nil for #password" do + expect(@uri.password).to eq(nil) + end + + it "returns nil for #normalized_password" do + expect(@uri.normalized_password).to eq(nil) + end + + it "returns nil for #userinfo" do + expect(@uri.userinfo).to eq(nil) + end + + it "returns nil for #normalized_userinfo" do + expect(@uri.normalized_userinfo).to eq(nil) + end + + it "returns nil for #host" do + expect(@uri.host).to eq(nil) + end + + it "returns nil for #normalized_host" do + expect(@uri.normalized_host).to eq(nil) + end + + it "returns nil for #authority" do + expect(@uri.authority).to eq(nil) + end + + it "returns nil for #normalized_authority" do + expect(@uri.normalized_authority).to eq(nil) + end + + it "returns nil for #port" do + expect(@uri.port).to eq(nil) + end + + it "returns nil for #normalized_port" do + expect(@uri.normalized_port).to eq(nil) + end + + it "returns nil for #default_port" do + expect(@uri.default_port).to eq(nil) + end + + it "returns nil for #site" do + expect(@uri.site).to eq(nil) + end + + it "returns nil for #normalized_site" do + expect(@uri.normalized_site).to eq(nil) + end + + it "returns '' for #path" do + expect(@uri.path).to eq('') + end + + it "returns '' for #normalized_path" do + expect(@uri.normalized_path).to eq('') + end + + it "returns nil for #query" do + expect(@uri.query).to eq(nil) + end + + it "returns nil for #normalized_query" do + expect(@uri.normalized_query).to eq(nil) + end + + it "returns nil for #fragment" do + expect(@uri.fragment).to eq(nil) + end + + it "returns nil for #normalized_fragment" do + expect(@uri.normalized_fragment).to eq(nil) + end + + it "returns #hash" do + expect(@uri.hash).not_to be nil + end + + it "returns #to_s" do + expect(@uri.to_s).to eq('') + end + + it "should be empty" do + expect(@uri).to be_empty + end + + it "should be frozen" do + expect(@uri).to be_frozen + end + + it "should not be frozen after duping" do + expect(@uri.dup).not_to be_frozen + end + + it "should not allow destructive operations" do + expect { @uri.normalize! }.to raise_error { |error| + expect(error.message).to match(/can't modify frozen/) + expect(error).to satisfy { |e| RuntimeError === e || TypeError === e } + } + end +end + +describe Addressable::URI, "when frozen" do + before do + @uri = Addressable::URI.parse( + "HTTP://example.com.:%38%30/%70a%74%68?a=%31#1%323" + ).freeze + end + + it "returns 'HTTP' for #scheme" do + expect(@uri.scheme).to eq("HTTP") + end + + it "returns 'http' for #normalized_scheme" do + expect(@uri.normalized_scheme).to eq("http") + expect(@uri.normalize.scheme).to eq("http") + end + + it "returns nil for #user" do + expect(@uri.user).to eq(nil) + end + + it "returns nil for #normalized_user" do + expect(@uri.normalized_user).to eq(nil) + end + + it "returns nil for #password" do + expect(@uri.password).to eq(nil) + end + + it "returns nil for #normalized_password" do + expect(@uri.normalized_password).to eq(nil) + end + + it "returns nil for #userinfo" do + expect(@uri.userinfo).to eq(nil) + end + + it "returns nil for #normalized_userinfo" do + expect(@uri.normalized_userinfo).to eq(nil) + end + + it "returns 'example.com.' for #host" do + expect(@uri.host).to eq("example.com.") + end + + it "returns nil for #normalized_host" do + expect(@uri.normalized_host).to eq("example.com") + expect(@uri.normalize.host).to eq("example.com") + end + + it "returns 'example.com.:80' for #authority" do + expect(@uri.authority).to eq("example.com.:80") + end + + it "returns 'example.com:80' for #normalized_authority" do + expect(@uri.normalized_authority).to eq("example.com") + expect(@uri.normalize.authority).to eq("example.com") + end + + it "returns 80 for #port" do + expect(@uri.port).to eq(80) + end + + it "returns nil for #normalized_port" do + expect(@uri.normalized_port).to eq(nil) + expect(@uri.normalize.port).to eq(nil) + end + + it "returns 80 for #default_port" do + expect(@uri.default_port).to eq(80) + end + + it "returns 'HTTP://example.com.:80' for #site" do + expect(@uri.site).to eq("HTTP://example.com.:80") + end + + it "returns 'http://example.com' for #normalized_site" do + expect(@uri.normalized_site).to eq("http://example.com") + expect(@uri.normalize.site).to eq("http://example.com") + end + + it "returns '/%70a%74%68' for #path" do + expect(@uri.path).to eq("/%70a%74%68") + end + + it "returns '/path' for #normalized_path" do + expect(@uri.normalized_path).to eq("/path") + expect(@uri.normalize.path).to eq("/path") + end + + it "returns 'a=%31' for #query" do + expect(@uri.query).to eq("a=%31") + end + + it "returns 'a=1' for #normalized_query" do + expect(@uri.normalized_query).to eq("a=1") + expect(@uri.normalize.query).to eq("a=1") + end + + it "returns '/%70a%74%68?a=%31' for #request_uri" do + expect(@uri.request_uri).to eq("/%70a%74%68?a=%31") + end + + it "returns '1%323' for #fragment" do + expect(@uri.fragment).to eq("1%323") + end + + it "returns '123' for #normalized_fragment" do + expect(@uri.normalized_fragment).to eq("123") + expect(@uri.normalize.fragment).to eq("123") + end + + it "returns #hash" do + expect(@uri.hash).not_to be nil + end + + it "returns #to_s" do + expect(@uri.to_s).to eq('HTTP://example.com.:80/%70a%74%68?a=%31#1%323') + expect(@uri.normalize.to_s).to eq('http://example.com/path?a=1#123') + end + + it "should not be empty" do + expect(@uri).not_to be_empty + end + + it "should be frozen" do + expect(@uri).to be_frozen + end + + it "should not be frozen after duping" do + expect(@uri.dup).not_to be_frozen + end + + it "should not allow destructive operations" do + expect { @uri.normalize! }.to raise_error { |error| + expect(error.message).to match(/can't modify frozen/) + expect(error).to satisfy { |e| RuntimeError === e || TypeError === e } + } + end +end + +describe Addressable::URI, "when normalized and then deeply frozen" do + before do + @uri = Addressable::URI.parse( + "http://user:password@example.com:8080/path?query=value#fragment" + ).normalize! + + @uri.instance_variables.each do |var| + @uri.instance_variable_set(var, @uri.instance_variable_get(var).freeze) + end + + @uri.freeze + end + + it "#normalized_scheme should not error" do + expect { @uri.normalized_scheme }.not_to raise_error + end + + it "#normalized_user should not error" do + expect { @uri.normalized_user }.not_to raise_error + end + + it "#normalized_password should not error" do + expect { @uri.normalized_password }.not_to raise_error + end + + it "#normalized_userinfo should not error" do + expect { @uri.normalized_userinfo }.not_to raise_error + end + + it "#normalized_host should not error" do + expect { @uri.normalized_host }.not_to raise_error + end + + it "#normalized_authority should not error" do + expect { @uri.normalized_authority }.not_to raise_error + end + + it "#normalized_port should not error" do + expect { @uri.normalized_port }.not_to raise_error + end + + it "#normalized_site should not error" do + expect { @uri.normalized_site }.not_to raise_error + end + + it "#normalized_path should not error" do + expect { @uri.normalized_path }.not_to raise_error + end + + it "#normalized_query should not error" do + expect { @uri.normalized_query }.not_to raise_error + end + + it "#normalized_fragment should not error" do + expect { @uri.normalized_fragment }.not_to raise_error + end + + it "should be frozen" do + expect(@uri).to be_frozen + end + + it "should not allow destructive operations" do + expect { @uri.normalize! }.to raise_error(RuntimeError) + end +end + +describe Addressable::URI, "when created from string components" do + before do + @uri = Addressable::URI.new( + :scheme => "http", :host => "example.com" + ) + end + + it "should have a site value of 'http://example.com'" do + expect(@uri.site).to eq("http://example.com") + end + + it "should be equal to the equivalent parsed URI" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com")) + end + + it "should raise an error if invalid components omitted" do + expect do + @uri.omit(:bogus) + end.to raise_error(ArgumentError) + expect do + @uri.omit(:scheme, :bogus, :path) + end.to raise_error(ArgumentError) + end +end + +describe Addressable::URI, "when created with a nil host but " + + "non-nil authority components" do + it "should raise an error" do + expect do + Addressable::URI.new(:user => "user", :password => "pass", :port => 80) + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when created with both an authority and a user" do + it "should raise an error" do + expect do + Addressable::URI.new( + :user => "user", :authority => "user@example.com:80" + ) + end.to raise_error(ArgumentError) + end +end + +describe Addressable::URI, "when created with an authority and no port" do + before do + @uri = Addressable::URI.new(:authority => "user@example.com") + end + + it "should not infer a port" do + expect(@uri.port).to eq(nil) + expect(@uri.default_port).to eq(nil) + expect(@uri.inferred_port).to eq(nil) + end + + it "should have a site value of '//user@example.com'" do + expect(@uri.site).to eq("//user@example.com") + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when created with a host with trailing dots" do + before do + @uri = Addressable::URI.new(:authority => "example...") + end + + it "should have a stable normalized form" do + expect(@uri.normalize.normalize.normalize.host).to eq( + @uri.normalize.host + ) + end +end + +describe Addressable::URI, "when created with a host with a backslash" do + it "should raise an error" do + expect do + Addressable::URI.new(:authority => "example\\example") + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when created with a host with a slash" do + it "should raise an error" do + expect do + Addressable::URI.new(:authority => "example/example") + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when created with a host with a space" do + it "should raise an error" do + expect do + Addressable::URI.new(:authority => "example example") + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when created with both a userinfo and a user" do + it "should raise an error" do + expect do + Addressable::URI.new(:user => "user", :userinfo => "user:pass") + end.to raise_error(ArgumentError) + end +end + +describe Addressable::URI, "when created with a path that hasn't been " + + "prefixed with a '/' but a host specified" do + before do + @uri = Addressable::URI.new( + :scheme => "http", :host => "example.com", :path => "path" + ) + end + + it "should prefix a '/' to the path" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com/path")) + end + + it "should have a site value of 'http://example.com'" do + expect(@uri.site).to eq("http://example.com") + end + + it "should have an origin of 'http://example.com" do + expect(@uri.origin).to eq('http://example.com') + end +end + +describe Addressable::URI, "when created with a path that hasn't been " + + "prefixed with a '/' but no host specified" do + before do + @uri = Addressable::URI.new( + :scheme => "http", :path => "path" + ) + end + + it "should not prefix a '/' to the path" do + expect(@uri).to eq(Addressable::URI.parse("http:path")) + end + + it "should have a site value of 'http:'" do + expect(@uri.site).to eq("http:") + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when parsed from an Addressable::URI object" do + it "should not have unexpected side-effects" do + original_uri = Addressable::URI.parse("http://example.com/") + new_uri = Addressable::URI.parse(original_uri) + new_uri.host = 'www.example.com' + expect(new_uri.host).to eq('www.example.com') + expect(new_uri.to_s).to eq('http://www.example.com/') + expect(original_uri.host).to eq('example.com') + expect(original_uri.to_s).to eq('http://example.com/') + end + + it "should not have unexpected side-effects" do + original_uri = Addressable::URI.parse("http://example.com/") + new_uri = Addressable::URI.heuristic_parse(original_uri) + new_uri.host = 'www.example.com' + expect(new_uri.host).to eq('www.example.com') + expect(new_uri.to_s).to eq('http://www.example.com/') + expect(original_uri.host).to eq('example.com') + expect(original_uri.to_s).to eq('http://example.com/') + end + + it "should not have unexpected side-effects" do + original_uri = Addressable::URI.parse("http://example.com/") + new_uri = Addressable::URI.parse(original_uri) + new_uri.origin = 'https://www.example.com:8080' + expect(new_uri.host).to eq('www.example.com') + expect(new_uri.to_s).to eq('https://www.example.com:8080/') + expect(original_uri.host).to eq('example.com') + expect(original_uri.to_s).to eq('http://example.com/') + end + + it "should not have unexpected side-effects" do + original_uri = Addressable::URI.parse("http://example.com/") + new_uri = Addressable::URI.heuristic_parse(original_uri) + new_uri.origin = 'https://www.example.com:8080' + expect(new_uri.host).to eq('www.example.com') + expect(new_uri.to_s).to eq('https://www.example.com:8080/') + expect(original_uri.host).to eq('example.com') + expect(original_uri.to_s).to eq('http://example.com/') + end +end + +describe Addressable::URI, "when parsed from something that looks " + + "like a URI object" do + it "should parse without error" do + uri = Addressable::URI.parse(Fake::URI::HTTP.new("http://example.com/")) + expect do + Addressable::URI.parse(uri) + end.not_to raise_error + end +end + +describe Addressable::URI, "when parsed from a standard library URI object" do + it "should parse without error" do + uri = Addressable::URI.parse(URI.parse("http://example.com/")) + expect do + Addressable::URI.parse(uri) + end.not_to raise_error + end +end + +describe Addressable::URI, "when parsed from ''" do + before do + @uri = Addressable::URI.parse("") + end + + it "should have no scheme" do + expect(@uri.scheme).to eq(nil) + end + + it "should not be considered to be ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should have a path of ''" do + expect(@uri.path).to eq("") + end + + it "should have a request URI of '/'" do + expect(@uri.request_uri).to eq("/") + end + + it "should be considered relative" do + expect(@uri).to be_relative + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +# Section 1.1.2 of RFC 3986 +describe Addressable::URI, "when parsed from " + + "'ftp://ftp.is.co.za/rfc/rfc1808.txt'" do + before do + @uri = Addressable::URI.parse("ftp://ftp.is.co.za/rfc/rfc1808.txt") + end + + it "should use the 'ftp' scheme" do + expect(@uri.scheme).to eq("ftp") + end + + it "should be considered to be ip-based" do + expect(@uri).to be_ip_based + end + + it "should have a host of 'ftp.is.co.za'" do + expect(@uri.host).to eq("ftp.is.co.za") + end + + it "should have inferred_port of 21" do + expect(@uri.inferred_port).to eq(21) + end + + it "should have a path of '/rfc/rfc1808.txt'" do + expect(@uri.path).to eq("/rfc/rfc1808.txt") + end + + it "should not have a request URI" do + expect(@uri.request_uri).to eq(nil) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have an origin of 'ftp://ftp.is.co.za'" do + expect(@uri.origin).to eq('ftp://ftp.is.co.za') + end +end + +# Section 1.1.2 of RFC 3986 +describe Addressable::URI, "when parsed from " + + "'http://www.ietf.org/rfc/rfc2396.txt'" do + before do + @uri = Addressable::URI.parse("http://www.ietf.org/rfc/rfc2396.txt") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should be considered to be ip-based" do + expect(@uri).to be_ip_based + end + + it "should have a host of 'www.ietf.org'" do + expect(@uri.host).to eq("www.ietf.org") + end + + it "should have inferred_port of 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have a path of '/rfc/rfc2396.txt'" do + expect(@uri.path).to eq("/rfc/rfc2396.txt") + end + + it "should have a request URI of '/rfc/rfc2396.txt'" do + expect(@uri.request_uri).to eq("/rfc/rfc2396.txt") + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should correctly omit components" do + expect(@uri.omit(:scheme).to_s).to eq("//www.ietf.org/rfc/rfc2396.txt") + expect(@uri.omit(:path).to_s).to eq("http://www.ietf.org") + end + + it "should correctly omit components destructively" do + @uri.omit!(:scheme) + expect(@uri.to_s).to eq("//www.ietf.org/rfc/rfc2396.txt") + end + + it "should have an origin of 'http://www.ietf.org'" do + expect(@uri.origin).to eq('http://www.ietf.org') + end +end + +# Section 1.1.2 of RFC 3986 +describe Addressable::URI, "when parsed from " + + "'ldap://[2001:db8::7]/c=GB?objectClass?one'" do + before do + @uri = Addressable::URI.parse("ldap://[2001:db8::7]/c=GB?objectClass?one") + end + + it "should use the 'ldap' scheme" do + expect(@uri.scheme).to eq("ldap") + end + + it "should be considered to be ip-based" do + expect(@uri).to be_ip_based + end + + it "should have a host of '[2001:db8::7]'" do + expect(@uri.host).to eq("[2001:db8::7]") + end + + it "should have inferred_port of 389" do + expect(@uri.inferred_port).to eq(389) + end + + it "should have a path of '/c=GB'" do + expect(@uri.path).to eq("/c=GB") + end + + it "should not have a request URI" do + expect(@uri.request_uri).to eq(nil) + end + + it "should not allow request URI assignment" do + expect do + @uri.request_uri = "/" + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should have a query of 'objectClass?one'" do + expect(@uri.query).to eq("objectClass?one") + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should correctly omit components" do + expect(@uri.omit(:scheme, :authority).to_s).to eq("/c=GB?objectClass?one") + expect(@uri.omit(:path).to_s).to eq("ldap://[2001:db8::7]?objectClass?one") + end + + it "should correctly omit components destructively" do + @uri.omit!(:scheme, :authority) + expect(@uri.to_s).to eq("/c=GB?objectClass?one") + end + + it "should raise an error if omission would create an invalid URI" do + expect do + @uri.omit(:authority, :path) + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should have an origin of 'ldap://[2001:db8::7]'" do + expect(@uri.origin).to eq('ldap://[2001:db8::7]') + end +end + +# Section 1.1.2 of RFC 3986 +describe Addressable::URI, "when parsed from " + + "'mailto:John.Doe@example.com'" do + before do + @uri = Addressable::URI.parse("mailto:John.Doe@example.com") + end + + it "should use the 'mailto' scheme" do + expect(@uri.scheme).to eq("mailto") + end + + it "should not be considered to be ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should not have an inferred_port" do + expect(@uri.inferred_port).to eq(nil) + end + + it "should have a path of 'John.Doe@example.com'" do + expect(@uri.path).to eq("John.Doe@example.com") + end + + it "should not have a request URI" do + expect(@uri.request_uri).to eq(nil) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +# Section 2 of RFC 6068 +describe Addressable::URI, "when parsed from " + + "'mailto:?to=addr1@an.example,addr2@an.example'" do + before do + @uri = Addressable::URI.parse( + "mailto:?to=addr1@an.example,addr2@an.example" + ) + end + + it "should use the 'mailto' scheme" do + expect(@uri.scheme).to eq("mailto") + end + + it "should not be considered to be ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should not have an inferred_port" do + expect(@uri.inferred_port).to eq(nil) + end + + it "should have a path of ''" do + expect(@uri.path).to eq("") + end + + it "should not have a request URI" do + expect(@uri.request_uri).to eq(nil) + end + + it "should have the To: field value parameterized" do + expect(@uri.query_values(Hash)["to"]).to eq( + "addr1@an.example,addr2@an.example" + ) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +# Section 1.1.2 of RFC 3986 +describe Addressable::URI, "when parsed from " + + "'news:comp.infosystems.www.servers.unix'" do + before do + @uri = Addressable::URI.parse("news:comp.infosystems.www.servers.unix") + end + + it "should use the 'news' scheme" do + expect(@uri.scheme).to eq("news") + end + + it "should not have an inferred_port" do + expect(@uri.inferred_port).to eq(nil) + end + + it "should not be considered to be ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should have a path of 'comp.infosystems.www.servers.unix'" do + expect(@uri.path).to eq("comp.infosystems.www.servers.unix") + end + + it "should not have a request URI" do + expect(@uri.request_uri).to eq(nil) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +# Section 1.1.2 of RFC 3986 +describe Addressable::URI, "when parsed from " + + "'tel:+1-816-555-1212'" do + before do + @uri = Addressable::URI.parse("tel:+1-816-555-1212") + end + + it "should use the 'tel' scheme" do + expect(@uri.scheme).to eq("tel") + end + + it "should not be considered to be ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should not have an inferred_port" do + expect(@uri.inferred_port).to eq(nil) + end + + it "should have a path of '+1-816-555-1212'" do + expect(@uri.path).to eq("+1-816-555-1212") + end + + it "should not have a request URI" do + expect(@uri.request_uri).to eq(nil) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +# Section 1.1.2 of RFC 3986 +describe Addressable::URI, "when parsed from " + + "'telnet://192.0.2.16:80/'" do + before do + @uri = Addressable::URI.parse("telnet://192.0.2.16:80/") + end + + it "should use the 'telnet' scheme" do + expect(@uri.scheme).to eq("telnet") + end + + it "should have a host of '192.0.2.16'" do + expect(@uri.host).to eq("192.0.2.16") + end + + it "should have a port of 80" do + expect(@uri.port).to eq(80) + end + + it "should have a inferred_port of 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have a default_port of 23" do + expect(@uri.default_port).to eq(23) + end + + it "should be considered to be ip-based" do + expect(@uri).to be_ip_based + end + + it "should have a path of '/'" do + expect(@uri.path).to eq("/") + end + + it "should not have a request URI" do + expect(@uri.request_uri).to eq(nil) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have an origin of 'telnet://192.0.2.16:80'" do + expect(@uri.origin).to eq('telnet://192.0.2.16:80') + end +end + +# Section 1.1.2 of RFC 3986 +describe Addressable::URI, "when parsed from " + + "'urn:oasis:names:specification:docbook:dtd:xml:4.1.2'" do + before do + @uri = Addressable::URI.parse( + "urn:oasis:names:specification:docbook:dtd:xml:4.1.2") + end + + it "should use the 'urn' scheme" do + expect(@uri.scheme).to eq("urn") + end + + it "should not have an inferred_port" do + expect(@uri.inferred_port).to eq(nil) + end + + it "should not be considered to be ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should have a path of " + + "'oasis:names:specification:docbook:dtd:xml:4.1.2'" do + expect(@uri.path).to eq("oasis:names:specification:docbook:dtd:xml:4.1.2") + end + + it "should not have a request URI" do + expect(@uri.request_uri).to eq(nil) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when heuristically parsed from " + + "'192.0.2.16:8000/path'" do + before do + @uri = Addressable::URI.heuristic_parse("192.0.2.16:8000/path") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have a host of '192.0.2.16'" do + expect(@uri.host).to eq("192.0.2.16") + end + + it "should have a port of '8000'" do + expect(@uri.port).to eq(8000) + end + + it "should be considered to be ip-based" do + expect(@uri).to be_ip_based + end + + it "should have a path of '/path'" do + expect(@uri.path).to eq("/path") + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have an origin of 'http://192.0.2.16:8000'" do + expect(@uri.origin).to eq('http://192.0.2.16:8000') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com'" do + before do + @uri = Addressable::URI.parse("http://example.com") + end + + it "when inspected, should have the correct URI" do + expect(@uri.inspect).to include("http://example.com") + end + + it "when inspected, should have the correct class name" do + expect(@uri.inspect).to include("Addressable::URI") + end + + it "when inspected, should have the correct object id" do + expect(@uri.inspect).to include("%#0x" % @uri.object_id) + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should be considered to be ip-based" do + expect(@uri).to be_ip_based + end + + it "should have an authority segment of 'example.com'" do + expect(@uri.authority).to eq("example.com") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should be considered ip-based" do + expect(@uri).to be_ip_based + end + + it "should have no username" do + expect(@uri.user).to eq(nil) + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should not have a specified port" do + expect(@uri.port).to eq(nil) + end + + it "should have an empty path" do + expect(@uri.path).to eq("") + end + + it "should have no query string" do + expect(@uri.query).to eq(nil) + expect(@uri.query_values).to eq(nil) + end + + it "should have a request URI of '/'" do + expect(@uri.request_uri).to eq("/") + end + + it "should have no fragment" do + expect(@uri.fragment).to eq(nil) + end + + it "should be considered absolute" do + expect(@uri).to be_absolute + end + + it "should not be considered relative" do + expect(@uri).not_to be_relative + end + + it "should not be exactly equal to 42" do + expect(@uri.eql?(42)).to eq(false) + end + + it "should not be equal to 42" do + expect(@uri == 42).to eq(false) + end + + it "should not be roughly equal to 42" do + expect(@uri === 42).to eq(false) + end + + it "should be exactly equal to http://example.com" do + expect(@uri.eql?(Addressable::URI.parse("http://example.com"))).to eq(true) + end + + it "should be roughly equal to http://example.com/" do + expect(@uri === Addressable::URI.parse("http://example.com/")).to eq(true) + end + + it "should be roughly equal to the string 'http://example.com/'" do + expect(@uri === "http://example.com/").to eq(true) + end + + it "should not be roughly equal to the string " + + "'http://example.com:bogus/'" do + expect do + expect(@uri === "http://example.com:bogus/").to eq(false) + end.not_to raise_error + end + + it "should result in itself when joined with itself" do + expect(@uri.join(@uri).to_s).to eq("http://example.com") + expect(@uri.join!(@uri).to_s).to eq("http://example.com") + end + + it "should be equivalent to http://EXAMPLE.com" do + expect(@uri).to eq(Addressable::URI.parse("http://EXAMPLE.com")) + end + + it "should be equivalent to http://EXAMPLE.com:80/" do + expect(@uri).to eq(Addressable::URI.parse("http://EXAMPLE.com:80/")) + end + + it "should have the same hash as http://example.com" do + expect(@uri.hash).to eq(Addressable::URI.parse("http://example.com").hash) + end + + it "should have the same hash as http://EXAMPLE.com after assignment" do + @uri.origin = "http://EXAMPLE.com" + expect(@uri.hash).to eq(Addressable::URI.parse("http://EXAMPLE.com").hash) + end + + it "should have a different hash from http://EXAMPLE.com" do + expect(@uri.hash).not_to eq(Addressable::URI.parse("http://EXAMPLE.com").hash) + end + + it "should not allow origin assignment without scheme" do + expect do + @uri.origin = "example.com" + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should not allow origin assignment without host" do + expect do + @uri.origin = "http://" + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should not allow origin assignment with bogus type" do + expect do + @uri.origin = :bogus + end.to raise_error(TypeError) + end + + # Section 6.2.3 of RFC 3986 + it "should be equivalent to http://example.com/" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com/")) + end + + # Section 6.2.3 of RFC 3986 + it "should be equivalent to http://example.com:/" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com:/")) + end + + # Section 6.2.3 of RFC 3986 + it "should be equivalent to http://example.com:80/" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com:80/")) + end + + # Section 6.2.2.1 of RFC 3986 + it "should be equivalent to http://EXAMPLE.COM/" do + expect(@uri).to eq(Addressable::URI.parse("http://EXAMPLE.COM/")) + end + + it "should have a route of '/path/' to 'http://example.com/path/'" do + expect(@uri.route_to("http://example.com/path/")).to eq( + Addressable::URI.parse("/path/") + ) + end + + it "should have a route of '..' from 'http://example.com/path/'" do + expect(@uri.route_from("http://example.com/path/")).to eq( + Addressable::URI.parse("..") + ) + end + + it "should have a route of '#' to 'http://example.com/'" do + expect(@uri.route_to("http://example.com/")).to eq( + Addressable::URI.parse("#") + ) + end + + it "should have a route of 'http://elsewhere.com/' to " + + "'http://elsewhere.com/'" do + expect(@uri.route_to("http://elsewhere.com/")).to eq( + Addressable::URI.parse("http://elsewhere.com/") + ) + end + + it "when joined with 'relative/path' should be " + + "'http://example.com/relative/path'" do + expect(@uri.join('relative/path')).to eq( + Addressable::URI.parse("http://example.com/relative/path") + ) + end + + it "when joined with a bogus object a TypeError should be raised" do + expect do + @uri.join(42) + end.to raise_error(TypeError) + end + + it "should have the correct username after assignment" do + @uri.user = "newuser" + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq(nil) + expect(@uri.to_s).to eq("http://newuser@example.com") + end + + it "should have the correct username after assignment" do + @uri.user = "user@123!" + expect(@uri.user).to eq("user@123!") + expect(@uri.normalized_user).to eq("user%40123%21") + expect(@uri.password).to eq(nil) + expect(@uri.normalize.to_s).to eq("http://user%40123%21@example.com/") + end + + it "should have the correct password after assignment" do + @uri.password = "newpass" + expect(@uri.password).to eq("newpass") + expect(@uri.user).to eq("") + expect(@uri.to_s).to eq("http://:newpass@example.com") + end + + it "should have the correct password after assignment" do + @uri.password = "#secret@123!" + expect(@uri.password).to eq("#secret@123!") + expect(@uri.normalized_password).to eq("%23secret%40123%21") + expect(@uri.user).to eq("") + expect(@uri.normalize.to_s).to eq("http://:%23secret%40123%21@example.com/") + expect(@uri.omit(:password).to_s).to eq("http://example.com") + end + + it "should have the correct user/pass after repeated assignment" do + @uri.user = nil + expect(@uri.user).to eq(nil) + @uri.password = "newpass" + expect(@uri.password).to eq("newpass") + # Username cannot be nil if the password is set + expect(@uri.user).to eq("") + expect(@uri.to_s).to eq("http://:newpass@example.com") + @uri.user = "newuser" + expect(@uri.user).to eq("newuser") + @uri.password = nil + expect(@uri.password).to eq(nil) + expect(@uri.to_s).to eq("http://newuser@example.com") + @uri.user = "newuser" + expect(@uri.user).to eq("newuser") + @uri.password = "" + expect(@uri.password).to eq("") + expect(@uri.to_s).to eq("http://newuser:@example.com") + @uri.password = "newpass" + expect(@uri.password).to eq("newpass") + @uri.user = nil + # Username cannot be nil if the password is set + expect(@uri.user).to eq("") + expect(@uri.to_s).to eq("http://:newpass@example.com") + end + + it "should have the correct user/pass after userinfo assignment" do + @uri.user = "newuser" + expect(@uri.user).to eq("newuser") + @uri.password = "newpass" + expect(@uri.password).to eq("newpass") + @uri.userinfo = nil + expect(@uri.userinfo).to eq(nil) + expect(@uri.user).to eq(nil) + expect(@uri.password).to eq(nil) + end + + it "should correctly convert to a hash" do + expect(@uri.to_hash).to eq({ + :scheme => "http", + :user => nil, + :password => nil, + :host => "example.com", + :port => nil, + :path => "", + :query => nil, + :fragment => nil + }) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end + + it "should have an origin of 'http://example.com'" do + expect(@uri.origin).to eq('http://example.com') + end +end + +# Section 5.1.2 of RFC 2616 +describe Addressable::URI, "when parsed from " + + "'HTTP://www.w3.org/pub/WWW/TheProject.html'" do + before do + @uri = Addressable::URI.parse("HTTP://www.w3.org/pub/WWW/TheProject.html") + end + + it "should have the correct request URI" do + expect(@uri.request_uri).to eq("/pub/WWW/TheProject.html") + end + + it "should have the correct request URI after assignment" do + @uri.request_uri = "/pub/WWW/TheProject.html?" + expect(@uri.request_uri).to eq("/pub/WWW/TheProject.html?") + expect(@uri.path).to eq("/pub/WWW/TheProject.html") + expect(@uri.query).to eq("") + end + + it "should have the correct request URI after assignment" do + @uri.request_uri = "/some/where/else.html" + expect(@uri.request_uri).to eq("/some/where/else.html") + expect(@uri.path).to eq("/some/where/else.html") + expect(@uri.query).to eq(nil) + end + + it "should have the correct request URI after assignment" do + @uri.request_uri = "/some/where/else.html?query?string" + expect(@uri.request_uri).to eq("/some/where/else.html?query?string") + expect(@uri.path).to eq("/some/where/else.html") + expect(@uri.query).to eq("query?string") + end + + it "should have the correct request URI after assignment" do + @uri.request_uri = "?x=y" + expect(@uri.request_uri).to eq("/?x=y") + expect(@uri.path).to eq("/") + expect(@uri.query).to eq("x=y") + end + + it "should raise an error if the site value is set to something bogus" do + expect do + @uri.site = 42 + end.to raise_error(TypeError) + end + + it "should raise an error if the request URI is set to something bogus" do + expect do + @uri.request_uri = 42 + end.to raise_error(TypeError) + end + + it "should correctly convert to a hash" do + expect(@uri.to_hash).to eq({ + :scheme => "HTTP", + :user => nil, + :password => nil, + :host => "www.w3.org", + :port => nil, + :path => "/pub/WWW/TheProject.html", + :query => nil, + :fragment => nil + }) + end + + it "should have an origin of 'http://www.w3.org'" do + expect(@uri.origin).to eq('http://www.w3.org') + end +end + +describe Addressable::URI, "when parsing IPv6 addresses" do + it "should not raise an error for " + + "'http://[3ffe:1900:4545:3:200:f8ff:fe21:67cf]/'" do + Addressable::URI.parse("http://[3ffe:1900:4545:3:200:f8ff:fe21:67cf]/") + end + + it "should not raise an error for " + + "'http://[fe80:0:0:0:200:f8ff:fe21:67cf]/'" do + Addressable::URI.parse("http://[fe80:0:0:0:200:f8ff:fe21:67cf]/") + end + + it "should not raise an error for " + + "'http://[fe80::200:f8ff:fe21:67cf]/'" do + Addressable::URI.parse("http://[fe80::200:f8ff:fe21:67cf]/") + end + + it "should not raise an error for " + + "'http://[::1]/'" do + Addressable::URI.parse("http://[::1]/") + end + + it "should not raise an error for " + + "'http://[fe80::1]/'" do + Addressable::URI.parse("http://[fe80::1]/") + end + + it "should raise an error for " + + "'http://[]/'" do + expect do + Addressable::URI.parse("http://[]/") + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when parsing IPv6 address" do + subject { Addressable::URI.parse("http://[3ffe:1900:4545:3:200:f8ff:fe21:67cf]/") } + its(:host) { should == '[3ffe:1900:4545:3:200:f8ff:fe21:67cf]' } + its(:hostname) { should == '3ffe:1900:4545:3:200:f8ff:fe21:67cf' } +end + +describe Addressable::URI, "when assigning IPv6 address" do + it "should allow to set bare IPv6 address as hostname" do + uri = Addressable::URI.parse("http://[::1]/") + uri.hostname = '3ffe:1900:4545:3:200:f8ff:fe21:67cf' + expect(uri.to_s).to eq('http://[3ffe:1900:4545:3:200:f8ff:fe21:67cf]/') + end + + it "should allow to set bare IPv6 address as hostname with IPAddr object" do + uri = Addressable::URI.parse("http://[::1]/") + uri.hostname = IPAddr.new('3ffe:1900:4545:3:200:f8ff:fe21:67cf') + expect(uri.to_s).to eq('http://[3ffe:1900:4545:3:200:f8ff:fe21:67cf]/') + end + + it "should not allow to set bare IPv6 address as host" do + uri = Addressable::URI.parse("http://[::1]/") + skip "not checked" + expect do + uri.host = '3ffe:1900:4545:3:200:f8ff:fe21:67cf' + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when parsing IPvFuture addresses" do + it "should not raise an error for " + + "'http://[v9.3ffe:1900:4545:3:200:f8ff:fe21:67cf]/'" do + Addressable::URI.parse("http://[v9.3ffe:1900:4545:3:200:f8ff:fe21:67cf]/") + end + + it "should not raise an error for " + + "'http://[vff.fe80:0:0:0:200:f8ff:fe21:67cf]/'" do + Addressable::URI.parse("http://[vff.fe80:0:0:0:200:f8ff:fe21:67cf]/") + end + + it "should not raise an error for " + + "'http://[v12.fe80::200:f8ff:fe21:67cf]/'" do + Addressable::URI.parse("http://[v12.fe80::200:f8ff:fe21:67cf]/") + end + + it "should not raise an error for " + + "'http://[va0.::1]/'" do + Addressable::URI.parse("http://[va0.::1]/") + end + + it "should not raise an error for " + + "'http://[v255.fe80::1]/'" do + Addressable::URI.parse("http://[v255.fe80::1]/") + end + + it "should raise an error for " + + "'http://[v0.]/'" do + expect do + Addressable::URI.parse("http://[v0.]/") + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/'" do + before do + @uri = Addressable::URI.parse("http://example.com/") + end + + # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence + it "should be equivalent to http://example.com" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com")) + end + + # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence + it "should be equivalent to HTTP://example.com/" do + expect(@uri).to eq(Addressable::URI.parse("HTTP://example.com/")) + end + + # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence + it "should be equivalent to http://example.com:/" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com:/")) + end + + # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence + it "should be equivalent to http://example.com:80/" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com:80/")) + end + + # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence + it "should be equivalent to http://Example.com/" do + expect(@uri).to eq(Addressable::URI.parse("http://Example.com/")) + end + + it "should have the correct username after assignment" do + @uri.user = nil + expect(@uri.user).to eq(nil) + expect(@uri.password).to eq(nil) + expect(@uri.to_s).to eq("http://example.com/") + end + + it "should have the correct password after assignment" do + @uri.password = nil + expect(@uri.password).to eq(nil) + expect(@uri.user).to eq(nil) + expect(@uri.to_s).to eq("http://example.com/") + end + + it "should have a request URI of '/'" do + expect(@uri.request_uri).to eq("/") + end + + it "should correctly convert to a hash" do + expect(@uri.to_hash).to eq({ + :scheme => "http", + :user => nil, + :password => nil, + :host => "example.com", + :port => nil, + :path => "/", + :query => nil, + :fragment => nil + }) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end + + it "should have the same hash as its duplicate" do + expect(@uri.hash).to eq(@uri.dup.hash) + end + + it "should have a different hash from its equivalent String value" do + expect(@uri.hash).not_to eq(@uri.to_s.hash) + end + + it "should have the same hash as an equal URI" do + expect(@uri.hash).to eq(Addressable::URI.parse("http://example.com/").hash) + end + + it "should be equivalent to http://EXAMPLE.com" do + expect(@uri).to eq(Addressable::URI.parse("http://EXAMPLE.com")) + end + + it "should be equivalent to http://EXAMPLE.com:80/" do + expect(@uri).to eq(Addressable::URI.parse("http://EXAMPLE.com:80/")) + end + + it "should have the same hash as http://example.com/" do + expect(@uri.hash).to eq(Addressable::URI.parse("http://example.com/").hash) + end + + it "should have the same hash as http://example.com after assignment" do + @uri.path = "" + expect(@uri.hash).to eq(Addressable::URI.parse("http://example.com").hash) + end + + it "should have the same hash as http://example.com/? after assignment" do + @uri.query = "" + expect(@uri.hash).to eq(Addressable::URI.parse("http://example.com/?").hash) + end + + it "should have the same hash as http://example.com/? after assignment" do + @uri.query_values = {} + expect(@uri.hash).to eq(Addressable::URI.parse("http://example.com/?").hash) + end + + it "should have the same hash as http://example.com/# after assignment" do + @uri.fragment = "" + expect(@uri.hash).to eq(Addressable::URI.parse("http://example.com/#").hash) + end + + it "should have a different hash from http://example.com" do + expect(@uri.hash).not_to eq(Addressable::URI.parse("http://example.com").hash) + end + + it "should have an origin of 'http://example.com'" do + expect(@uri.origin).to eq('http://example.com') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com?#'" do + before do + @uri = Addressable::URI.parse("http://example.com?#") + end + + it "should correctly convert to a hash" do + expect(@uri.to_hash).to eq({ + :scheme => "http", + :user => nil, + :password => nil, + :host => "example.com", + :port => nil, + :path => "", + :query => "", + :fragment => "" + }) + end + + it "should have a request URI of '/?'" do + expect(@uri.request_uri).to eq("/?") + end + + it "should normalize to 'http://example.com/'" do + expect(@uri.normalize.to_s).to eq("http://example.com/") + end + + it "should have an origin of 'http://example.com'" do + expect(@uri.origin).to eq("http://example.com") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://@example.com/'" do + before do + @uri = Addressable::URI.parse("http://@example.com/") + end + + it "should be equivalent to http://example.com" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com")) + end + + it "should correctly convert to a hash" do + expect(@uri.to_hash).to eq({ + :scheme => "http", + :user => "", + :password => nil, + :host => "example.com", + :port => nil, + :path => "/", + :query => nil, + :fragment => nil + }) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end + + it "should have an origin of 'http://example.com'" do + expect(@uri.origin).to eq('http://example.com') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com./'" do + before do + @uri = Addressable::URI.parse("http://example.com./") + end + + it "should be equivalent to http://example.com" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com")) + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end + + it "should have an origin of 'http://example.com'" do + expect(@uri.origin).to eq('http://example.com') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://:@example.com/'" do + before do + @uri = Addressable::URI.parse("http://:@example.com/") + end + + it "should be equivalent to http://example.com" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com")) + end + + it "should correctly convert to a hash" do + expect(@uri.to_hash).to eq({ + :scheme => "http", + :user => "", + :password => "", + :host => "example.com", + :port => nil, + :path => "/", + :query => nil, + :fragment => nil + }) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end + + it "should have an origin of 'http://example.com'" do + expect(@uri.origin).to eq('http://example.com') + end +end + +describe Addressable::URI, "when parsed from " + + "'HTTP://EXAMPLE.COM/'" do + before do + @uri = Addressable::URI.parse("HTTP://EXAMPLE.COM/") + end + + it "should be equivalent to http://example.com" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com")) + end + + it "should correctly convert to a hash" do + expect(@uri.to_hash).to eq({ + :scheme => "HTTP", + :user => nil, + :password => nil, + :host => "EXAMPLE.COM", + :port => nil, + :path => "/", + :query => nil, + :fragment => nil + }) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end + + it "should have an origin of 'http://example.com'" do + expect(@uri.origin).to eq('http://example.com') + end + + it "should have a tld of 'com'" do + expect(@uri.tld).to eq('com') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://www.example.co.uk/'" do + before do + @uri = Addressable::URI.parse("http://www.example.co.uk/") + end + + it "should have an origin of 'http://www.example.co.uk'" do + expect(@uri.origin).to eq('http://www.example.co.uk') + end + + it "should have a tld of 'co.uk'" do + expect(@uri.tld).to eq('co.uk') + end + + it "should have a domain of 'example.co.uk'" do + expect(@uri.domain).to eq('example.co.uk') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://sub_domain.blogspot.com/'" do + before do + @uri = Addressable::URI.parse("http://sub_domain.blogspot.com/") + end + + it "should have an origin of 'http://sub_domain.blogspot.com'" do + expect(@uri.origin).to eq('http://sub_domain.blogspot.com') + end + + it "should have a tld of 'com'" do + expect(@uri.tld).to eq('com') + end + + it "should have a domain of 'blogspot.com'" do + expect(@uri.domain).to eq('blogspot.com') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/~smith/'" do + before do + @uri = Addressable::URI.parse("http://example.com/~smith/") + end + + # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence + it "should be equivalent to http://example.com/%7Esmith/" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com/%7Esmith/")) + end + + # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence + it "should be equivalent to http://example.com/%7esmith/" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com/%7esmith/")) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/%E8'" do + before do + @uri = Addressable::URI.parse("http://example.com/%E8") + end + + it "should not raise an exception when normalized" do + expect do + @uri.normalize + end.not_to raise_error + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should not change if encoded with the normalizing algorithm" do + expect(Addressable::URI.normalized_encode(@uri).to_s).to eq( + "http://example.com/%E8" + ) + expect(Addressable::URI.normalized_encode(@uri, Addressable::URI).to_s).to be === + "http://example.com/%E8" + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/path%2Fsegment/'" do + before do + @uri = Addressable::URI.parse("http://example.com/path%2Fsegment/") + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should be equal to 'http://example.com/path%2Fsegment/'" do + expect(@uri.normalize).to be_eql( + Addressable::URI.parse("http://example.com/path%2Fsegment/") + ) + end + + it "should not be equal to 'http://example.com/path/segment/'" do + expect(@uri).not_to eq( + Addressable::URI.parse("http://example.com/path/segment/") + ) + end + + it "should not be equal to 'http://example.com/path/segment/'" do + expect(@uri.normalize).not_to be_eql( + Addressable::URI.parse("http://example.com/path/segment/") + ) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?%F6'" do + before do + @uri = Addressable::URI.parse("http://example.com/?%F6") + end + + it "should not raise an exception when normalized" do + expect do + @uri.normalize + end.not_to raise_error + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should not change if encoded with the normalizing algorithm" do + expect(Addressable::URI.normalized_encode(@uri).to_s).to eq( + "http://example.com/?%F6" + ) + expect(Addressable::URI.normalized_encode(@uri, Addressable::URI).to_s).to be === + "http://example.com/?%F6" + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/#%F6'" do + before do + @uri = Addressable::URI.parse("http://example.com/#%F6") + end + + it "should not raise an exception when normalized" do + expect do + @uri.normalize + end.not_to raise_error + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should not change if encoded with the normalizing algorithm" do + expect(Addressable::URI.normalized_encode(@uri).to_s).to eq( + "http://example.com/#%F6" + ) + expect(Addressable::URI.normalized_encode(@uri, Addressable::URI).to_s).to be === + "http://example.com/#%F6" + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/%C3%87'" do + before do + @uri = Addressable::URI.parse("http://example.com/%C3%87") + end + + # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence + it "should be equivalent to 'http://example.com/C%CC%A7'" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com/C%CC%A7")) + end + + it "should not change if encoded with the normalizing algorithm" do + expect(Addressable::URI.normalized_encode(@uri).to_s).to eq( + "http://example.com/%C3%87" + ) + expect(Addressable::URI.normalized_encode(@uri, Addressable::URI).to_s).to be === + "http://example.com/%C3%87" + end + + it "should raise an error if encoding with an unexpected return type" do + expect do + Addressable::URI.normalized_encode(@uri, Integer) + end.to raise_error(TypeError) + end + + it "if percent encoded should be 'http://example.com/C%25CC%25A7'" do + expect(Addressable::URI.encode(@uri).to_s).to eq( + "http://example.com/%25C3%2587" + ) + end + + it "if percent encoded should be 'http://example.com/C%25CC%25A7'" do + expect(Addressable::URI.encode(@uri, Addressable::URI)).to eq( + Addressable::URI.parse("http://example.com/%25C3%2587") + ) + end + + it "should raise an error if encoding with an unexpected return type" do + expect do + Addressable::URI.encode(@uri, Integer) + end.to raise_error(TypeError) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?q=string'" do + before do + @uri = Addressable::URI.parse("http://example.com/?q=string") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have an authority segment of 'example.com'" do + expect(@uri.authority).to eq("example.com") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should have no username" do + expect(@uri.user).to eq(nil) + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have a path of '/'" do + expect(@uri.path).to eq("/") + end + + it "should have a query string of 'q=string'" do + expect(@uri.query).to eq("q=string") + end + + it "should have no fragment" do + expect(@uri.fragment).to eq(nil) + end + + it "should be considered absolute" do + expect(@uri).to be_absolute + end + + it "should not be considered relative" do + expect(@uri).not_to be_relative + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com:80/'" do + before do + @uri = Addressable::URI.parse("http://example.com:80/") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have an authority segment of 'example.com:80'" do + expect(@uri.authority).to eq("example.com:80") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should have no username" do + expect(@uri.user).to eq(nil) + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have explicit port 80" do + expect(@uri.port).to eq(80) + end + + it "should have a path of '/'" do + expect(@uri.path).to eq("/") + end + + it "should have no query string" do + expect(@uri.query).to eq(nil) + end + + it "should have no fragment" do + expect(@uri.fragment).to eq(nil) + end + + it "should be considered absolute" do + expect(@uri).to be_absolute + end + + it "should not be considered relative" do + expect(@uri).not_to be_relative + end + + it "should be exactly equal to http://example.com:80/" do + expect(@uri.eql?(Addressable::URI.parse("http://example.com:80/"))).to eq(true) + end + + it "should be roughly equal to http://example.com/" do + expect(@uri === Addressable::URI.parse("http://example.com/")).to eq(true) + end + + it "should be roughly equal to the string 'http://example.com/'" do + expect(@uri === "http://example.com/").to eq(true) + end + + it "should not be roughly equal to the string " + + "'http://example.com:bogus/'" do + expect do + expect(@uri === "http://example.com:bogus/").to eq(false) + end.not_to raise_error + end + + it "should result in itself when joined with itself" do + expect(@uri.join(@uri).to_s).to eq("http://example.com:80/") + expect(@uri.join!(@uri).to_s).to eq("http://example.com:80/") + end + + # Section 6.2.3 of RFC 3986 + it "should be equal to http://example.com/" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com/")) + end + + # Section 6.2.3 of RFC 3986 + it "should be equal to http://example.com:/" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com:/")) + end + + # Section 6.2.3 of RFC 3986 + it "should be equal to http://example.com:80/" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com:80/")) + end + + # Section 6.2.2.1 of RFC 3986 + it "should be equal to http://EXAMPLE.COM/" do + expect(@uri).to eq(Addressable::URI.parse("http://EXAMPLE.COM/")) + end + + it "should correctly convert to a hash" do + expect(@uri.to_hash).to eq({ + :scheme => "http", + :user => nil, + :password => nil, + :host => "example.com", + :port => 80, + :path => "/", + :query => nil, + :fragment => nil + }) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end + + it "should have an origin of 'http://example.com'" do + expect(@uri.origin).to eq('http://example.com') + end + + it "should not change if encoded with the normalizing algorithm" do + expect(Addressable::URI.normalized_encode(@uri).to_s).to eq( + "http://example.com:80/" + ) + expect(Addressable::URI.normalized_encode(@uri, Addressable::URI).to_s).to be === + "http://example.com:80/" + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com:8080/'" do + before do + @uri = Addressable::URI.parse("http://example.com:8080/") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have an authority segment of 'example.com:8080'" do + expect(@uri.authority).to eq("example.com:8080") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should have no username" do + expect(@uri.user).to eq(nil) + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should use port 8080" do + expect(@uri.inferred_port).to eq(8080) + end + + it "should have explicit port 8080" do + expect(@uri.port).to eq(8080) + end + + it "should have default port 80" do + expect(@uri.default_port).to eq(80) + end + + it "should have a path of '/'" do + expect(@uri.path).to eq("/") + end + + it "should have no query string" do + expect(@uri.query).to eq(nil) + end + + it "should have no fragment" do + expect(@uri.fragment).to eq(nil) + end + + it "should be considered absolute" do + expect(@uri).to be_absolute + end + + it "should not be considered relative" do + expect(@uri).not_to be_relative + end + + it "should be exactly equal to http://example.com:8080/" do + expect(@uri.eql?(Addressable::URI.parse( + "http://example.com:8080/"))).to eq(true) + end + + it "should have a route of 'http://example.com:8080/' from " + + "'http://example.com/path/to/'" do + expect(@uri.route_from("http://example.com/path/to/")).to eq( + Addressable::URI.parse("http://example.com:8080/") + ) + end + + it "should have a route of 'http://example.com:8080/' from " + + "'http://example.com:80/path/to/'" do + expect(@uri.route_from("http://example.com:80/path/to/")).to eq( + Addressable::URI.parse("http://example.com:8080/") + ) + end + + it "should have a route of '../../' from " + + "'http://example.com:8080/path/to/'" do + expect(@uri.route_from("http://example.com:8080/path/to/")).to eq( + Addressable::URI.parse("../../") + ) + end + + it "should have a route of 'http://example.com:8080/' from " + + "'http://user:pass@example.com/path/to/'" do + expect(@uri.route_from("http://user:pass@example.com/path/to/")).to eq( + Addressable::URI.parse("http://example.com:8080/") + ) + end + + it "should correctly convert to a hash" do + expect(@uri.to_hash).to eq({ + :scheme => "http", + :user => nil, + :password => nil, + :host => "example.com", + :port => 8080, + :path => "/", + :query => nil, + :fragment => nil + }) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end + + it "should have an origin of 'http://example.com:8080'" do + expect(@uri.origin).to eq('http://example.com:8080') + end + + it "should not change if encoded with the normalizing algorithm" do + expect(Addressable::URI.normalized_encode(@uri).to_s).to eq( + "http://example.com:8080/" + ) + expect(Addressable::URI.normalized_encode(@uri, Addressable::URI).to_s).to be === + "http://example.com:8080/" + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com:%38%30/'" do + before do + @uri = Addressable::URI.parse("http://example.com:%38%30/") + end + + it "should have the correct port" do + expect(@uri.port).to eq(80) + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + it "should normalize to 'http://example.com/'" do + expect(@uri.normalize.to_s).to eq("http://example.com/") + end + + it "should have an origin of 'http://example.com'" do + expect(@uri.origin).to eq('http://example.com') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/%2E/'" do + before do + @uri = Addressable::URI.parse("http://example.com/%2E/") + end + + it "should be considered to be in normal form" do + skip( + 'path segment normalization should happen before ' + + 'percent escaping normalization' + ) + @uri.normalize.should be_eql(@uri) + end + + it "should normalize to 'http://example.com/%2E/'" do + skip( + 'path segment normalization should happen before ' + + 'percent escaping normalization' + ) + expect(@uri.normalize).to eq("http://example.com/%2E/") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/..'" do + before do + @uri = Addressable::URI.parse("http://example.com/..") + end + + it "should have the correct port" do + expect(@uri.inferred_port).to eq(80) + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + it "should normalize to 'http://example.com/'" do + expect(@uri.normalize.to_s).to eq("http://example.com/") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/../..'" do + before do + @uri = Addressable::URI.parse("http://example.com/../..") + end + + it "should have the correct port" do + expect(@uri.inferred_port).to eq(80) + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + it "should normalize to 'http://example.com/'" do + expect(@uri.normalize.to_s).to eq("http://example.com/") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/path(/..'" do + before do + @uri = Addressable::URI.parse("http://example.com/path(/..") + end + + it "should have the correct port" do + expect(@uri.inferred_port).to eq(80) + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + it "should normalize to 'http://example.com/'" do + expect(@uri.normalize.to_s).to eq("http://example.com/") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/(path)/..'" do + before do + @uri = Addressable::URI.parse("http://example.com/(path)/..") + end + + it "should have the correct port" do + expect(@uri.inferred_port).to eq(80) + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + it "should normalize to 'http://example.com/'" do + expect(@uri.normalize.to_s).to eq("http://example.com/") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/path(/../'" do + before do + @uri = Addressable::URI.parse("http://example.com/path(/../") + end + + it "should have the correct port" do + expect(@uri.inferred_port).to eq(80) + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + it "should normalize to 'http://example.com/'" do + expect(@uri.normalize.to_s).to eq("http://example.com/") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/(path)/../'" do + before do + @uri = Addressable::URI.parse("http://example.com/(path)/../") + end + + it "should have the correct port" do + expect(@uri.inferred_port).to eq(80) + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + it "should normalize to 'http://example.com/'" do + expect(@uri.normalize.to_s).to eq("http://example.com/") + end +end + +describe Addressable::URI, "when parsed from " + + "'/..//example.com'" do + before do + @uri = Addressable::URI.parse("/..//example.com") + end + + it "should become invalid when normalized" do + expect do + @uri.normalize + end.to raise_error(Addressable::URI::InvalidURIError, /authority/) + end + + it "should have a path of '/..//example.com'" do + expect(@uri.path).to eq("/..//example.com") + end +end + +describe Addressable::URI, "when parsed from '/a/b/c/./../../g'" do + before do + @uri = Addressable::URI.parse("/a/b/c/./../../g") + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + # Section 5.2.4 of RFC 3986 + it "should normalize to '/a/g'" do + expect(@uri.normalize.to_s).to eq("/a/g") + end +end + +describe Addressable::URI, "when parsed from 'mid/content=5/../6'" do + before do + @uri = Addressable::URI.parse("mid/content=5/../6") + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + # Section 5.2.4 of RFC 3986 + it "should normalize to 'mid/6'" do + expect(@uri.normalize.to_s).to eq("mid/6") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://www.example.com///../'" do + before do + @uri = Addressable::URI.parse('http://www.example.com///../') + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + it "should normalize to 'http://www.example.com//'" do + expect(@uri.normalize.to_s).to eq("http://www.example.com//") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/path/to/resource/'" do + before do + @uri = Addressable::URI.parse("http://example.com/path/to/resource/") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have an authority segment of 'example.com'" do + expect(@uri.authority).to eq("example.com") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should have no username" do + expect(@uri.user).to eq(nil) + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have a path of '/path/to/resource/'" do + expect(@uri.path).to eq("/path/to/resource/") + end + + it "should have no query string" do + expect(@uri.query).to eq(nil) + end + + it "should have no fragment" do + expect(@uri.fragment).to eq(nil) + end + + it "should be considered absolute" do + expect(@uri).to be_absolute + end + + it "should not be considered relative" do + expect(@uri).not_to be_relative + end + + it "should be exactly equal to http://example.com:8080/" do + expect(@uri.eql?(Addressable::URI.parse( + "http://example.com/path/to/resource/"))).to eq(true) + end + + it "should have a route of 'resource/' from " + + "'http://example.com/path/to/'" do + expect(@uri.route_from("http://example.com/path/to/")).to eq( + Addressable::URI.parse("resource/") + ) + end + + it "should have a route of '../' from " + + "'http://example.com/path/to/resource/sub'" do + expect(@uri.route_from("http://example.com/path/to/resource/sub")).to eq( + Addressable::URI.parse("../") + ) + end + + + it "should have a route of 'resource/' from " + + "'http://example.com/path/to/another'" do + expect(@uri.route_from("http://example.com/path/to/another")).to eq( + Addressable::URI.parse("resource/") + ) + end + + it "should have a route of 'resource/' from " + + "'http://example.com/path/to/res'" do + expect(@uri.route_from("http://example.com/path/to/res")).to eq( + Addressable::URI.parse("resource/") + ) + end + + it "should have a route of 'resource/' from " + + "'http://example.com:80/path/to/'" do + expect(@uri.route_from("http://example.com:80/path/to/")).to eq( + Addressable::URI.parse("resource/") + ) + end + + it "should have a route of 'http://example.com/path/to/' from " + + "'http://example.com:8080/path/to/'" do + expect(@uri.route_from("http://example.com:8080/path/to/")).to eq( + Addressable::URI.parse("http://example.com/path/to/resource/") + ) + end + + it "should have a route of 'http://example.com/path/to/' from " + + "'http://user:pass@example.com/path/to/'" do + expect(@uri.route_from("http://user:pass@example.com/path/to/")).to eq( + Addressable::URI.parse("http://example.com/path/to/resource/") + ) + end + + it "should have a route of '../../path/to/resource/' from " + + "'http://example.com/to/resource/'" do + expect(@uri.route_from("http://example.com/to/resource/")).to eq( + Addressable::URI.parse("../../path/to/resource/") + ) + end + + it "should correctly convert to a hash" do + expect(@uri.to_hash).to eq({ + :scheme => "http", + :user => nil, + :password => nil, + :host => "example.com", + :port => nil, + :path => "/path/to/resource/", + :query => nil, + :fragment => nil + }) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end +end + +describe Addressable::URI, "when parsed from " + + "'relative/path/to/resource'" do + before do + @uri = Addressable::URI.parse("relative/path/to/resource") + end + + it "should not have a scheme" do + expect(@uri.scheme).to eq(nil) + end + + it "should not be considered ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should not have an authority segment" do + expect(@uri.authority).to eq(nil) + end + + it "should not have a host" do + expect(@uri.host).to eq(nil) + end + + it "should have no username" do + expect(@uri.user).to eq(nil) + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should not have a port" do + expect(@uri.port).to eq(nil) + end + + it "should have a path of 'relative/path/to/resource'" do + expect(@uri.path).to eq("relative/path/to/resource") + end + + it "should have no query string" do + expect(@uri.query).to eq(nil) + end + + it "should have no fragment" do + expect(@uri.fragment).to eq(nil) + end + + it "should not be considered absolute" do + expect(@uri).not_to be_absolute + end + + it "should be considered relative" do + expect(@uri).to be_relative + end + + it "should raise an error if routing is attempted" do + expect do + @uri.route_to("http://example.com/") + end.to raise_error(ArgumentError, /relative\/path\/to\/resource/) + expect do + @uri.route_from("http://example.com/") + end.to raise_error(ArgumentError, /relative\/path\/to\/resource/) + end + + it "when joined with 'another/relative/path' should be " + + "'relative/path/to/another/relative/path'" do + expect(@uri.join('another/relative/path')).to eq( + Addressable::URI.parse("relative/path/to/another/relative/path") + ) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end +end + +describe Addressable::URI, "when parsed from " + + "'relative_path_with_no_slashes'" do + before do + @uri = Addressable::URI.parse("relative_path_with_no_slashes") + end + + it "should not have a scheme" do + expect(@uri.scheme).to eq(nil) + end + + it "should not be considered ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should not have an authority segment" do + expect(@uri.authority).to eq(nil) + end + + it "should not have a host" do + expect(@uri.host).to eq(nil) + end + + it "should have no username" do + expect(@uri.user).to eq(nil) + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should not have a port" do + expect(@uri.port).to eq(nil) + end + + it "should have a path of 'relative_path_with_no_slashes'" do + expect(@uri.path).to eq("relative_path_with_no_slashes") + end + + it "should have no query string" do + expect(@uri.query).to eq(nil) + end + + it "should have no fragment" do + expect(@uri.fragment).to eq(nil) + end + + it "should not be considered absolute" do + expect(@uri).not_to be_absolute + end + + it "should be considered relative" do + expect(@uri).to be_relative + end + + it "when joined with 'another_relative_path' should be " + + "'another_relative_path'" do + expect(@uri.join('another_relative_path')).to eq( + Addressable::URI.parse("another_relative_path") + ) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/file.txt'" do + before do + @uri = Addressable::URI.parse("http://example.com/file.txt") + end + + it "should have a scheme of 'http'" do + expect(@uri.scheme).to eq("http") + end + + it "should have an authority segment of 'example.com'" do + expect(@uri.authority).to eq("example.com") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should have no username" do + expect(@uri.user).to eq(nil) + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have a path of '/file.txt'" do + expect(@uri.path).to eq("/file.txt") + end + + it "should have a basename of 'file.txt'" do + expect(@uri.basename).to eq("file.txt") + end + + it "should have an extname of '.txt'" do + expect(@uri.extname).to eq(".txt") + end + + it "should have no query string" do + expect(@uri.query).to eq(nil) + end + + it "should have no fragment" do + expect(@uri.fragment).to eq(nil) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/file.txt;parameter'" do + before do + @uri = Addressable::URI.parse("http://example.com/file.txt;parameter") + end + + it "should have a scheme of 'http'" do + expect(@uri.scheme).to eq("http") + end + + it "should have an authority segment of 'example.com'" do + expect(@uri.authority).to eq("example.com") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should have no username" do + expect(@uri.user).to eq(nil) + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have a path of '/file.txt;parameter'" do + expect(@uri.path).to eq("/file.txt;parameter") + end + + it "should have a basename of 'file.txt'" do + expect(@uri.basename).to eq("file.txt") + end + + it "should have an extname of '.txt'" do + expect(@uri.extname).to eq(".txt") + end + + it "should have no query string" do + expect(@uri.query).to eq(nil) + end + + it "should have no fragment" do + expect(@uri.fragment).to eq(nil) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/file.txt;x=y'" do + before do + @uri = Addressable::URI.parse("http://example.com/file.txt;x=y") + end + + it "should have a scheme of 'http'" do + expect(@uri.scheme).to eq("http") + end + + it "should have a scheme of 'http'" do + expect(@uri.scheme).to eq("http") + end + + it "should have an authority segment of 'example.com'" do + expect(@uri.authority).to eq("example.com") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should have no username" do + expect(@uri.user).to eq(nil) + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have a path of '/file.txt;x=y'" do + expect(@uri.path).to eq("/file.txt;x=y") + end + + it "should have an extname of '.txt'" do + expect(@uri.extname).to eq(".txt") + end + + it "should have no query string" do + expect(@uri.query).to eq(nil) + end + + it "should have no fragment" do + expect(@uri.fragment).to eq(nil) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end +end + +describe Addressable::URI, "when parsed from " + + "'svn+ssh://developername@rubyforge.org/var/svn/project'" do + before do + @uri = Addressable::URI.parse( + "svn+ssh://developername@rubyforge.org/var/svn/project" + ) + end + + it "should have a scheme of 'svn+ssh'" do + expect(@uri.scheme).to eq("svn+ssh") + end + + it "should be considered to be ip-based" do + expect(@uri).to be_ip_based + end + + it "should have a path of '/var/svn/project'" do + expect(@uri.path).to eq("/var/svn/project") + end + + it "should have a username of 'developername'" do + expect(@uri.user).to eq("developername") + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end +end + +describe Addressable::URI, "when parsed from " + + "'ssh+svn://developername@RUBYFORGE.ORG/var/svn/project'" do + before do + @uri = Addressable::URI.parse( + "ssh+svn://developername@RUBYFORGE.ORG/var/svn/project" + ) + end + + it "should have a scheme of 'ssh+svn'" do + expect(@uri.scheme).to eq("ssh+svn") + end + + it "should have a normalized scheme of 'svn+ssh'" do + expect(@uri.normalized_scheme).to eq("svn+ssh") + end + + it "should have a normalized site of 'svn+ssh'" do + expect(@uri.normalized_site).to eq("svn+ssh://developername@rubyforge.org") + end + + it "should not be considered to be ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should have a path of '/var/svn/project'" do + expect(@uri.path).to eq("/var/svn/project") + end + + it "should have a username of 'developername'" do + expect(@uri.user).to eq("developername") + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end +end + +describe Addressable::URI, "when parsed from " + + "'mailto:user@example.com'" do + before do + @uri = Addressable::URI.parse("mailto:user@example.com") + end + + it "should have a scheme of 'mailto'" do + expect(@uri.scheme).to eq("mailto") + end + + it "should not be considered to be ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should have a path of 'user@example.com'" do + expect(@uri.path).to eq("user@example.com") + end + + it "should have no user" do + expect(@uri.user).to eq(nil) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end +end + +describe Addressable::URI, "when parsed from " + + "'tag:example.com,2006-08-18:/path/to/something'" do + before do + @uri = Addressable::URI.parse( + "tag:example.com,2006-08-18:/path/to/something") + end + + it "should have a scheme of 'tag'" do + expect(@uri.scheme).to eq("tag") + end + + it "should be considered to be ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should have a path of " + + "'example.com,2006-08-18:/path/to/something'" do + expect(@uri.path).to eq("example.com,2006-08-18:/path/to/something") + end + + it "should have no user" do + expect(@uri.user).to eq(nil) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/x;y/'" do + before do + @uri = Addressable::URI.parse("http://example.com/x;y/") + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?x=1&y=2'" do + before do + @uri = Addressable::URI.parse("http://example.com/?x=1&y=2") + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end +end + +describe Addressable::URI, "when parsed from " + + "'view-source:http://example.com/'" do + before do + @uri = Addressable::URI.parse("view-source:http://example.com/") + end + + it "should have a scheme of 'view-source'" do + expect(@uri.scheme).to eq("view-source") + end + + it "should have a path of 'http://example.com/'" do + expect(@uri.path).to eq("http://example.com/") + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://user:pass@example.com/path/to/resource?query=x#fragment'" do + before do + @uri = Addressable::URI.parse( + "http://user:pass@example.com/path/to/resource?query=x#fragment") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have an authority segment of 'user:pass@example.com'" do + expect(@uri.authority).to eq("user:pass@example.com") + end + + it "should have a username of 'user'" do + expect(@uri.user).to eq("user") + end + + it "should have a password of 'pass'" do + expect(@uri.password).to eq("pass") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have a path of '/path/to/resource'" do + expect(@uri.path).to eq("/path/to/resource") + end + + it "should have a query string of 'query=x'" do + expect(@uri.query).to eq("query=x") + end + + it "should have a fragment of 'fragment'" do + expect(@uri.fragment).to eq("fragment") + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have a route of '../../' to " + + "'http://user:pass@example.com/path/'" do + expect(@uri.route_to("http://user:pass@example.com/path/")).to eq( + Addressable::URI.parse("../../") + ) + end + + it "should have a route of 'to/resource?query=x#fragment' " + + "from 'http://user:pass@example.com/path/'" do + expect(@uri.route_from("http://user:pass@example.com/path/")).to eq( + Addressable::URI.parse("to/resource?query=x#fragment") + ) + end + + it "should have a route of '?query=x#fragment' " + + "from 'http://user:pass@example.com/path/to/resource'" do + expect(@uri.route_from("http://user:pass@example.com/path/to/resource")).to eq( + Addressable::URI.parse("?query=x#fragment") + ) + end + + it "should have a route of '#fragment' " + + "from 'http://user:pass@example.com/path/to/resource?query=x'" do + expect(@uri.route_from( + "http://user:pass@example.com/path/to/resource?query=x")).to eq( + Addressable::URI.parse("#fragment") + ) + end + + it "should have a route of '#fragment' from " + + "'http://user:pass@example.com/path/to/resource?query=x#fragment'" do + expect(@uri.route_from( + "http://user:pass@example.com/path/to/resource?query=x#fragment" + )).to eq(Addressable::URI.parse("#fragment")) + end + + it "should have a route of 'http://elsewhere.com/' to " + + "'http://elsewhere.com/'" do + expect(@uri.route_to("http://elsewhere.com/")).to eq( + Addressable::URI.parse("http://elsewhere.com/") + ) + end + + it "should have a route of " + + "'http://user:pass@example.com/path/to/resource?query=x#fragment' " + + "from 'http://example.com/path/to/'" do + expect(@uri.route_from("http://elsewhere.com/path/to/")).to eq( + Addressable::URI.parse( + "http://user:pass@example.com/path/to/resource?query=x#fragment") + ) + end + + it "should have the correct scheme after assignment" do + @uri.scheme = "ftp" + expect(@uri.scheme).to eq("ftp") + expect(@uri.to_s).to eq( + "ftp://user:pass@example.com/path/to/resource?query=x#fragment" + ) + expect(@uri.to_str).to eq( + "ftp://user:pass@example.com/path/to/resource?query=x#fragment" + ) + end + + it "should have the correct site segment after assignment" do + @uri.site = "https://newuser:newpass@example.com:443" + expect(@uri.scheme).to eq("https") + expect(@uri.authority).to eq("newuser:newpass@example.com:443") + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq("newpass") + expect(@uri.userinfo).to eq("newuser:newpass") + expect(@uri.normalized_userinfo).to eq("newuser:newpass") + expect(@uri.host).to eq("example.com") + expect(@uri.port).to eq(443) + expect(@uri.inferred_port).to eq(443) + expect(@uri.to_s).to eq( + "https://newuser:newpass@example.com:443" + + "/path/to/resource?query=x#fragment" + ) + end + + it "should have the correct authority segment after assignment" do + @uri.authority = "newuser:newpass@example.com:80" + expect(@uri.authority).to eq("newuser:newpass@example.com:80") + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq("newpass") + expect(@uri.userinfo).to eq("newuser:newpass") + expect(@uri.normalized_userinfo).to eq("newuser:newpass") + expect(@uri.host).to eq("example.com") + expect(@uri.port).to eq(80) + expect(@uri.inferred_port).to eq(80) + expect(@uri.to_s).to eq( + "http://newuser:newpass@example.com:80" + + "/path/to/resource?query=x#fragment" + ) + end + + it "should have the correct userinfo segment after assignment" do + @uri.userinfo = "newuser:newpass" + expect(@uri.userinfo).to eq("newuser:newpass") + expect(@uri.authority).to eq("newuser:newpass@example.com") + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq("newpass") + expect(@uri.host).to eq("example.com") + expect(@uri.port).to eq(nil) + expect(@uri.inferred_port).to eq(80) + expect(@uri.to_s).to eq( + "http://newuser:newpass@example.com" + + "/path/to/resource?query=x#fragment" + ) + end + + it "should have the correct username after assignment" do + @uri.user = "newuser" + expect(@uri.user).to eq("newuser") + expect(@uri.authority).to eq("newuser:pass@example.com") + end + + it "should have the correct password after assignment" do + @uri.password = "newpass" + expect(@uri.password).to eq("newpass") + expect(@uri.authority).to eq("user:newpass@example.com") + end + + it "should have the correct host after assignment" do + @uri.host = "newexample.com" + expect(@uri.host).to eq("newexample.com") + expect(@uri.authority).to eq("user:pass@newexample.com") + end + + it "should have the correct host after assignment" do + @uri.hostname = "newexample.com" + expect(@uri.host).to eq("newexample.com") + expect(@uri.hostname).to eq("newexample.com") + expect(@uri.authority).to eq("user:pass@newexample.com") + end + + it "should raise an error if assigning a bogus object to the hostname" do + expect do + @uri.hostname = Object.new + end.to raise_error(TypeError) + end + + it "should have the correct port after assignment" do + @uri.port = 8080 + expect(@uri.port).to eq(8080) + expect(@uri.authority).to eq("user:pass@example.com:8080") + end + + it "should have the correct origin after assignment" do + @uri.origin = "http://newexample.com" + expect(@uri.host).to eq("newexample.com") + expect(@uri.authority).to eq("newexample.com") + end + + it "should have the correct path after assignment" do + @uri.path = "/newpath/to/resource" + expect(@uri.path).to eq("/newpath/to/resource") + expect(@uri.to_s).to eq( + "http://user:pass@example.com/newpath/to/resource?query=x#fragment" + ) + end + + it "should have the correct scheme and authority after nil assignment" do + @uri.site = nil + expect(@uri.scheme).to eq(nil) + expect(@uri.authority).to eq(nil) + expect(@uri.to_s).to eq("/path/to/resource?query=x#fragment") + end + + it "should have the correct scheme and authority after assignment" do + @uri.site = "file://" + expect(@uri.scheme).to eq("file") + expect(@uri.authority).to eq("") + expect(@uri.to_s).to eq("file:///path/to/resource?query=x#fragment") + end + + it "should have the correct path after nil assignment" do + @uri.path = nil + expect(@uri.path).to eq("") + expect(@uri.to_s).to eq( + "http://user:pass@example.com?query=x#fragment" + ) + end + + it "should have the correct query string after assignment" do + @uri.query = "newquery=x" + expect(@uri.query).to eq("newquery=x") + expect(@uri.to_s).to eq( + "http://user:pass@example.com/path/to/resource?newquery=x#fragment" + ) + @uri.query = nil + expect(@uri.query).to eq(nil) + expect(@uri.to_s).to eq( + "http://user:pass@example.com/path/to/resource#fragment" + ) + end + + it "should have the correct query string after hash assignment" do + @uri.query_values = {"?uestion mark" => "=sign", "hello" => "g\xC3\xBCnther"} + expect(@uri.query.split("&")).to include("%3Fuestion%20mark=%3Dsign") + expect(@uri.query.split("&")).to include("hello=g%C3%BCnther") + expect(@uri.query_values).to eq({ + "?uestion mark" => "=sign", "hello" => "g\xC3\xBCnther" + }) + end + + it "should have the correct query string after flag hash assignment" do + @uri.query_values = {'flag?1' => nil, 'fl=ag2' => nil, 'flag3' => nil} + expect(@uri.query.split("&")).to include("flag%3F1") + expect(@uri.query.split("&")).to include("fl%3Dag2") + expect(@uri.query.split("&")).to include("flag3") + expect(@uri.query_values(Array).sort).to eq([["fl=ag2"], ["flag3"], ["flag?1"]]) + expect(@uri.query_values(Hash)).to eq({ + 'flag?1' => nil, 'fl=ag2' => nil, 'flag3' => nil + }) + end + + it "should raise an error if query values are set to a bogus type" do + expect do + @uri.query_values = "bogus" + end.to raise_error(TypeError) + end + + it "should have the correct fragment after assignment" do + @uri.fragment = "newfragment" + expect(@uri.fragment).to eq("newfragment") + expect(@uri.to_s).to eq( + "http://user:pass@example.com/path/to/resource?query=x#newfragment" + ) + + @uri.fragment = nil + expect(@uri.fragment).to eq(nil) + expect(@uri.to_s).to eq( + "http://user:pass@example.com/path/to/resource?query=x" + ) + end + + it "should have the correct values after a merge" do + expect(@uri.merge(:fragment => "newfragment").to_s).to eq( + "http://user:pass@example.com/path/to/resource?query=x#newfragment" + ) + end + + it "should have the correct values after a merge" do + expect(@uri.merge(:fragment => nil).to_s).to eq( + "http://user:pass@example.com/path/to/resource?query=x" + ) + end + + it "should have the correct values after a merge" do + expect(@uri.merge(:userinfo => "newuser:newpass").to_s).to eq( + "http://newuser:newpass@example.com/path/to/resource?query=x#fragment" + ) + end + + it "should have the correct values after a merge" do + expect(@uri.merge(:userinfo => nil).to_s).to eq( + "http://example.com/path/to/resource?query=x#fragment" + ) + end + + it "should have the correct values after a merge" do + expect(@uri.merge(:path => "newpath").to_s).to eq( + "http://user:pass@example.com/newpath?query=x#fragment" + ) + end + + it "should have the correct values after a merge" do + expect(@uri.merge(:port => "42", :path => "newpath", :query => "").to_s).to eq( + "http://user:pass@example.com:42/newpath?#fragment" + ) + end + + it "should have the correct values after a merge" do + expect(@uri.merge(:authority => "foo:bar@baz:42").to_s).to eq( + "http://foo:bar@baz:42/path/to/resource?query=x#fragment" + ) + # Ensure the operation was not destructive + expect(@uri.to_s).to eq( + "http://user:pass@example.com/path/to/resource?query=x#fragment" + ) + end + + it "should have the correct values after a destructive merge" do + @uri.merge!(:authority => "foo:bar@baz:42") + # Ensure the operation was destructive + expect(@uri.to_s).to eq( + "http://foo:bar@baz:42/path/to/resource?query=x#fragment" + ) + end + + it "should fail to merge with bogus values" do + expect do + @uri.merge(:port => "bogus") + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should fail to merge with bogus values" do + expect do + @uri.merge(:authority => "bar@baz:bogus") + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should fail to merge with bogus parameters" do + expect do + @uri.merge(42) + end.to raise_error(TypeError) + end + + it "should fail to merge with bogus parameters" do + expect do + @uri.merge("http://example.com/") + end.to raise_error(TypeError) + end + + it "should fail to merge with both authority and subcomponents" do + expect do + @uri.merge(:authority => "foo:bar@baz:42", :port => "42") + end.to raise_error(ArgumentError) + end + + it "should fail to merge with both userinfo and subcomponents" do + expect do + @uri.merge(:userinfo => "foo:bar", :user => "foo") + end.to raise_error(ArgumentError) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end + + it "should have an origin of 'http://example.com'" do + expect(@uri.origin).to eq('http://example.com') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/search?q=Q%26A'" do + + before do + @uri = Addressable::URI.parse("http://example.com/search?q=Q%26A") + end + + it "should have a query of 'q=Q%26A'" do + expect(@uri.query).to eq("q=Q%26A") + end + + it "should have query_values of {'q' => 'Q&A'}" do + expect(@uri.query_values).to eq({ 'q' => 'Q&A' }) + end + + it "should normalize to the original uri " + + "(with the ampersand properly percent-encoded)" do + expect(@uri.normalize.to_s).to eq("http://example.com/search?q=Q%26A") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?&x=b'" do + before do + @uri = Addressable::URI.parse("http://example.com/?&x=b") + end + + it "should have a query of '&x=b'" do + expect(@uri.query).to eq("&x=b") + end + + it "should have query_values of {'x' => 'b'}" do + expect(@uri.query_values).to eq({'x' => 'b'}) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?q='one;two'&x=1'" do + before do + @uri = Addressable::URI.parse("http://example.com/?q='one;two'&x=1") + end + + it "should have a query of 'q='one;two'&x=1'" do + expect(@uri.query).to eq("q='one;two'&x=1") + end + + it "should have query_values of {\"q\" => \"'one;two'\", \"x\" => \"1\"}" do + expect(@uri.query_values).to eq({"q" => "'one;two'", "x" => "1"}) + end + + it "should escape the ';' character when normalizing to avoid ambiguity " + + "with the W3C HTML 4.01 specification" do + # HTML 4.01 Section B.2.2 + expect(@uri.normalize.query).to eq("q='one%3Btwo'&x=1") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?&&x=b'" do + before do + @uri = Addressable::URI.parse("http://example.com/?&&x=b") + end + + it "should have a query of '&&x=b'" do + expect(@uri.query).to eq("&&x=b") + end + + it "should have query_values of {'x' => 'b'}" do + expect(@uri.query_values).to eq({'x' => 'b'}) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?q=a&&x=b'" do + before do + @uri = Addressable::URI.parse("http://example.com/?q=a&&x=b") + end + + it "should have a query of 'q=a&&x=b'" do + expect(@uri.query).to eq("q=a&&x=b") + end + + it "should have query_values of {'q' => 'a, 'x' => 'b'}" do + expect(@uri.query_values).to eq({'q' => 'a', 'x' => 'b'}) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?q&&x=b'" do + before do + @uri = Addressable::URI.parse("http://example.com/?q&&x=b") + end + + it "should have a query of 'q&&x=b'" do + expect(@uri.query).to eq("q&&x=b") + end + + it "should have query_values of {'q' => true, 'x' => 'b'}" do + expect(@uri.query_values).to eq({'q' => nil, 'x' => 'b'}) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?q=a+b'" do + before do + @uri = Addressable::URI.parse("http://example.com/?q=a+b") + end + + it "should have a query of 'q=a+b'" do + expect(@uri.query).to eq("q=a+b") + end + + it "should have query_values of {'q' => 'a b'}" do + expect(@uri.query_values).to eq({'q' => 'a b'}) + end + + it "should have a normalized query of 'q=a+b'" do + expect(@uri.normalized_query).to eq("q=a+b") + end +end + +describe Addressable::URI, "when parsed from 'https://example.com/?q=a+b'" do + before do + @uri = Addressable::URI.parse("https://example.com/?q=a+b") + end + + it "should have query_values of {'q' => 'a b'}" do + expect(@uri.query_values).to eq("q" => "a b") + end +end + +describe Addressable::URI, "when parsed from 'example.com?q=a+b'" do + before do + @uri = Addressable::URI.parse("example.com?q=a+b") + end + + it "should have query_values of {'q' => 'a b'}" do + expect(@uri.query_values).to eq("q" => "a b") + end +end + +describe Addressable::URI, "when parsed from 'mailto:?q=a+b'" do + before do + @uri = Addressable::URI.parse("mailto:?q=a+b") + end + + it "should have query_values of {'q' => 'a+b'}" do + expect(@uri.query_values).to eq("q" => "a+b") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?q=a%2bb'" do + before do + @uri = Addressable::URI.parse("http://example.com/?q=a%2bb") + end + + it "should have a query of 'q=a+b'" do + expect(@uri.query).to eq("q=a%2bb") + end + + it "should have query_values of {'q' => 'a+b'}" do + expect(@uri.query_values).to eq({'q' => 'a+b'}) + end + + it "should have a normalized query of 'q=a%2Bb'" do + expect(@uri.normalized_query).to eq("q=a%2Bb") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?v=%7E&w=%&x=%25&y=%2B&z=C%CC%A7'" do + before do + @uri = Addressable::URI.parse("http://example.com/?v=%7E&w=%&x=%25&y=%2B&z=C%CC%A7") + end + + it "should have a normalized query of 'v=~&w=%25&x=%25&y=%2B&z=%C3%87'" do + expect(@uri.normalized_query).to eq("v=~&w=%25&x=%25&y=%2B&z=%C3%87") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?v=%7E&w=%&x=%25&y=+&z=C%CC%A7'" do + before do + @uri = Addressable::URI.parse("http://example.com/?v=%7E&w=%&x=%25&y=+&z=C%CC%A7") + end + + it "should have a normalized query of 'v=~&w=%25&x=%25&y=+&z=%C3%87'" do + expect(@uri.normalized_query).to eq("v=~&w=%25&x=%25&y=+&z=%C3%87") + end +end + +describe Addressable::URI, "when parsed from 'http://example/?b=1&a=2&c=3'" do + before do + @uri = Addressable::URI.parse("http://example/?b=1&a=2&c=3") + end + + it "should have a sorted normalized query of 'a=2&b=1&c=3'" do + expect(@uri.normalized_query(:sorted)).to eq("a=2&b=1&c=3") + end +end + +describe Addressable::URI, "when parsed from 'http://example/?&a&&c&'" do + before do + @uri = Addressable::URI.parse("http://example/?&a&&c&") + end + + it "should have a compacted normalized query of 'a&c'" do + expect(@uri.normalized_query(:compacted)).to eq("a&c") + end +end + +describe Addressable::URI, "when parsed from 'http://example.com/?a=1&a=1'" do + before do + @uri = Addressable::URI.parse("http://example.com/?a=1&a=1") + end + + it "should have a compacted normalized query of 'a=1'" do + expect(@uri.normalized_query(:compacted)).to eq("a=1") + end +end + +describe Addressable::URI, "when parsed from 'http://example.com/?a=1&a=2'" do + before do + @uri = Addressable::URI.parse("http://example.com/?a=1&a=2") + end + + it "should have a compacted normalized query of 'a=1&a=2'" do + expect(@uri.normalized_query(:compacted)).to eq("a=1&a=2") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/sound%2bvision'" do + before do + @uri = Addressable::URI.parse("http://example.com/sound%2bvision") + end + + it "should have a normalized path of '/sound+vision'" do + expect(@uri.normalized_path).to eq('/sound+vision') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?q='" do + before do + @uri = Addressable::URI.parse("http://example.com/?q=") + end + + it "should have a query of 'q='" do + expect(@uri.query).to eq("q=") + end + + it "should have query_values of {'q' => ''}" do + expect(@uri.query_values).to eq({'q' => ''}) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://user@example.com'" do + before do + @uri = Addressable::URI.parse("http://user@example.com") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have a username of 'user'" do + expect(@uri.user).to eq("user") + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should have a userinfo of 'user'" do + expect(@uri.userinfo).to eq("user") + end + + it "should have a normalized userinfo of 'user'" do + expect(@uri.normalized_userinfo).to eq("user") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should have default_port 80" do + expect(@uri.default_port).to eq(80) + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have the correct username after assignment" do + @uri.user = "newuser" + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq(nil) + expect(@uri.to_s).to eq("http://newuser@example.com") + end + + it "should have the correct password after assignment" do + @uri.password = "newpass" + expect(@uri.password).to eq("newpass") + expect(@uri.to_s).to eq("http://user:newpass@example.com") + end + + it "should have the correct userinfo segment after assignment" do + @uri.userinfo = "newuser:newpass" + expect(@uri.userinfo).to eq("newuser:newpass") + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq("newpass") + expect(@uri.host).to eq("example.com") + expect(@uri.port).to eq(nil) + expect(@uri.inferred_port).to eq(80) + expect(@uri.to_s).to eq("http://newuser:newpass@example.com") + end + + it "should have the correct userinfo segment after nil assignment" do + @uri.userinfo = nil + expect(@uri.userinfo).to eq(nil) + expect(@uri.user).to eq(nil) + expect(@uri.password).to eq(nil) + expect(@uri.host).to eq("example.com") + expect(@uri.port).to eq(nil) + expect(@uri.inferred_port).to eq(80) + expect(@uri.to_s).to eq("http://example.com") + end + + it "should have the correct authority segment after assignment" do + @uri.authority = "newuser@example.com" + expect(@uri.authority).to eq("newuser@example.com") + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq(nil) + expect(@uri.host).to eq("example.com") + expect(@uri.port).to eq(nil) + expect(@uri.inferred_port).to eq(80) + expect(@uri.to_s).to eq("http://newuser@example.com") + end + + it "should raise an error after nil assignment of authority segment" do + expect do + # This would create an invalid URI + @uri.authority = nil + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://user:@example.com'" do + before do + @uri = Addressable::URI.parse("http://user:@example.com") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have a username of 'user'" do + expect(@uri.user).to eq("user") + end + + it "should have a password of ''" do + expect(@uri.password).to eq("") + end + + it "should have a normalized userinfo of 'user:'" do + expect(@uri.normalized_userinfo).to eq("user:") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have the correct username after assignment" do + @uri.user = "newuser" + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq("") + expect(@uri.to_s).to eq("http://newuser:@example.com") + end + + it "should have the correct password after assignment" do + @uri.password = "newpass" + expect(@uri.password).to eq("newpass") + expect(@uri.to_s).to eq("http://user:newpass@example.com") + end + + it "should have the correct authority segment after assignment" do + @uri.authority = "newuser:@example.com" + expect(@uri.authority).to eq("newuser:@example.com") + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq("") + expect(@uri.host).to eq("example.com") + expect(@uri.port).to eq(nil) + expect(@uri.inferred_port).to eq(80) + expect(@uri.to_s).to eq("http://newuser:@example.com") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://:pass@example.com'" do + before do + @uri = Addressable::URI.parse("http://:pass@example.com") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have a username of ''" do + expect(@uri.user).to eq("") + end + + it "should have a password of 'pass'" do + expect(@uri.password).to eq("pass") + end + + it "should have a userinfo of ':pass'" do + expect(@uri.userinfo).to eq(":pass") + end + + it "should have a normalized userinfo of ':pass'" do + expect(@uri.normalized_userinfo).to eq(":pass") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have the correct username after assignment" do + @uri.user = "newuser" + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq("pass") + expect(@uri.to_s).to eq("http://newuser:pass@example.com") + end + + it "should have the correct password after assignment" do + @uri.password = "newpass" + expect(@uri.password).to eq("newpass") + expect(@uri.user).to eq("") + expect(@uri.to_s).to eq("http://:newpass@example.com") + end + + it "should have the correct authority segment after assignment" do + @uri.authority = ":newpass@example.com" + expect(@uri.authority).to eq(":newpass@example.com") + expect(@uri.user).to eq("") + expect(@uri.password).to eq("newpass") + expect(@uri.host).to eq("example.com") + expect(@uri.port).to eq(nil) + expect(@uri.inferred_port).to eq(80) + expect(@uri.to_s).to eq("http://:newpass@example.com") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://:@example.com'" do + before do + @uri = Addressable::URI.parse("http://:@example.com") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have a username of ''" do + expect(@uri.user).to eq("") + end + + it "should have a password of ''" do + expect(@uri.password).to eq("") + end + + it "should have a normalized userinfo of nil" do + expect(@uri.normalized_userinfo).to eq(nil) + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have the correct username after assignment" do + @uri.user = "newuser" + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq("") + expect(@uri.to_s).to eq("http://newuser:@example.com") + end + + it "should have the correct password after assignment" do + @uri.password = "newpass" + expect(@uri.password).to eq("newpass") + expect(@uri.user).to eq("") + expect(@uri.to_s).to eq("http://:newpass@example.com") + end + + it "should have the correct authority segment after assignment" do + @uri.authority = ":@newexample.com" + expect(@uri.authority).to eq(":@newexample.com") + expect(@uri.user).to eq("") + expect(@uri.password).to eq("") + expect(@uri.host).to eq("newexample.com") + expect(@uri.port).to eq(nil) + expect(@uri.inferred_port).to eq(80) + expect(@uri.to_s).to eq("http://:@newexample.com") + end +end + +describe Addressable::URI, "when parsed from " + + "'#example'" do + before do + @uri = Addressable::URI.parse("#example") + end + + it "should be considered relative" do + expect(@uri).to be_relative + end + + it "should have a host of nil" do + expect(@uri.host).to eq(nil) + end + + it "should have a site of nil" do + expect(@uri.site).to eq(nil) + end + + it "should have a normalized_site of nil" do + expect(@uri.normalized_site).to eq(nil) + end + + it "should have a path of ''" do + expect(@uri.path).to eq("") + end + + it "should have a query string of nil" do + expect(@uri.query).to eq(nil) + end + + it "should have a fragment of 'example'" do + expect(@uri.fragment).to eq("example") + end +end + +describe Addressable::URI, "when parsed from " + + "the network-path reference '//example.com/'" do + before do + @uri = Addressable::URI.parse("//example.com/") + end + + it "should be considered relative" do + expect(@uri).to be_relative + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should have a path of '/'" do + expect(@uri.path).to eq("/") + end + + it "should raise an error if routing is attempted" do + expect do + @uri.route_to("http://example.com/") + end.to raise_error(ArgumentError, /\/\/example.com\//) + expect do + @uri.route_from("http://example.com/") + end.to raise_error(ArgumentError, /\/\/example.com\//) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when parsed from " + + "'feed://http://example.com/'" do + before do + @uri = Addressable::URI.parse("feed://http://example.com/") + end + + it "should have a host of 'http'" do + expect(@uri.host).to eq("http") + end + + it "should have a path of '//example.com/'" do + expect(@uri.path).to eq("//example.com/") + end +end + +describe Addressable::URI, "when parsed from " + + "'feed:http://example.com/'" do + before do + @uri = Addressable::URI.parse("feed:http://example.com/") + end + + it "should have a path of 'http://example.com/'" do + expect(@uri.path).to eq("http://example.com/") + end + + it "should normalize to 'http://example.com/'" do + expect(@uri.normalize.to_s).to eq("http://example.com/") + expect(@uri.normalize!.to_s).to eq("http://example.com/") + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when parsed from " + + "'example://a/b/c/%7Bfoo%7D'" do + before do + @uri = Addressable::URI.parse("example://a/b/c/%7Bfoo%7D") + end + + # Section 6.2.2 of RFC 3986 + it "should be equivalent to eXAMPLE://a/./b/../b/%63/%7bfoo%7d" do + expect(@uri).to eq( + Addressable::URI.parse("eXAMPLE://a/./b/../b/%63/%7bfoo%7d") + ) + end + + it "should have an origin of 'example://a'" do + expect(@uri.origin).to eq('example://a') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/indirect/path/./to/../resource/'" do + before do + @uri = Addressable::URI.parse( + "http://example.com/indirect/path/./to/../resource/") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have a path of '/indirect/path/./to/../resource/'" do + expect(@uri.path).to eq("/indirect/path/./to/../resource/") + end + + # Section 6.2.2.3 of RFC 3986 + it "should have a normalized path of '/indirect/path/resource/'" do + expect(@uri.normalize.path).to eq("/indirect/path/resource/") + expect(@uri.normalize!.path).to eq("/indirect/path/resource/") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://under_score.example.com/'" do + it "should not cause an error" do + expect do + Addressable::URI.parse("http://under_score.example.com/") + end.not_to raise_error + end +end + +describe Addressable::URI, "when parsed from " + + "'./this:that'" do + before do + @uri = Addressable::URI.parse("./this:that") + end + + it "should be considered relative" do + expect(@uri).to be_relative + end + + it "should have no scheme" do + expect(@uri.scheme).to eq(nil) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when parsed from " + + "'this:that'" do + before do + @uri = Addressable::URI.parse("this:that") + end + + it "should be considered absolute" do + expect(@uri).to be_absolute + end + + it "should have a scheme of 'this'" do + expect(@uri.scheme).to eq("this") + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when parsed from '?'" do + before do + @uri = Addressable::URI.parse("?") + end + + it "should normalize to ''" do + expect(@uri.normalize.to_s).to eq("") + end + + it "should have the correct return type" do + expect(@uri.query_values).to eq({}) + expect(@uri.query_values(Hash)).to eq({}) + expect(@uri.query_values(Array)).to eq([]) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when parsed from '?one=1&two=2&three=3'" do + before do + @uri = Addressable::URI.parse("?one=1&two=2&three=3") + end + + it "should have the correct query values" do + expect(@uri.query_values).to eq({"one" => "1", "two" => "2", "three" => "3"}) + end + + it "should raise an error for invalid return type values" do + expect do + @uri.query_values(Integer) + end.to raise_error(ArgumentError) + end + + it "should have the correct array query values" do + expect(@uri.query_values(Array)).to eq([ + ["one", "1"], ["two", "2"], ["three", "3"] + ]) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when parsed from '?one=1=uno&two=2=dos'" do + before do + @uri = Addressable::URI.parse("?one=1=uno&two=2=dos") + end + + it "should have the correct query values" do + expect(@uri.query_values).to eq({"one" => "1=uno", "two" => "2=dos"}) + end + + it "should have the correct array query values" do + expect(@uri.query_values(Array)).to eq([ + ["one", "1=uno"], ["two", "2=dos"] + ]) + end +end + +describe Addressable::URI, "when parsed from '?one[two][three]=four'" do + before do + @uri = Addressable::URI.parse("?one[two][three]=four") + end + + it "should have the correct query values" do + expect(@uri.query_values).to eq({"one[two][three]" => "four"}) + end + + it "should have the correct array query values" do + expect(@uri.query_values(Array)).to eq([ + ["one[two][three]", "four"] + ]) + end +end + +describe Addressable::URI, "when parsed from '?one.two.three=four'" do + before do + @uri = Addressable::URI.parse("?one.two.three=four") + end + + it "should have the correct query values" do + expect(@uri.query_values).to eq({ + "one.two.three" => "four" + }) + end + + it "should have the correct array query values" do + expect(@uri.query_values(Array)).to eq([ + ["one.two.three", "four"] + ]) + end +end + +describe Addressable::URI, "when parsed from " + + "'?one[two][three]=four&one[two][five]=six'" do + before do + @uri = Addressable::URI.parse("?one[two][three]=four&one[two][five]=six") + end + + it "should have the correct query values" do + expect(@uri.query_values).to eq({ + "one[two][three]" => "four", "one[two][five]" => "six" + }) + end + + it "should have the correct array query values" do + expect(@uri.query_values(Array)).to eq([ + ["one[two][three]", "four"], ["one[two][five]", "six"] + ]) + end +end + +describe Addressable::URI, "when parsed from " + + "'?one.two.three=four&one.two.five=six'" do + before do + @uri = Addressable::URI.parse("?one.two.three=four&one.two.five=six") + end + + it "should have the correct query values" do + expect(@uri.query_values).to eq({ + "one.two.three" => "four", "one.two.five" => "six" + }) + end + + it "should have the correct array query values" do + expect(@uri.query_values(Array)).to eq([ + ["one.two.three", "four"], ["one.two.five", "six"] + ]) + end +end + +describe Addressable::URI, "when parsed from " + + "'?one=two&one=three'" do + before do + @uri = Addressable::URI.parse( + "?one=two&one=three&one=four" + ) + end + + it "should have correct array query values" do + expect(@uri.query_values(Array)).to eq( + [['one', 'two'], ['one', 'three'], ['one', 'four']] + ) + end + + it "should have correct hash query values" do + skip("This is probably more desirable behavior.") + expect(@uri.query_values(Hash)).to eq( + {'one' => ['two', 'three', 'four']} + ) + end + + it "should handle assignment with keys of mixed type" do + @uri.query_values = @uri.query_values(Hash).merge({:one => 'three'}) + expect(@uri.query_values(Hash)).to eq({'one' => 'three'}) + end +end + +describe Addressable::URI, "when parsed from " + + "'?one[two][three][]=four&one[two][three][]=five'" do + before do + @uri = Addressable::URI.parse( + "?one[two][three][]=four&one[two][three][]=five" + ) + end + + it "should have correct query values" do + expect(@uri.query_values(Hash)).to eq({"one[two][three][]" => "five"}) + end + + it "should have correct array query values" do + expect(@uri.query_values(Array)).to eq([ + ["one[two][three][]", "four"], ["one[two][three][]", "five"] + ]) + end +end + +describe Addressable::URI, "when parsed from " + + "'?one[two][three][0]=four&one[two][three][1]=five'" do + before do + @uri = Addressable::URI.parse( + "?one[two][three][0]=four&one[two][three][1]=five" + ) + end + + it "should have the correct query values" do + expect(@uri.query_values).to eq({ + "one[two][three][0]" => "four", "one[two][three][1]" => "five" + }) + end +end + +describe Addressable::URI, "when parsed from " + + "'?one[two][three][1]=four&one[two][three][0]=five'" do + before do + @uri = Addressable::URI.parse( + "?one[two][three][1]=four&one[two][three][0]=five" + ) + end + + it "should have the correct query values" do + expect(@uri.query_values).to eq({ + "one[two][three][1]" => "four", "one[two][three][0]" => "five" + }) + end +end + +describe Addressable::URI, "when parsed from " + + "'?one[two][three][2]=four&one[two][three][1]=five'" do + before do + @uri = Addressable::URI.parse( + "?one[two][three][2]=four&one[two][three][1]=five" + ) + end + + it "should have the correct query values" do + expect(@uri.query_values).to eq({ + "one[two][three][2]" => "four", "one[two][three][1]" => "five" + }) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://www.詹姆斯.com/'" do + before do + @uri = Addressable::URI.parse("http://www.詹姆斯.com/") + end + + it "should be equivalent to 'http://www.xn--8ws00zhy3a.com/'" do + expect(@uri).to eq( + Addressable::URI.parse("http://www.xn--8ws00zhy3a.com/") + ) + end + + it "should not have domain name encoded during normalization" do + expect(Addressable::URI.normalized_encode(@uri.to_s)).to eq( + "http://www.詹姆斯.com/" + ) + end + + it "should have an origin of 'http://www.xn--8ws00zhy3a.com'" do + expect(@uri.origin).to eq('http://www.xn--8ws00zhy3a.com') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://www.詹姆斯.com/ some spaces /'" do + before do + @uri = Addressable::URI.parse("http://www.詹姆斯.com/ some spaces /") + end + + it "should be equivalent to " + + "'http://www.xn--8ws00zhy3a.com/%20some%20spaces%20/'" do + expect(@uri).to eq( + Addressable::URI.parse( + "http://www.xn--8ws00zhy3a.com/%20some%20spaces%20/") + ) + end + + it "should not have domain name encoded during normalization" do + expect(Addressable::URI.normalized_encode(@uri.to_s)).to eq( + "http://www.詹姆斯.com/%20some%20spaces%20/" + ) + end + + it "should have an origin of 'http://www.xn--8ws00zhy3a.com'" do + expect(@uri.origin).to eq('http://www.xn--8ws00zhy3a.com') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://www.xn--8ws00zhy3a.com/'" do + before do + @uri = Addressable::URI.parse("http://www.xn--8ws00zhy3a.com/") + end + + it "should be displayed as http://www.詹姆斯.com/" do + expect(@uri.display_uri.to_s).to eq("http://www.詹姆斯.com/") + end + + it "should properly force the encoding" do + display_string = @uri.display_uri.to_str + expect(display_string).to eq("http://www.詹姆斯.com/") + if display_string.respond_to?(:encoding) + expect(display_string.encoding.to_s).to eq(Encoding::UTF_8.to_s) + end + end + + it "should have an origin of 'http://www.xn--8ws00zhy3a.com'" do + expect(@uri.origin).to eq('http://www.xn--8ws00zhy3a.com') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://www.詹姆斯.com/atomtests/iri/詹.html'" do + before do + @uri = Addressable::URI.parse("http://www.詹姆斯.com/atomtests/iri/詹.html") + end + + it "should normalize to " + + "http://www.xn--8ws00zhy3a.com/atomtests/iri/%E8%A9%B9.html" do + expect(@uri.normalize.to_s).to eq( + "http://www.xn--8ws00zhy3a.com/atomtests/iri/%E8%A9%B9.html" + ) + expect(@uri.normalize!.to_s).to eq( + "http://www.xn--8ws00zhy3a.com/atomtests/iri/%E8%A9%B9.html" + ) + end +end + +describe Addressable::URI, "when parsed from a percent-encoded IRI" do + before do + @uri = Addressable::URI.parse( + "http://www.%E3%81%BB%E3%82%93%E3%81%A8%E3%81%86%E3%81%AB%E3%81%AA" + + "%E3%81%8C%E3%81%84%E3%82%8F%E3%81%91%E3%81%AE%E3%82%8F%E3%81%8B%E3" + + "%82%89%E3%81%AA%E3%81%84%E3%81%A9%E3%82%81%E3%81%84%E3%82%93%E3%82" + + "%81%E3%81%84%E3%81%AE%E3%82%89%E3%81%B9%E3%82%8B%E3%81%BE%E3%81%A0" + + "%E3%81%AA%E3%81%8C%E3%81%8F%E3%81%97%E3%81%AA%E3%81%84%E3%81%A8%E3" + + "%81%9F%E3%82%8A%E3%81%AA%E3%81%84.w3.mag.keio.ac.jp" + ) + end + + it "should normalize to something sane" do + expect(@uri.normalize.to_s).to eq( + "http://www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3f" + + "g11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp/" + ) + expect(@uri.normalize!.to_s).to eq( + "http://www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3f" + + "g11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp/" + ) + end + + it "should have the correct origin" do + expect(@uri.origin).to eq( + "http://www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3f" + + "g11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp" + ) + end +end + +describe Addressable::URI, "with a base uri of 'http://a/b/c/d;p?q'" do + before do + @uri = Addressable::URI.parse("http://a/b/c/d;p?q") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with 'g:h' should resolve to g:h" do + expect((@uri + "g:h").to_s).to eq("g:h") + expect(Addressable::URI.join(@uri, "g:h").to_s).to eq("g:h") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with 'g' should resolve to http://a/b/c/g" do + expect((@uri + "g").to_s).to eq("http://a/b/c/g") + expect(Addressable::URI.join(@uri.to_s, "g").to_s).to eq("http://a/b/c/g") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with './g' should resolve to http://a/b/c/g" do + expect((@uri + "./g").to_s).to eq("http://a/b/c/g") + expect(Addressable::URI.join(@uri.to_s, "./g").to_s).to eq("http://a/b/c/g") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with 'g/' should resolve to http://a/b/c/g/" do + expect((@uri + "g/").to_s).to eq("http://a/b/c/g/") + expect(Addressable::URI.join(@uri.to_s, "g/").to_s).to eq("http://a/b/c/g/") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '/g' should resolve to http://a/g" do + expect((@uri + "/g").to_s).to eq("http://a/g") + expect(Addressable::URI.join(@uri.to_s, "/g").to_s).to eq("http://a/g") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '//g' should resolve to http://g" do + expect((@uri + "//g").to_s).to eq("http://g") + expect(Addressable::URI.join(@uri.to_s, "//g").to_s).to eq("http://g") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '?y' should resolve to http://a/b/c/d;p?y" do + expect((@uri + "?y").to_s).to eq("http://a/b/c/d;p?y") + expect(Addressable::URI.join(@uri.to_s, "?y").to_s).to eq("http://a/b/c/d;p?y") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with 'g?y' should resolve to http://a/b/c/g?y" do + expect((@uri + "g?y").to_s).to eq("http://a/b/c/g?y") + expect(Addressable::URI.join(@uri.to_s, "g?y").to_s).to eq("http://a/b/c/g?y") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '#s' should resolve to http://a/b/c/d;p?q#s" do + expect((@uri + "#s").to_s).to eq("http://a/b/c/d;p?q#s") + expect(Addressable::URI.join(@uri.to_s, "#s").to_s).to eq( + "http://a/b/c/d;p?q#s" + ) + end + + # Section 5.4.1 of RFC 3986 + it "when joined with 'g#s' should resolve to http://a/b/c/g#s" do + expect((@uri + "g#s").to_s).to eq("http://a/b/c/g#s") + expect(Addressable::URI.join(@uri.to_s, "g#s").to_s).to eq("http://a/b/c/g#s") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with 'g?y#s' should resolve to http://a/b/c/g?y#s" do + expect((@uri + "g?y#s").to_s).to eq("http://a/b/c/g?y#s") + expect(Addressable::URI.join( + @uri.to_s, "g?y#s").to_s).to eq("http://a/b/c/g?y#s") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with ';x' should resolve to http://a/b/c/;x" do + expect((@uri + ";x").to_s).to eq("http://a/b/c/;x") + expect(Addressable::URI.join(@uri.to_s, ";x").to_s).to eq("http://a/b/c/;x") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with 'g;x' should resolve to http://a/b/c/g;x" do + expect((@uri + "g;x").to_s).to eq("http://a/b/c/g;x") + expect(Addressable::URI.join(@uri.to_s, "g;x").to_s).to eq("http://a/b/c/g;x") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with 'g;x?y#s' should resolve to http://a/b/c/g;x?y#s" do + expect((@uri + "g;x?y#s").to_s).to eq("http://a/b/c/g;x?y#s") + expect(Addressable::URI.join( + @uri.to_s, "g;x?y#s").to_s).to eq("http://a/b/c/g;x?y#s") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '' should resolve to http://a/b/c/d;p?q" do + expect((@uri + "").to_s).to eq("http://a/b/c/d;p?q") + expect(Addressable::URI.join(@uri.to_s, "").to_s).to eq("http://a/b/c/d;p?q") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '.' should resolve to http://a/b/c/" do + expect((@uri + ".").to_s).to eq("http://a/b/c/") + expect(Addressable::URI.join(@uri.to_s, ".").to_s).to eq("http://a/b/c/") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with './' should resolve to http://a/b/c/" do + expect((@uri + "./").to_s).to eq("http://a/b/c/") + expect(Addressable::URI.join(@uri.to_s, "./").to_s).to eq("http://a/b/c/") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '..' should resolve to http://a/b/" do + expect((@uri + "..").to_s).to eq("http://a/b/") + expect(Addressable::URI.join(@uri.to_s, "..").to_s).to eq("http://a/b/") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '../' should resolve to http://a/b/" do + expect((@uri + "../").to_s).to eq("http://a/b/") + expect(Addressable::URI.join(@uri.to_s, "../").to_s).to eq("http://a/b/") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '../g' should resolve to http://a/b/g" do + expect((@uri + "../g").to_s).to eq("http://a/b/g") + expect(Addressable::URI.join(@uri.to_s, "../g").to_s).to eq("http://a/b/g") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '../..' should resolve to http://a/" do + expect((@uri + "../..").to_s).to eq("http://a/") + expect(Addressable::URI.join(@uri.to_s, "../..").to_s).to eq("http://a/") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '../../' should resolve to http://a/" do + expect((@uri + "../../").to_s).to eq("http://a/") + expect(Addressable::URI.join(@uri.to_s, "../../").to_s).to eq("http://a/") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '../../g' should resolve to http://a/g" do + expect((@uri + "../../g").to_s).to eq("http://a/g") + expect(Addressable::URI.join(@uri.to_s, "../../g").to_s).to eq("http://a/g") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with '../../../g' should resolve to http://a/g" do + expect((@uri + "../../../g").to_s).to eq("http://a/g") + expect(Addressable::URI.join(@uri.to_s, "../../../g").to_s).to eq("http://a/g") + end + + it "when joined with '../.././../g' should resolve to http://a/g" do + expect((@uri + "../.././../g").to_s).to eq("http://a/g") + expect(Addressable::URI.join(@uri.to_s, "../.././../g").to_s).to eq( + "http://a/g" + ) + end + + # Section 5.4.2 of RFC 3986 + it "when joined with '../../../../g' should resolve to http://a/g" do + expect((@uri + "../../../../g").to_s).to eq("http://a/g") + expect(Addressable::URI.join( + @uri.to_s, "../../../../g").to_s).to eq("http://a/g") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with '/./g' should resolve to http://a/g" do + expect((@uri + "/./g").to_s).to eq("http://a/g") + expect(Addressable::URI.join(@uri.to_s, "/./g").to_s).to eq("http://a/g") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with '/../g' should resolve to http://a/g" do + expect((@uri + "/../g").to_s).to eq("http://a/g") + expect(Addressable::URI.join(@uri.to_s, "/../g").to_s).to eq("http://a/g") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'g.' should resolve to http://a/b/c/g." do + expect((@uri + "g.").to_s).to eq("http://a/b/c/g.") + expect(Addressable::URI.join(@uri.to_s, "g.").to_s).to eq("http://a/b/c/g.") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with '.g' should resolve to http://a/b/c/.g" do + expect((@uri + ".g").to_s).to eq("http://a/b/c/.g") + expect(Addressable::URI.join(@uri.to_s, ".g").to_s).to eq("http://a/b/c/.g") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'g..' should resolve to http://a/b/c/g.." do + expect((@uri + "g..").to_s).to eq("http://a/b/c/g..") + expect(Addressable::URI.join(@uri.to_s, "g..").to_s).to eq("http://a/b/c/g..") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with '..g' should resolve to http://a/b/c/..g" do + expect((@uri + "..g").to_s).to eq("http://a/b/c/..g") + expect(Addressable::URI.join(@uri.to_s, "..g").to_s).to eq("http://a/b/c/..g") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with './../g' should resolve to http://a/b/g" do + expect((@uri + "./../g").to_s).to eq("http://a/b/g") + expect(Addressable::URI.join(@uri.to_s, "./../g").to_s).to eq("http://a/b/g") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with './g/.' should resolve to http://a/b/c/g/" do + expect((@uri + "./g/.").to_s).to eq("http://a/b/c/g/") + expect(Addressable::URI.join(@uri.to_s, "./g/.").to_s).to eq("http://a/b/c/g/") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'g/./h' should resolve to http://a/b/c/g/h" do + expect((@uri + "g/./h").to_s).to eq("http://a/b/c/g/h") + expect(Addressable::URI.join(@uri.to_s, "g/./h").to_s).to eq("http://a/b/c/g/h") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'g/../h' should resolve to http://a/b/c/h" do + expect((@uri + "g/../h").to_s).to eq("http://a/b/c/h") + expect(Addressable::URI.join(@uri.to_s, "g/../h").to_s).to eq("http://a/b/c/h") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'g;x=1/./y' " + + "should resolve to http://a/b/c/g;x=1/y" do + expect((@uri + "g;x=1/./y").to_s).to eq("http://a/b/c/g;x=1/y") + expect(Addressable::URI.join( + @uri.to_s, "g;x=1/./y").to_s).to eq("http://a/b/c/g;x=1/y") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'g;x=1/../y' should resolve to http://a/b/c/y" do + expect((@uri + "g;x=1/../y").to_s).to eq("http://a/b/c/y") + expect(Addressable::URI.join( + @uri.to_s, "g;x=1/../y").to_s).to eq("http://a/b/c/y") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'g?y/./x' " + + "should resolve to http://a/b/c/g?y/./x" do + expect((@uri + "g?y/./x").to_s).to eq("http://a/b/c/g?y/./x") + expect(Addressable::URI.join( + @uri.to_s, "g?y/./x").to_s).to eq("http://a/b/c/g?y/./x") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'g?y/../x' " + + "should resolve to http://a/b/c/g?y/../x" do + expect((@uri + "g?y/../x").to_s).to eq("http://a/b/c/g?y/../x") + expect(Addressable::URI.join( + @uri.to_s, "g?y/../x").to_s).to eq("http://a/b/c/g?y/../x") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'g#s/./x' " + + "should resolve to http://a/b/c/g#s/./x" do + expect((@uri + "g#s/./x").to_s).to eq("http://a/b/c/g#s/./x") + expect(Addressable::URI.join( + @uri.to_s, "g#s/./x").to_s).to eq("http://a/b/c/g#s/./x") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'g#s/../x' " + + "should resolve to http://a/b/c/g#s/../x" do + expect((@uri + "g#s/../x").to_s).to eq("http://a/b/c/g#s/../x") + expect(Addressable::URI.join( + @uri.to_s, "g#s/../x").to_s).to eq("http://a/b/c/g#s/../x") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'http:g' should resolve to http:g" do + expect((@uri + "http:g").to_s).to eq("http:g") + expect(Addressable::URI.join(@uri.to_s, "http:g").to_s).to eq("http:g") + end + + # Edge case to be sure + it "when joined with '//example.com/' should " + + "resolve to http://example.com/" do + expect((@uri + "//example.com/").to_s).to eq("http://example.com/") + expect(Addressable::URI.join( + @uri.to_s, "//example.com/").to_s).to eq("http://example.com/") + end + + it "when joined with a bogus object a TypeError should be raised" do + expect do + Addressable::URI.join(@uri, 42) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when converting the path " + + "'relative/path/to/something'" do + before do + @path = 'relative/path/to/something' + end + + it "should convert to " + + "\'relative/path/to/something\'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.to_str).to eq("relative/path/to/something") + end + + it "should join with an absolute file path correctly" do + @base = Addressable::URI.convert_path("/absolute/path/") + @uri = Addressable::URI.convert_path(@path) + expect((@base + @uri).to_str).to eq( + "file:///absolute/path/relative/path/to/something" + ) + end +end + +describe Addressable::URI, "when converting a bogus path" do + it "should raise a TypeError" do + expect do + Addressable::URI.convert_path(42) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when given a UNIX root directory" do + before do + @path = "/" + end + + it "should convert to \'file:///\'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.to_str).to eq("file:///") + end + + it "should have an origin of 'file://'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.origin).to eq('file://') + end +end + +describe Addressable::URI, "when given a Windows root directory" do + before do + @path = "C:\\" + end + + it "should convert to \'file:///c:/\'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.to_str).to eq("file:///c:/") + end + + it "should have an origin of 'file://'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.origin).to eq('file://') + end +end + +describe Addressable::URI, "when given the path '/one/two/'" do + before do + @path = '/one/two/' + end + + it "should convert to " + + "\'file:///one/two/\'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.to_str).to eq("file:///one/two/") + end + + it "should have an origin of 'file://'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.origin).to eq('file://') + end +end + +describe Addressable::URI, "when given the tld " do + it "'uk' should have a tld of 'uk'" do + uri = Addressable::URI.parse("http://example.com") + uri.tld = "uk" + + expect(uri.tld).to eq("uk") + end + + context "which " do + let (:uri) { Addressable::URI.parse("http://www.comrade.net/path/to/source/") } + + it "contains a subdomain" do + uri.tld = "co.uk" + + expect(uri.to_s).to eq("http://www.comrade.co.uk/path/to/source/") + end + + it "is part of the domain" do + uri.tld = "com" + + expect(uri.to_s).to eq("http://www.comrade.com/path/to/source/") + end + end +end + +describe Addressable::URI, "when given the path " + + "'c:\\windows\\My Documents 100%20\\foo.txt'" do + before do + @path = "c:\\windows\\My Documents 100%20\\foo.txt" + end + + it "should convert to " + + "\'file:///c:/windows/My%20Documents%20100%20/foo.txt\'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.to_str).to eq("file:///c:/windows/My%20Documents%20100%20/foo.txt") + end + + it "should have an origin of 'file://'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.origin).to eq('file://') + end +end + +describe Addressable::URI, "when given the path " + + "'file://c:\\windows\\My Documents 100%20\\foo.txt'" do + before do + @path = "file://c:\\windows\\My Documents 100%20\\foo.txt" + end + + it "should convert to " + + "\'file:///c:/windows/My%20Documents%20100%20/foo.txt\'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.to_str).to eq("file:///c:/windows/My%20Documents%20100%20/foo.txt") + end + + it "should have an origin of 'file://'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.origin).to eq('file://') + end +end + +describe Addressable::URI, "when given the path " + + "'file:c:\\windows\\My Documents 100%20\\foo.txt'" do + before do + @path = "file:c:\\windows\\My Documents 100%20\\foo.txt" + end + + it "should convert to " + + "\'file:///c:/windows/My%20Documents%20100%20/foo.txt\'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.to_str).to eq("file:///c:/windows/My%20Documents%20100%20/foo.txt") + end + + it "should have an origin of 'file://'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.origin).to eq('file://') + end +end + +describe Addressable::URI, "when given the path " + + "'file:/c:\\windows\\My Documents 100%20\\foo.txt'" do + before do + @path = "file:/c:\\windows\\My Documents 100%20\\foo.txt" + end + + it "should convert to " + + "\'file:///c:/windows/My%20Documents%20100%20/foo.txt\'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.to_str).to eq("file:///c:/windows/My%20Documents%20100%20/foo.txt") + end + + it "should have an origin of 'file://'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.origin).to eq('file://') + end +end + +describe Addressable::URI, "when given the path " + + "'file:///c|/windows/My%20Documents%20100%20/foo.txt'" do + before do + @path = "file:///c|/windows/My%20Documents%20100%20/foo.txt" + end + + it "should convert to " + + "\'file:///c:/windows/My%20Documents%20100%20/foo.txt\'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.to_str).to eq("file:///c:/windows/My%20Documents%20100%20/foo.txt") + end + + it "should have an origin of 'file://'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.origin).to eq('file://') + end +end + +describe Addressable::URI, "when given an http protocol URI" do + before do + @path = "http://example.com/" + end + + it "should not do any conversion at all" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.to_str).to eq("http://example.com/") + end +end + +class SuperString + def initialize(string) + @string = string.to_s + end + + def to_str + return @string + end +end + +describe Addressable::URI, "when parsing a non-String object" do + it "should correctly parse anything with a 'to_str' method" do + Addressable::URI.parse(SuperString.new(42)) + end + + it "should raise a TypeError for objects than cannot be converted" do + expect do + Addressable::URI.parse(42) + end.to raise_error(TypeError) + end + + it "should correctly parse heuristically anything with a 'to_str' method" do + Addressable::URI.heuristic_parse(SuperString.new(42)) + end + + it "should raise a TypeError for objects than cannot be converted" do + expect do + Addressable::URI.heuristic_parse(42) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when form encoding a hash" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.form_encode( + [["&one", "/1"], ["=two", "?2"], [":three", "#3"]] + )).to eq("%26one=%2F1&%3Dtwo=%3F2&%3Athree=%233") + end + + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.form_encode( + {"q" => "one two three"} + )).to eq("q=one+two+three") + end + + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.form_encode( + {"key" => nil} + )).to eq("key=") + end + + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.form_encode( + {"q" => ["one", "two", "three"]} + )).to eq("q=one&q=two&q=three") + end + + it "should result in correctly encoded newlines" do + expect(Addressable::URI.form_encode( + {"text" => "one\ntwo\rthree\r\nfour\n\r"} + )).to eq("text=one%0D%0Atwo%0D%0Athree%0D%0Afour%0D%0A%0D%0A") + end + + it "should result in a sorted percent encoded sequence" do + expect(Addressable::URI.form_encode( + [["a", "1"], ["dup", "3"], ["dup", "2"]], true + )).to eq("a=1&dup=2&dup=3") + end +end + +describe Addressable::URI, "when form encoding a non-Array object" do + it "should raise a TypeError for objects than cannot be converted" do + expect do + Addressable::URI.form_encode(42) + end.to raise_error(TypeError) + end +end + +# See https://tools.ietf.org/html/rfc6749#appendix-B +describe Addressable::URI, "when form encoding the example value from OAuth 2" do + it "should result in correct values" do + expect(Addressable::URI.form_encode( + {"value" => " %&+£€"} + )).to eq("value=+%25%26%2B%C2%A3%E2%82%AC") + end +end + +# See https://tools.ietf.org/html/rfc6749#appendix-B +describe Addressable::URI, "when form unencoding the example value from OAuth 2" do + it "should result in correct values" do + expect(Addressable::URI.form_unencode( + "value=+%25%26%2B%C2%A3%E2%82%AC" + )).to eq([["value", " %&+£€"]]) + end +end + +describe Addressable::URI, "when form unencoding a string" do + it "should result in correct values" do + expect(Addressable::URI.form_unencode( + "%26one=%2F1&%3Dtwo=%3F2&%3Athree=%233" + )).to eq([["&one", "/1"], ["=two", "?2"], [":three", "#3"]]) + end + + it "should result in correct values" do + expect(Addressable::URI.form_unencode( + "q=one+two+three" + )).to eq([["q", "one two three"]]) + end + + it "should result in correct values" do + expect(Addressable::URI.form_unencode( + "text=one%0D%0Atwo%0D%0Athree%0D%0Afour%0D%0A%0D%0A" + )).to eq([["text", "one\ntwo\nthree\nfour\n\n"]]) + end + + it "should result in correct values" do + expect(Addressable::URI.form_unencode( + "a=1&dup=2&dup=3" + )).to eq([["a", "1"], ["dup", "2"], ["dup", "3"]]) + end + + it "should result in correct values" do + expect(Addressable::URI.form_unencode( + "key" + )).to eq([["key", nil]]) + end + + it "should result in correct values" do + expect(Addressable::URI.form_unencode("GivenName=Ren%C3%A9")).to eq( + [["GivenName", "René"]] + ) + end +end + +describe Addressable::URI, "when form unencoding a non-String object" do + it "should correctly parse anything with a 'to_str' method" do + Addressable::URI.form_unencode(SuperString.new(42)) + end + + it "should raise a TypeError for objects than cannot be converted" do + expect do + Addressable::URI.form_unencode(42) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when normalizing a non-String object" do + it "should correctly parse anything with a 'to_str' method" do + Addressable::URI.normalize_component(SuperString.new(42)) + end + + it "should raise a TypeError for objects than cannot be converted" do + expect do + Addressable::URI.normalize_component(42) + end.to raise_error(TypeError) + end + + it "should raise a TypeError for objects than cannot be converted" do + expect do + Addressable::URI.normalize_component("component", 42) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when normalizing a path with an encoded slash" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.parse("/path%2Fsegment/").normalize.path).to eq( + "/path%2Fsegment/" + ) + end +end + +describe Addressable::URI, "when normalizing a partially encoded string" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.normalize_component( + "partially % encoded%21" + )).to eq("partially%20%25%20encoded!") + end + + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.normalize_component( + "partially %25 encoded!" + )).to eq("partially%20%25%20encoded!") + end +end + +describe Addressable::URI, "when normalizing a unicode sequence" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.normalize_component( + "/C%CC%A7" + )).to eq("/%C3%87") + end + + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.normalize_component( + "/%C3%87" + )).to eq("/%C3%87") + end +end + +describe Addressable::URI, "when normalizing a multibyte string" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.normalize_component("günther")).to eq( + "g%C3%BCnther" + ) + end + + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.normalize_component("g%C3%BCnther")).to eq( + "g%C3%BCnther" + ) + end +end + +describe Addressable::URI, "when normalizing a string but leaving some characters encoded" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.normalize_component("%58X%59Y%5AZ", "0-9a-zXY", "Y")).to eq( + "XX%59Y%5A%5A" + ) + end + + it "should not modify the character class" do + character_class = "0-9a-zXY" + + character_class_copy = character_class.dup + + Addressable::URI.normalize_component("%58X%59Y%5AZ", character_class, "Y") + + expect(character_class).to eq(character_class_copy) + end +end + +describe Addressable::URI, "when encoding IP literals" do + it "should work for IPv4" do + input = "http://127.0.0.1/" + expect(Addressable::URI.encode(input)).to eq(input) + end + + it "should work for IPv6" do + input = "http://[fe80::200:f8ff:fe21:67cf]/" + expect(Addressable::URI.encode(input)).to eq(input) + end +end + +describe Addressable::URI, "when encoding a string with existing encodings to upcase" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.encode_component("JK%4c", "0-9A-IKM-Za-z%", "L")).to eq("%4AK%4C") + end +end + +describe Addressable::URI, "when encoding a multibyte string" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.encode_component("günther")).to eq("g%C3%BCnther") + end + + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.encode_component( + "günther", /[^a-zA-Z0-9\:\/\?\#\[\]\@\!\$\&\'\(\)\*\+\,\;\=\-\.\_\~]/ + )).to eq("g%C3%BCnther") + end +end + +describe Addressable::URI, "when form encoding a multibyte string" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.form_encode({"GivenName" => "René"})).to eq( + "GivenName=Ren%C3%A9" + ) + end +end + +describe Addressable::URI, "when encoding a string with ASCII chars 0-15" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.encode_component("one\ntwo")).to eq("one%0Atwo") + end + + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.encode_component( + "one\ntwo", /[^a-zA-Z0-9\:\/\?\#\[\]\@\!\$\&\'\(\)\*\+\,\;\=\-\.\_\~]/ + )).to eq("one%0Atwo") + end +end + +describe Addressable::URI, "when unencoding a multibyte string" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.unencode_component("g%C3%BCnther")).to eq("günther") + end + + it "should consistently use UTF-8 internally" do + expect(Addressable::URI.unencode_component("ski=%BA%DAɫ")).to eq("ski=\xBA\xDAɫ") + end + + it "should not fail with UTF-8 incompatible string" do + url = "/M%E9/\xE9?p=\xFC".b + expect(Addressable::URI.unencode_component(url)).to eq("/M\xE9/\xE9?p=\xFC") + end + + it "should result in correct percent encoded sequence as a URI" do + expect(Addressable::URI.unencode( + "/path?g%C3%BCnther", ::Addressable::URI + )).to eq(Addressable::URI.new( + :path => "/path", :query => "günther" + )) + end +end + +describe Addressable::URI, "when partially unencoding a string" do + it "should unencode all characters by default" do + expect(Addressable::URI.unencode('%%25~%7e+%2b', String)).to eq('%%~~++') + end + + it "should unencode characters not in leave_encoded" do + expect(Addressable::URI.unencode('%%25~%7e+%2b', String, '~')).to eq('%%~%7e++') + end + + it "should leave characters in leave_encoded alone" do + expect(Addressable::URI.unencode('%%25~%7e+%2b', String, '%~+')).to eq('%%25~%7e+%2b') + end +end + +describe Addressable::URI, "when unencoding a bogus object" do + it "should raise a TypeError" do + expect do + Addressable::URI.unencode_component(42) + end.to raise_error(TypeError) + end + + it "should raise a TypeError" do + expect do + Addressable::URI.unencode("/path?g%C3%BCnther", Integer) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when encoding a bogus object" do + it "should raise a TypeError" do + expect do + Addressable::URI.encode(Object.new) + end.to raise_error(TypeError) + end + + it "should raise a TypeError" do + expect do + Addressable::URI.normalized_encode(Object.new) + end.to raise_error(TypeError) + end + + it "should raise a TypeError" do + expect do + Addressable::URI.encode_component("günther", Object.new) + end.to raise_error(TypeError) + end + + it "should raise a TypeError" do + expect do + Addressable::URI.encode_component(Object.new) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when given the input " + + "'http://example.com/'" do + before do + @input = "http://example.com/" + end + + it "should heuristically parse to 'http://example.com/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("http://example.com/") + end + + it "should not raise error when frozen" do + expect do + Addressable::URI.heuristic_parse(@input).freeze.to_s + end.not_to raise_error + end +end + +describe Addressable::URI, "when given the input " + + "'https://example.com/'" do + before do + @input = "https://example.com/" + end + + it "should heuristically parse to 'https://example.com/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("https://example.com/") + end +end + +describe Addressable::URI, "when given the input " + + "'http:example.com/'" do + before do + @input = "http:example.com/" + end + + it "should heuristically parse to 'http://example.com/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("http://example.com/") + end + + it "should heuristically parse to 'http://example.com/' " + + "even with a scheme hint of 'ftp'" do + @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'}) + expect(@uri.to_s).to eq("http://example.com/") + end +end + +describe Addressable::URI, "when given the input " + + "'https:example.com/'" do + before do + @input = "https:example.com/" + end + + it "should heuristically parse to 'https://example.com/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("https://example.com/") + end + + it "should heuristically parse to 'https://example.com/' " + + "even with a scheme hint of 'ftp'" do + @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'}) + expect(@uri.to_s).to eq("https://example.com/") + end +end + +describe Addressable::URI, "when given the input " + + "'http://example.com/example.com/'" do + before do + @input = "http://example.com/example.com/" + end + + it "should heuristically parse to 'http://example.com/example.com/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("http://example.com/example.com/") + end +end + +describe Addressable::URI, "when given the input " + + "'http://prefix\\.example.com/'" do + before do + @input = "http://prefix\\.example.com/" + end + + it "should heuristically parse to 'http://prefix/.example.com/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.authority).to eq("prefix") + expect(@uri.to_s).to eq("http://prefix/.example.com/") + end + + it "should heuristically parse to 'http://prefix/.example.com/' " + + "even with a scheme hint of 'ftp'" do + @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'}) + expect(@uri.to_s).to eq("http://prefix/.example.com/") + end +end + +describe Addressable::URI, "when given the input " + + "'http://p:\\/'" do + before do + @input = "http://p:\\/" + end + + it "should heuristically parse to 'http://p//'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.authority).to eq("p") + expect(@uri.to_s).to eq("http://p//") + end + + it "should heuristically parse to 'http://p//' " + + "even with a scheme hint of 'ftp'" do + @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'}) + expect(@uri.to_s).to eq("http://p//") + end +end + +describe Addressable::URI, "when given the input " + + "'http://p://'" do + before do + @input = "http://p://" + end + + it "should heuristically parse to 'http://p//'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.authority).to eq("p") + expect(@uri.to_s).to eq("http://p//") + end + + it "should heuristically parse to 'http://p//' " + + "even with a scheme hint of 'ftp'" do + @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'}) + expect(@uri.to_s).to eq("http://p//") + end +end + +describe Addressable::URI, "when given the input " + + "'http://p://p'" do + before do + @input = "http://p://p" + end + + it "should heuristically parse to 'http://p//p'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.authority).to eq("p") + expect(@uri.to_s).to eq("http://p//p") + end + + it "should heuristically parse to 'http://p//p' " + + "even with a scheme hint of 'ftp'" do + @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'}) + expect(@uri.to_s).to eq("http://p//p") + end +end + +describe Addressable::URI, "when given the input " + + "'http://prefix .example.com/'" do + before do + @input = "http://prefix .example.com/" + end + + # Justification here being that no browser actually tries to resolve this. + # They all treat this as a web search. + it "should heuristically parse to 'http://prefix%20.example.com/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.authority).to eq("prefix%20.example.com") + expect(@uri.to_s).to eq("http://prefix%20.example.com/") + end + + it "should heuristically parse to 'http://prefix%20.example.com/' " + + "even with a scheme hint of 'ftp'" do + @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'}) + expect(@uri.to_s).to eq("http://prefix%20.example.com/") + end +end + +describe Addressable::URI, "when given the input " + + "' http://www.example.com/ '" do + before do + @input = " http://www.example.com/ " + end + + it "should heuristically parse to 'http://prefix%20.example.com/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.scheme).to eq("http") + expect(@uri.path).to eq("/") + expect(@uri.to_s).to eq("http://www.example.com/") + end +end + +describe Addressable::URI, "when given the input " + + "'http://prefix%2F.example.com/'" do + before do + @input = "http://prefix%2F.example.com/" + end + + it "should heuristically parse to 'http://prefix%2F.example.com/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.authority).to eq("prefix%2F.example.com") + expect(@uri.to_s).to eq("http://prefix%2F.example.com/") + end + + it "should heuristically parse to 'http://prefix%2F.example.com/' " + + "even with a scheme hint of 'ftp'" do + @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'}) + expect(@uri.to_s).to eq("http://prefix%2F.example.com/") + end +end + +describe Addressable::URI, "when given the input " + + "'/path/to/resource'" do + before do + @input = "/path/to/resource" + end + + it "should heuristically parse to '/path/to/resource'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("/path/to/resource") + end +end + +describe Addressable::URI, "when given the input " + + "'relative/path/to/resource'" do + before do + @input = "relative/path/to/resource" + end + + it "should heuristically parse to 'relative/path/to/resource'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("relative/path/to/resource") + end +end + +describe Addressable::URI, "when given the input " + + "'example.com'" do + before do + @input = "example.com" + end + + it "should heuristically parse to 'http://example.com'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("http://example.com") + end +end + +describe Addressable::URI, "when given the input " + + "'example.com' and a scheme hint of 'ftp'" do + before do + @input = "example.com" + @hints = {:scheme => 'ftp'} + end + + it "should heuristically parse to 'http://example.com'" do + @uri = Addressable::URI.heuristic_parse(@input, @hints) + expect(@uri.to_s).to eq("ftp://example.com") + end +end + +describe Addressable::URI, "when given the input " + + "'example.com:21' and a scheme hint of 'ftp'" do + before do + @input = "example.com:21" + @hints = {:scheme => 'ftp'} + end + + it "should heuristically parse to 'http://example.com:21'" do + @uri = Addressable::URI.heuristic_parse(@input, @hints) + expect(@uri.to_s).to eq("ftp://example.com:21") + end +end + +describe Addressable::URI, "when given the input " + + "'example.com/path/to/resource'" do + before do + @input = "example.com/path/to/resource" + end + + it "should heuristically parse to 'http://example.com/path/to/resource'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("http://example.com/path/to/resource") + end +end + +describe Addressable::URI, "when given the input " + + "'http:///example.com'" do + before do + @input = "http:///example.com" + end + + it "should heuristically parse to 'http://example.com'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("http://example.com") + end +end + +describe Addressable::URI, "when given the input which "\ + "start with digits and has specified port" do + before do + @input = "7777.example.org:8089" + end + + it "should heuristically parse to 'http://7777.example.org:8089'" do + uri = Addressable::URI.heuristic_parse(@input) + expect(uri.to_s).to eq("http://7777.example.org:8089") + end +end + +describe Addressable::URI, "when given the input " + + "'feed:///example.com'" do + before do + @input = "feed:///example.com" + end + + it "should heuristically parse to 'feed://example.com'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("feed://example.com") + end +end + +describe Addressable::URI, "when given the input " + + "'file://localhost/path/to/resource/'" do + before do + @input = "file://localhost/path/to/resource/" + end + + it "should heuristically parse to 'file:///path/to/resource/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("file:///path/to/resource/") + end +end + +describe Addressable::URI, "when given the input " + + "'file://path/to/resource/'" do + before do + @input = "file://path/to/resource/" + end + + it "should heuristically parse to 'file:///path/to/resource/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("file:///path/to/resource/") + end +end + +describe Addressable::URI, "when given the input " + + "'file://///path/to/resource/'" do + before do + @input = "file:///////path/to/resource/" + end + + it "should heuristically parse to 'file:////path/to/resource/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("file:////path/to/resource/") + end +end + +describe Addressable::URI, "when given the input " + + "'feed://http://example.com'" do + before do + @input = "feed://http://example.com" + end + + it "should heuristically parse to 'feed:http://example.com'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("feed:http://example.com") + end +end + +describe Addressable::URI, "when given the input " + + "::URI.parse('http://example.com')" do + before do + @input = ::URI.parse('http://example.com') + end + + it "should heuristically parse to 'http://example.com'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("http://example.com") + end +end + +describe Addressable::URI, "when given the input: 'user@domain.com'" do + before do + @input = "user@domain.com" + end + + context "for heuristic parse" do + it "should remain 'mailto:user@domain.com'" do + uri = Addressable::URI.heuristic_parse("mailto:#{@input}") + expect(uri.to_s).to eq("mailto:user@domain.com") + end + + it "should have a scheme of 'mailto'" do + uri = Addressable::URI.heuristic_parse(@input) + expect(uri.to_s).to eq("mailto:user@domain.com") + expect(uri.scheme).to eq("mailto") + end + + it "should remain 'acct:user@domain.com'" do + uri = Addressable::URI.heuristic_parse("acct:#{@input}") + expect(uri.to_s).to eq("acct:user@domain.com") + end + + context "HTTP" do + before do + @uri = Addressable::URI.heuristic_parse("http://#{@input}/") + end + + it "should remain 'http://user@domain.com/'" do + expect(@uri.to_s).to eq("http://user@domain.com/") + end + + it "should have the username 'user' for HTTP basic authentication" do + expect(@uri.user).to eq("user") + end + end + end +end + +describe Addressable::URI, "when assigning query values" do + before do + @uri = Addressable::URI.new + end + + it "should correctly assign {:a => 'a', :b => ['c', 'd', 'e']}" do + @uri.query_values = {:a => "a", :b => ["c", "d", "e"]} + expect(@uri.query).to eq("a=a&b=c&b=d&b=e") + end + + it "should raise an error attempting to assign {'a' => {'b' => ['c']}}" do + expect do + @uri.query_values = { 'a' => {'b' => ['c'] } } + end.to raise_error(TypeError) + end + + it "should raise an error attempting to assign " + + "{:b => '2', :a => {:c => '1'}}" do + expect do + @uri.query_values = {:b => '2', :a => {:c => '1'}} + end.to raise_error(TypeError) + end + + it "should raise an error attempting to assign " + + "{:a => 'a', :b => [{:c => 'c', :d => 'd'}, " + + "{:e => 'e', :f => 'f'}]}" do + expect do + @uri.query_values = { + :a => "a", :b => [{:c => "c", :d => "d"}, {:e => "e", :f => "f"}] + } + end.to raise_error(TypeError) + end + + it "should raise an error attempting to assign " + + "{:a => 'a', :b => [{:c => true, :d => 'd'}, " + + "{:e => 'e', :f => 'f'}]}" do + expect do + @uri.query_values = { + :a => 'a', :b => [{:c => true, :d => 'd'}, {:e => 'e', :f => 'f'}] + } + end.to raise_error(TypeError) + end + + it "should raise an error attempting to assign " + + "{:a => 'a', :b => {:c => true, :d => 'd'}}" do + expect do + @uri.query_values = { + :a => 'a', :b => {:c => true, :d => 'd'} + } + end.to raise_error(TypeError) + end + + it "should raise an error attempting to assign " + + "{:a => 'a', :b => {:c => true, :d => 'd'}}" do + expect do + @uri.query_values = { + :a => 'a', :b => {:c => true, :d => 'd'} + } + end.to raise_error(TypeError) + end + + it "should correctly assign {:a => 1, :b => 1.5}" do + @uri.query_values = { :a => 1, :b => 1.5 } + expect(@uri.query).to eq("a=1&b=1.5") + end + + it "should raise an error attempting to assign " + + "{:z => 1, :f => [2, {999.1 => [3,'4']}, ['h', 'i']], " + + ":a => {:b => ['c', 'd'], :e => true, :y => 0.5}}" do + expect do + @uri.query_values = { + :z => 1, + :f => [ 2, {999.1 => [3,'4']}, ['h', 'i'] ], + :a => { :b => ['c', 'd'], :e => true, :y => 0.5 } + } + end.to raise_error(TypeError) + end + + it "should correctly assign {}" do + @uri.query_values = {} + expect(@uri.query).to eq('') + end + + it "should correctly assign nil" do + @uri.query_values = nil + expect(@uri.query).to eq(nil) + end + + it "should correctly sort {'ab' => 'c', :ab => 'a', :a => 'x'}" do + @uri.query_values = {'ab' => 'c', :ab => 'a', :a => 'x'} + expect(@uri.query).to eq("a=x&ab=a&ab=c") + end + + it "should correctly assign " + + "[['b', 'c'], ['b', 'a'], ['a', 'a']]" do + # Order can be guaranteed in this format, so preserve it. + @uri.query_values = [['b', 'c'], ['b', 'a'], ['a', 'a']] + expect(@uri.query).to eq("b=c&b=a&a=a") + end + + it "should preserve query string order" do + query_string = (('a'..'z').to_a.reverse.map { |e| "#{e}=#{e}" }).join("&") + @uri.query = query_string + original_uri = @uri.to_s + @uri.query_values = @uri.query_values(Array) + expect(@uri.to_s).to eq(original_uri) + end + + describe 'when a hash with mixed types is assigned to query_values' do + it 'should not raise an error' do + skip 'Issue #94' + expect { subject.query_values = { "page" => "1", :page => 2 } }.to_not raise_error + end + end +end + +describe Addressable::URI, "when assigning path values" do + before do + @uri = Addressable::URI.new + end + + it "should correctly assign paths containing colons" do + @uri.path = "acct:bob@sporkmonger.com" + expect(@uri.path).to eq("acct:bob@sporkmonger.com") + expect(@uri.normalize.to_str).to eq("acct%2Fbob@sporkmonger.com") + expect { @uri.to_s }.to raise_error( + Addressable::URI::InvalidURIError + ) + end + + it "should correctly assign paths containing colons" do + @uri.path = "/acct:bob@sporkmonger.com" + @uri.authority = "example.com" + expect(@uri.normalize.to_str).to eq("//example.com/acct:bob@sporkmonger.com") + end + + it "should correctly assign paths containing colons" do + @uri.path = "acct:bob@sporkmonger.com" + @uri.scheme = "something" + expect(@uri.normalize.to_str).to eq("something:acct:bob@sporkmonger.com") + end + + it "should not allow relative paths to be assigned on absolute URIs" do + expect do + @uri.scheme = "http" + @uri.host = "example.com" + @uri.path = "acct:bob@sporkmonger.com" + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should not allow relative paths to be assigned on absolute URIs" do + expect do + @uri.path = "acct:bob@sporkmonger.com" + @uri.scheme = "http" + @uri.host = "example.com" + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should not allow relative paths to be assigned on absolute URIs" do + expect do + @uri.path = "uuid:0b3ecf60-3f93-11df-a9c3-001f5bfffe12" + @uri.scheme = "urn" + end.not_to raise_error + end +end + +describe Addressable::URI, "when initializing a subclass of Addressable::URI" do + before do + @uri = Class.new(Addressable::URI).new + end + + it "should have the same class after being parsed" do + expect(@uri.class).to eq(Addressable::URI.parse(@uri).class) + end + + it "should have the same class as its duplicate" do + expect(@uri.class).to eq(@uri.dup.class) + end + + it "should have the same class after being normalized" do + expect(@uri.class).to eq(@uri.normalize.class) + end + + it "should have the same class after being merged" do + expect(@uri.class).to eq(@uri.merge(:path => 'path').class) + end + + it "should have the same class after being joined" do + expect(@uri.class).to eq(@uri.join('path').class) + end +end + +describe Addressable::URI, "when initialized in a non-main `Ractor`" do + it "should have the same value as if used in the main `Ractor`" do + pending("Ruby 3.0+ for `Ractor` support") unless defined?(Ractor) + main = Addressable::URI.parse("http://example.com") + expect( + Ractor.new { Addressable::URI.parse("http://example.com") }.take + ).to eq(main) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/spec/spec_helper.rb b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/spec/spec_helper.rb new file mode 100644 index 0000000..bd8e395 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/spec/spec_helper.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require 'bundler/setup' +require 'rspec/its' + +begin + require 'coveralls' + Coveralls.wear! do + add_filter "spec/" + add_filter "vendor/" + end +rescue LoadError + warn "warning: coveralls gem not found; skipping Coveralls" + require 'simplecov' + SimpleCov.start do + add_filter "spec/" + add_filter "vendor/" + end +end if Gem.loaded_specs.key?("simplecov") + +class TestHelper + def self.native_supported? + mri = RUBY_ENGINE == "ruby" + windows = RUBY_PLATFORM.include?("mingw") + + mri && !windows + end +end + +RSpec.configure do |config| + config.warnings = true + config.filter_run_when_matching :focus +end diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/tasks/clobber.rake b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/tasks/clobber.rake new file mode 100644 index 0000000..a9e32b3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/tasks/clobber.rake @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +desc "Remove all build products" +task "clobber" diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/tasks/gem.rake b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/tasks/gem.rake new file mode 100644 index 0000000..24d9714 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/tasks/gem.rake @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +require "rubygems/package_task" + +namespace :gem do + GEM_SPEC = Gem::Specification.new do |s| + s.name = PKG_NAME + s.version = PKG_VERSION + s.summary = PKG_SUMMARY + s.description = PKG_DESCRIPTION + + s.files = PKG_FILES.to_a + + s.extra_rdoc_files = %w( README.md ) + s.rdoc_options.concat ["--main", "README.md"] + + if !s.respond_to?(:add_development_dependency) + puts "Cannot build Gem with this version of RubyGems." + exit(1) + end + + s.required_ruby_version = ">= 2.2" + + s.add_runtime_dependency "public_suffix", ">= 2.0.2", "< 6.0" + s.add_development_dependency "bundler", ">= 1.0", "< 3.0" + + s.require_path = "lib" + + s.author = "Bob Aman" + s.email = "bob@sporkmonger.com" + s.homepage = "https://github.com/sporkmonger/addressable" + s.license = "Apache-2.0" + s.metadata = { + "changelog_uri" => "https://github.com/sporkmonger/addressable/blob/main/CHANGELOG.md" + } + end + + Gem::PackageTask.new(GEM_SPEC) do |p| + p.gem_spec = GEM_SPEC + p.need_tar = true + p.need_zip = true + end + + desc "Generates .gemspec file" + task :gemspec do + spec_string = GEM_SPEC.to_ruby + File.open("#{GEM_SPEC.name}.gemspec", "w") do |file| + file.write spec_string + end + end + + desc "Show information about the gem" + task :debug do + puts GEM_SPEC.to_ruby + end + + desc "Install the gem" + task :install => ["clobber", "gem:package"] do + sh "#{SUDO} gem install --local pkg/#{GEM_SPEC.full_name}" + end + + desc "Uninstall the gem" + task :uninstall do + installed_list = Gem.source_index.find_name(PKG_NAME) + if installed_list && + (installed_list.collect { |s| s.version.to_s}.include?(PKG_VERSION)) + sh( + "#{SUDO} gem uninstall --version '#{PKG_VERSION}' " + + "--ignore-dependencies --executables #{PKG_NAME}" + ) + end + end + + desc "Reinstall the gem" + task :reinstall => [:uninstall, :install] + + desc "Package for release" + task :release => ["gem:package", "gem:gemspec"] do |t| + v = ENV["VERSION"] or abort "Must supply VERSION=x.y.z" + abort "Versions don't match #{v} vs #{PROJ.version}" if v != PKG_VERSION + pkg = "pkg/#{GEM_SPEC.full_name}" + + changelog = File.open("CHANGELOG.md") { |file| file.read } + + puts "Releasing #{PKG_NAME} v. #{PKG_VERSION}" + Rake::Task["git:tag:create"].invoke + end +end + +desc "Alias to gem:package" +task "gem" => "gem:package" + +task "gem:release" => "gem:gemspec" + +task "clobber" => ["gem:clobber_package"] diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/tasks/git.rake b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/tasks/git.rake new file mode 100644 index 0000000..1238c8d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/tasks/git.rake @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +namespace :git do + namespace :tag do + desc "List tags from the Git repository" + task :list do + tags = `git tag -l` + tags.gsub!("\r", "") + tags = tags.split("\n").sort {|a, b| b <=> a } + puts tags.join("\n") + end + + desc "Create a new tag in the Git repository" + task :create do + changelog = File.open("CHANGELOG.md", "r") { |file| file.read } + puts "-" * 80 + puts changelog + puts "-" * 80 + puts + + v = ENV["VERSION"] or abort "Must supply VERSION=x.y.z" + abort "Versions don't match #{v} vs #{PKG_VERSION}" if v != PKG_VERSION + + git_status = `git status` + if git_status !~ /^nothing to commit/ + abort "Working directory isn't clean." + end + + tag = "#{PKG_NAME}-#{PKG_VERSION}" + msg = "Release #{PKG_NAME}-#{PKG_VERSION}" + + existing_tags = `git tag -l #{PKG_NAME}-*`.split('\n') + if existing_tags.include?(tag) + warn("Tag already exists, deleting...") + unless system "git tag -d #{tag}" + abort "Tag deletion failed." + end + end + puts "Creating git tag '#{tag}'..." + unless system "git tag -a -m \"#{msg}\" #{tag}" + abort "Tag creation failed." + end + end + end +end + +task "gem:release" => "git:tag:create" diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/tasks/metrics.rake b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/tasks/metrics.rake new file mode 100644 index 0000000..107cc24 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/tasks/metrics.rake @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +namespace :metrics do + task :lines do + lines, codelines, total_lines, total_codelines = 0, 0, 0, 0 + for file_name in FileList["lib/**/*.rb"] + f = File.open(file_name) + while line = f.gets + lines += 1 + next if line =~ /^\s*$/ + next if line =~ /^\s*#/ + codelines += 1 + end + puts "L: #{sprintf("%4d", lines)}, " + + "LOC #{sprintf("%4d", codelines)} | #{file_name}" + total_lines += lines + total_codelines += codelines + + lines, codelines = 0, 0 + end + + puts "Total: Lines #{total_lines}, LOC #{total_codelines}" + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/tasks/profile.rake b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/tasks/profile.rake new file mode 100644 index 0000000..b697d48 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/tasks/profile.rake @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +namespace :profile do + desc "Profile Template match memory allocations" + task :template_match_memory do + require "memory_profiler" + require "addressable/template" + + start_at = Time.now.to_f + template = Addressable::Template.new("http://example.com/{?one,two,three}") + report = MemoryProfiler.report do + 30_000.times do + template.match( + "http://example.com/?one=one&two=floo&three=me" + ) + end + end + end_at = Time.now.to_f + print_options = { scale_bytes: true, normalize_paths: true } + puts "\n\n" + + if ENV["CI"] + report.pretty_print(print_options) + else + t_allocated = report.scale_bytes(report.total_allocated_memsize) + t_retained = report.scale_bytes(report.total_retained_memsize) + + puts "Total allocated: #{t_allocated} (#{report.total_allocated} objects)" + puts "Total retained: #{t_retained} (#{report.total_retained} objects)" + puts "Took #{end_at - start_at} seconds" + + FileUtils.mkdir_p("tmp") + report.pretty_print(to_file: "tmp/memprof.txt", **print_options) + end + end + + desc "Profile URI parse memory allocations" + task :memory do + require "memory_profiler" + require "addressable/uri" + if ENV["IDNA_MODE"] == "pure" + Addressable.send(:remove_const, :IDNA) + load "addressable/idna/pure.rb" + end + + start_at = Time.now.to_f + report = MemoryProfiler.report do + 30_000.times do + Addressable::URI.parse( + "http://google.com/stuff/../?with_lots=of¶ms=asdff#!stuff" + ).normalize + end + end + end_at = Time.now.to_f + print_options = { scale_bytes: true, normalize_paths: true } + puts "\n\n" + + if ENV["CI"] + report.pretty_print(**print_options) + else + t_allocated = report.scale_bytes(report.total_allocated_memsize) + t_retained = report.scale_bytes(report.total_retained_memsize) + + puts "Total allocated: #{t_allocated} (#{report.total_allocated} objects)" + puts "Total retained: #{t_retained} (#{report.total_retained} objects)" + puts "Took #{end_at - start_at} seconds" + + FileUtils.mkdir_p("tmp") + report.pretty_print(to_file: "tmp/memprof.txt", **print_options) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/tasks/rspec.rake b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/tasks/rspec.rake new file mode 100644 index 0000000..e3d9f01 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/tasks/rspec.rake @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require "rspec/core/rake_task" + +namespace :spec do + RSpec::Core::RakeTask.new(:simplecov) do |t| + t.pattern = FileList['spec/**/*_spec.rb'] + t.rspec_opts = %w[--color --format documentation] unless ENV["CI"] + end + + namespace :simplecov do + desc "Browse the code coverage report." + task :browse => "spec:simplecov" do + require "launchy" + Launchy.open("coverage/index.html") + end + end +end + +desc "Alias to spec:simplecov" +task "spec" => "spec:simplecov" + +task "clobber" => ["spec:clobber_simplecov"] diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/tasks/yard.rake b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/tasks/yard.rake new file mode 100644 index 0000000..515f960 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/tasks/yard.rake @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require "rake" + +begin + require "yard" + require "yard/rake/yardoc_task" + + namespace :doc do + desc "Generate Yardoc documentation" + YARD::Rake::YardocTask.new do |yardoc| + yardoc.name = "yard" + yardoc.options = ["--verbose", "--markup", "markdown"] + yardoc.files = FileList[ + "lib/**/*.rb", "ext/**/*.c", + "README.md", "CHANGELOG.md", "LICENSE.txt" + ].exclude(/idna/) + end + end + + task "clobber" => ["doc:clobber_yard"] + + desc "Alias to doc:yard" + task "doc" => "doc:yard" +rescue LoadError + # If yard isn't available, it's not the end of the world + desc "Alias to doc:rdoc" + task "doc" => "doc:rdoc" +end diff --git a/vendor/bundle/ruby/2.7.0/gems/ast-2.4.2/LICENSE.MIT b/vendor/bundle/ruby/2.7.0/gems/ast-2.4.2/LICENSE.MIT new file mode 100644 index 0000000..7c483cf --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/ast-2.4.2/LICENSE.MIT @@ -0,0 +1,20 @@ +Copyright (c) 2011-2013 Peter Zotov + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/bundle/ruby/2.7.0/gems/ast-2.4.2/README.YARD.md b/vendor/bundle/ruby/2.7.0/gems/ast-2.4.2/README.YARD.md new file mode 100644 index 0000000..d2616c3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/ast-2.4.2/README.YARD.md @@ -0,0 +1,12 @@ +{AST} is a library for manipulating abstract syntax trees. + +It embraces immutability; each AST node is inherently frozen at +creation, and updating a child node requires recreating that node +and its every parent, recursively. + +This is a design choice. It does create some pressure on +garbage collector, but completely eliminates all concurrency +and aliasing problems. + +See also {AST::Node}, {AST::Processor::Mixin} and {AST::Sexp} for +additional recommendations and design patterns. diff --git a/vendor/bundle/ruby/2.7.0/gems/ast-2.4.2/lib/ast.rb b/vendor/bundle/ruby/2.7.0/gems/ast-2.4.2/lib/ast.rb new file mode 100644 index 0000000..acbcf74 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/ast-2.4.2/lib/ast.rb @@ -0,0 +1,17 @@ +# {AST} is a library for manipulating abstract syntax trees. +# +# It embraces immutability; each AST node is inherently frozen at +# creation, and updating a child node requires recreating that node +# and its every parent, recursively. +# This is a design choice. It does create some pressure on +# garbage collector, but completely eliminates all concurrency +# and aliasing problems. +# +# See also {AST::Node}, {AST::Processor::Mixin} and {AST::Sexp} for +# additional recommendations and design patterns. +# +module AST + require 'ast/node' + require 'ast/processor' + require 'ast/sexp' +end diff --git a/vendor/bundle/ruby/2.7.0/gems/ast-2.4.2/lib/ast/node.rb b/vendor/bundle/ruby/2.7.0/gems/ast-2.4.2/lib/ast/node.rb new file mode 100644 index 0000000..432d865 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/ast-2.4.2/lib/ast/node.rb @@ -0,0 +1,268 @@ +# frozen_string_literal: true + +module AST + # Node is an immutable class, instances of which represent abstract + # syntax tree nodes. It combines semantic information (i.e. anything + # that affects the algorithmic properties of a program) with + # meta-information (line numbers or compiler intermediates). + # + # Notes on inheritance + # ==================== + # + # The distinction between semantics and metadata is important. Complete + # semantic information should be contained within just the {#type} and + # {#children} of a Node instance; in other words, if an AST was to be + # stripped of all meta-information, it should remain a valid AST which + # could be successfully processed to yield a result with the same + # algorithmic properties. + # + # Thus, Node should never be inherited in order to define methods which + # affect or return semantic information, such as getters for `class_name`, + # `superclass` and `body` in the case of a hypothetical `ClassNode`. The + # correct solution is to use a generic Node with a {#type} of `:class` + # and three children. See also {Processor} for tips on working with such + # ASTs. + # + # On the other hand, Node can and should be inherited to define + # application-specific metadata (see also {#initialize}) or customize the + # printing format. It is expected that an application would have one or two + # such classes and use them across the entire codebase. + # + # The rationale for this pattern is extensibility and maintainability. + # Unlike static ones, dynamic languages do not require the presence of a + # predefined, rigid structure, nor does it improve dispatch efficiency, + # and while such a structure can certainly be defined, it does not add + # any value but incurs a maintaining cost. + # For example, extending the AST even with a transformation-local + # temporary node type requires making globally visible changes to + # the codebase. + # + class Node + # Returns the type of this node. + # @return [Symbol] + attr_reader :type + + # Returns the children of this node. + # The returned value is frozen. + # The to_a alias is useful for decomposing nodes concisely. + # For example: + # + # node = s(:gasgn, :$foo, s(:integer, 1)) + # var_name, value = *node + # p var_name # => :$foo + # p value # => (integer 1) + # + # @return [Array] + attr_reader :children + alias to_a children + + # Returns the precomputed hash value for this node + # @return [Fixnum] + attr_reader :hash + + # Constructs a new instance of Node. + # + # The arguments `type` and `children` are converted with `to_sym` and + # `to_a` respectively. Additionally, the result of converting `children` + # is frozen. While mutating the arguments is generally considered harmful, + # the most common case is to pass an array literal to the constructor. If + # your code does not expect the argument to be frozen, use `#dup`. + # + # The `properties` hash is passed to {#assign_properties}. + def initialize(type, children=[], properties={}) + @type, @children = type.to_sym, children.to_a.freeze + + assign_properties(properties) + + @hash = [@type, @children, self.class].hash + + freeze + end + + # Test if other object is equal to + # @param [Object] other + # @return [Boolean] + def eql?(other) + self.class.eql?(other.class) && + @type.eql?(other.type) && + @children.eql?(other.children) + end + + # By default, each entry in the `properties` hash is assigned to + # an instance variable in this instance of Node. A subclass should define + # attribute readers for such variables. The values passed in the hash + # are not frozen or whitelisted; such behavior can also be implemented + # by subclassing Node and overriding this method. + # + # @return [nil] + def assign_properties(properties) + properties.each do |name, value| + instance_variable_set :"@#{name}", value + end + + nil + end + protected :assign_properties + + alias :original_dup :dup + private :original_dup + + # Nodes are already frozen, so there is no harm in returning the + # current node as opposed to initializing from scratch and freezing + # another one. + # + # @return self + def dup + self + end + alias :clone :dup + + # Returns a new instance of Node where non-nil arguments replace the + # corresponding fields of `self`. + # + # For example, `Node.new(:foo, [ 1, 2 ]).updated(:bar)` would yield + # `(bar 1 2)`, and `Node.new(:foo, [ 1, 2 ]).updated(nil, [])` would + # yield `(foo)`. + # + # If the resulting node would be identical to `self`, does nothing. + # + # @param [Symbol, nil] type + # @param [Array, nil] children + # @param [Hash, nil] properties + # @return [AST::Node] + def updated(type=nil, children=nil, properties=nil) + new_type = type || @type + new_children = children || @children + new_properties = properties || {} + + if @type == new_type && + @children == new_children && + properties.nil? + self + else + copy = original_dup + copy.send :initialize, new_type, new_children, new_properties + copy + end + end + + # Compares `self` to `other`, possibly converting with `to_ast`. Only + # `type` and `children` are compared; metadata is deliberately ignored. + # + # @return [Boolean] + def ==(other) + if equal?(other) + true + elsif other.respond_to? :to_ast + other = other.to_ast + other.type == self.type && + other.children == self.children + else + false + end + end + + # Concatenates `array` with `children` and returns the resulting node. + # + # @return [AST::Node] + def concat(array) + updated(nil, @children + array.to_a) + end + + alias + concat + + # Appends `element` to `children` and returns the resulting node. + # + # @return [AST::Node] + def append(element) + updated(nil, @children + [element]) + end + + alias << append + + # Converts `self` to a pretty-printed s-expression. + # + # @param [Integer] indent Base indentation level. + # @return [String] + def to_sexp(indent=0) + indented = " " * indent + sexp = "#{indented}(#{fancy_type}" + + children.each do |child| + if child.is_a?(Node) + sexp += "\n#{child.to_sexp(indent + 1)}" + else + sexp += " #{child.inspect}" + end + end + + sexp += ")" + + sexp + end + + alias to_s to_sexp + + # Converts `self` to a s-expression ruby string. + # The code return will recreate the node, using the sexp module s() + # + # @param [Integer] indent Base indentation level. + # @return [String] + def inspect(indent=0) + indented = " " * indent + sexp = "#{indented}s(:#{@type}" + + children.each do |child| + if child.is_a?(Node) + sexp += ",\n#{child.inspect(indent + 1)}" + else + sexp += ", #{child.inspect}" + end + end + + sexp += ")" + + sexp + end + + # @return [AST::Node] self + def to_ast + self + end + + # Converts `self` to an Array where the first element is the type as a Symbol, + # and subsequent elements are the same representation of its children. + # + # @return [Array] + def to_sexp_array + children_sexp_arrs = children.map do |child| + if child.is_a?(Node) + child.to_sexp_array + else + child + end + end + + [type, *children_sexp_arrs] + end + + # Enables matching for Node, where type is the first element + # and the children are remaining items. + # + # @return [Array] + def deconstruct + [type, *children] + end + + protected + + # Returns `@type` with all underscores replaced by dashes. This allows + # to write symbol literals without quotes in Ruby sources and yet have + # nicely looking s-expressions. + # + # @return [String] + def fancy_type + @type.to_s.gsub('_', '-') + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/ast-2.4.2/lib/ast/processor.rb b/vendor/bundle/ruby/2.7.0/gems/ast-2.4.2/lib/ast/processor.rb new file mode 100644 index 0000000..f1910c5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/ast-2.4.2/lib/ast/processor.rb @@ -0,0 +1,12 @@ +module AST + # This class includes {AST::Processor::Mixin}; however, it is + # deprecated, since the module defines all of the behaviors that + # the processor includes. Any new libraries should use + # {AST::Processor::Mixin} instead of subclassing this. + # + # @deprecated Use {AST::Processor::Mixin} instead. + class Processor + require 'ast/processor/mixin' + include Mixin + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/ast-2.4.2/lib/ast/processor/mixin.rb b/vendor/bundle/ruby/2.7.0/gems/ast-2.4.2/lib/ast/processor/mixin.rb new file mode 100644 index 0000000..6febec8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/ast-2.4.2/lib/ast/processor/mixin.rb @@ -0,0 +1,288 @@ +module AST + class Processor + # The processor module is a module which helps transforming one + # AST into another. In a nutshell, the {#process} method accepts + # a {Node} and dispatches it to a handler corresponding to its + # type, and returns a (possibly) updated variant of the node. + # + # The processor module has a set of associated design patterns. + # They are best explained with a concrete example. Let's define a + # simple arithmetic language and an AST format for it: + # + # Terminals (AST nodes which do not have other AST nodes inside): + # + # * `(integer )`, + # + # Nonterminals (AST nodes with other nodes as children): + # + # * `(add )`, + # * `(multiply )`, + # * `(divide )`, + # * `(negate )`, + # * `(store )`: stores value of `` + # into a variable named ``, + # * `(load )`: loads value of a variable named + # ``, + # * `(each ...)`: computes each of the ``s and + # prints the result. + # + # All AST nodes have the same Ruby class, and therefore they don't + # know how to traverse themselves. (A solution which dynamically + # checks the type of children is possible, but is slow and + # error-prone.) So, a class including the module which knows how + # to traverse the entire tree should be defined. Such classes + # have a handler for each nonterminal node which recursively + # processes children nodes: + # + # require 'ast' + # + # class ArithmeticsProcessor + # include AST::Processor::Mixin + # # This method traverses any binary operators such as (add) + # # or (multiply). + # def process_binary_op(node) + # # Children aren't decomposed automatically; it is + # # suggested to use Ruby multiple assignment expansion, + # # as it is very convenient here. + # left_expr, right_expr = *node + # + # # AST::Node#updated won't change node type if nil is + # # passed as a first argument, which allows to reuse the + # # same handler for multiple node types using `alias' + # # (below). + # node.updated(nil, [ + # process(left_expr), + # process(right_expr) + # ]) + # end + # alias_method :on_add, :process_binary_op + # alias_method :on_multiply, :process_binary_op + # alias_method :on_divide, :process_binary_op + # + # def on_negate(node) + # # It is also possible to use #process_all for more + # # compact code if every child is a Node. + # node.updated(nil, process_all(node)) + # end + # + # def on_store(node) + # expr, variable_name = *node + # + # # Note that variable_name is not a Node and thus isn't + # # passed to #process. + # node.updated(nil, [ + # process(expr), + # variable_name + # ]) + # end + # + # # (load) is effectively a terminal node, and so it does + # # not need an explicit handler, as the following is the + # # default behavior. Essentially, for any nodes that don't + # # have a defined handler, the node remains unchanged. + # def on_load(node) + # nil + # end + # + # def on_each(node) + # node.updated(nil, process_all(node)) + # end + # end + # + # Let's test our ArithmeticsProcessor: + # + # include AST::Sexp + # expr = s(:add, s(:integer, 2), s(:integer, 2)) + # + # p ArithmeticsProcessor.new.process(expr) == expr # => true + # + # As expected, it does not change anything at all. This isn't + # actually very useful, so let's now define a Calculator, which + # will compute the expression values: + # + # # This Processor folds nonterminal nodes and returns an + # # (integer) terminal node. + # class ArithmeticsCalculator < ArithmeticsProcessor + # def compute_op(node) + # # First, node children are processed and then unpacked + # # to local variables. + # nodes = process_all(node) + # + # if nodes.all? { |node| node.type == :integer } + # # If each of those nodes represents a literal, we can + # # fold this node! + # values = nodes.map { |node| node.children.first } + # AST::Node.new(:integer, [ + # yield(values) + # ]) + # else + # # Otherwise, we can just leave the current node in the + # # tree and only update it with processed children + # # nodes, which can be partially folded. + # node.updated(nil, nodes) + # end + # end + # + # def on_add(node) + # compute_op(node) { |left, right| left + right } + # end + # + # def on_multiply(node) + # compute_op(node) { |left, right| left * right } + # end + # end + # + # Let's check: + # + # p ArithmeticsCalculator.new.process(expr) # => (integer 4) + # + # Excellent, the calculator works! Now, a careful reader could + # notice that the ArithmeticsCalculator does not know how to + # divide numbers. What if we pass an expression with division to + # it? + # + # expr_with_division = \ + # s(:add, + # s(:integer, 1), + # s(:divide, + # s(:add, s(:integer, 8), s(:integer, 4)), + # s(:integer, 3))) # 1 + (8 + 4) / 3 + # + # folded_expr_with_division = ArithmeticsCalculator.new.process(expr_with_division) + # p folded_expr_with_division + # # => (add + # # (integer 1) + # # (divide + # # (integer 12) + # # (integer 3))) + # + # As you can see, the expression was folded _partially_: the inner + # `(add)` node which could be computed was folded to + # `(integer 12)`, the `(divide)` node is left as-is because there + # is no computing handler for it, and the root `(add)` node was + # also left as it is because some of its children were not + # literals. + # + # Note that this partial folding is only possible because the + # _data_ format, i.e. the format in which the computed values of + # the nodes are represented, is the same as the AST itself. + # + # Let's extend our ArithmeticsCalculator class further. + # + # class ArithmeticsCalculator + # def on_divide(node) + # compute_op(node) { |left, right| left / right } + # end + # + # def on_negate(node) + # # Note how #compute_op works regardless of the operator + # # arity. + # compute_op(node) { |value| -value } + # end + # end + # + # Now, let's apply our renewed ArithmeticsCalculator to a partial + # result of previous evaluation: + # + # p ArithmeticsCalculator.new.process(expr_with_division) # => (integer 5) + # + # Five! Excellent. This is also pretty much how CRuby 1.8 executed + # its programs. + # + # Now, let's do some automated bug searching. Division by zero is + # an error, right? So if we could detect that someone has divided + # by zero before the program is even run, that could save some + # debugging time. + # + # class DivisionByZeroVerifier < ArithmeticsProcessor + # class VerificationFailure < Exception; end + # + # def on_divide(node) + # # You need to process the children to handle nested divisions + # # such as: + # # (divide + # # (integer 1) + # # (divide (integer 1) (integer 0)) + # left, right = process_all(node) + # + # if right.type == :integer && + # right.children.first == 0 + # raise VerificationFailure, "Ouch! This code divides by zero." + # end + # end + # + # def divides_by_zero?(ast) + # process(ast) + # false + # rescue VerificationFailure + # true + # end + # end + # + # nice_expr = \ + # s(:divide, + # s(:add, s(:integer, 10), s(:integer, 2)), + # s(:integer, 4)) + # + # p DivisionByZeroVerifier.new.divides_by_zero?(nice_expr) + # # => false. Good. + # + # bad_expr = \ + # s(:add, s(:integer, 10), + # s(:divide, s(:integer, 1), s(:integer, 0))) + # + # p DivisionByZeroVerifier.new.divides_by_zero?(bad_expr) + # # => true. WHOOPS. DO NOT RUN THIS. + # + # Of course, this won't detect more complex cases... unless you + # use some partial evaluation before! The possibilites are + # endless. Have fun. + module Mixin + # Dispatches `node`. If a node has type `:foo`, then a handler + # named `on_foo` is invoked with one argument, the `node`; if + # there isn't such a handler, {#handler_missing} is invoked + # with the same argument. + # + # If the handler returns `nil`, `node` is returned; otherwise, + # the return value of the handler is passed along. + # + # @param [AST::Node, nil] node + # @return [AST::Node, nil] + def process(node) + return if node.nil? + + node = node.to_ast + + # Invoke a specific handler + on_handler = :"on_#{node.type}" + if respond_to? on_handler + new_node = send on_handler, node + else + new_node = handler_missing(node) + end + + node = new_node if new_node + + node + end + + # {#process}es each node from `nodes` and returns an array of + # results. + # + # @param [Array] nodes + # @return [Array] + def process_all(nodes) + nodes.to_a.map do |node| + process node + end + end + + # Default handler. Does nothing. + # + # @param [AST::Node] node + # @return [AST::Node, nil] + def handler_missing(node) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/ast-2.4.2/lib/ast/sexp.rb b/vendor/bundle/ruby/2.7.0/gems/ast-2.4.2/lib/ast/sexp.rb new file mode 100644 index 0000000..9b03d26 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/ast-2.4.2/lib/ast/sexp.rb @@ -0,0 +1,30 @@ +module AST + # This simple module is very useful in the cases where one needs + # to define deeply nested ASTs from Ruby code, for example, in + # tests. It should be used like this: + # + # describe YourLanguage::AST do + # include Sexp + # + # it "should correctly parse expressions" do + # YourLanguage.parse("1 + 2 * 3").should == + # s(:add, + # s(:integer, 1), + # s(:multiply, + # s(:integer, 2), + # s(:integer, 3))) + # end + # end + # + # This way the amount of boilerplate code is greatly reduced. + module Sexp + # Creates a {Node} with type `type` and children `children`. + # Note that the resulting node is of the type AST::Node and not a + # subclass. + # This would not pose a problem with comparisons, as {Node#==} + # ignores metadata. + def s(type, *children) + Node.new(type, children) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async.rb b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async.rb new file mode 100644 index 0000000..2a1393a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative "async/version" +require_relative "async/logger" +require_relative "async/reactor" + +require_relative "kernel/async" +require_relative "kernel/sync" + +module Async + # Invoke `Reactor.run` with all arguments/block. + def self.run(*arguments, &block) + Reactor.run(*arguments, &block) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/barrier.rb b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/barrier.rb new file mode 100644 index 0000000..b82a98e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/barrier.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +# Copyright, 2019, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'task' + +module Async + # A barrier is used to synchronize multiple tasks, waiting for them all to complete before continuing. + class Barrier + def initialize(parent: nil) + @tasks = [] + + @parent = parent + end + + # All tasks which have been invoked into the barrier. + attr :tasks + + def size + @tasks.size + end + + def async(*arguments, parent: (@parent or Task.current), **options, &block) + task = parent.async(*arguments, **options, &block) + + @tasks << task + + return task + end + + def empty? + @tasks.empty? + end + + # Wait for all tasks. + # @asynchronous Will wait for tasks to finish executing. + def wait + # TODO: This would be better with linked list. + while @tasks.any? + task = @tasks.first + + begin + task.wait + ensure + # We don't know for sure that the exception was due to the task completion. + unless task.running? + # Remove the task from the waiting list if it's finished: + @tasks.shift if @tasks.first == task + end + end + end + end + + def stop + # We have to be careful to avoid enumerating tasks while adding/removing to it: + tasks = @tasks.dup + tasks.each(&:stop) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/clock.rb b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/clock.rb new file mode 100644 index 0000000..5c25114 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/clock.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +module Async + class Clock + # Get the current elapsed monotonic time. + def self.now + ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) + end + + # Measure the execution of a block of code. + def self.measure + start_time = self.now + + yield + + return self.now - start_time + end + + def self.start + self.new.tap(&:start!) + end + + def initialize(total = 0) + @total = total + @started = nil + end + + def start! + @started ||= Clock.now + end + + def stop! + if @started + @total += (Clock.now - @started) + @started = nil + end + + return @total + end + + def total + total = @total + + if @started + total += (Clock.now - @started) + end + + return total + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/condition.rb b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/condition.rb new file mode 100644 index 0000000..9940f92 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/condition.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'fiber' +require_relative 'node' + +module Async + # A synchronization primative, which allows fibers to wait until a particular condition is triggered. Signalling the condition directly resumes the waiting fibers and thus blocks the caller. + class Condition + def initialize + @waiting = [] + end + + # Queue up the current fiber and wait on yielding the task. + # @return [Object] + def wait + fiber = Fiber.current + @waiting << fiber + + Task.yield + + # It would be nice if there was a better construct for this. We only need to invoke #delete if the task was not resumed normally. This can only occur with `raise` and `throw`. But there is no easy way to detect this. + # ensure when not return or ensure when raise, throw + rescue Exception + @waiting.delete(fiber) + raise + end + + # Is any fiber waiting on this notification? + # @return [Boolean] + def empty? + @waiting.empty? + end + + # Signal to a given task that it should resume operations. + # @param value The value to return to the waiting fibers. + # @see Task.yield which is responsible for handling value. + # @return [void] + def signal(value = nil) + waiting = @waiting + @waiting = [] + + waiting.each do |fiber| + fiber.resume(value) if fiber.alive? + end + + return nil + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/debug/monitor.rb b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/debug/monitor.rb new file mode 100644 index 0000000..9983700 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/debug/monitor.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'delegate' + +module Async + module Debug + class Monitor < Delegator + def initialize(monitor, selector) + @monitor = monitor + @selector = selector + end + + def __getobj__ + @monitor + end + + def close + @selector.deregister(self) + @monitor.close + end + + def inspect + "\#<#{self.class} io=#{@monitor.io.inspect} interests=#{@monitor.interests.inspect} readiness=#{@monitor.readiness.inspect}>" + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/debug/selector.rb b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/debug/selector.rb new file mode 100644 index 0000000..c388793 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/debug/selector.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'monitor' +require_relative '../logger' + +require 'nio' +require 'set' + +module Async + module Debug + class LeakError < RuntimeError + def initialize(monitors) + super "Trying to close selector with active monitors: #{monitors.inspect}! This may cause your socket or file descriptor to leak." + end + end + + class Selector + def initialize(selector = NIO::Selector.new) + @selector = selector + @monitors = Set.new + end + + def register(object, interests) + Async.logger.debug(self) {"Registering #{object.inspect} for #{interests}."} + + unless io = ::IO.try_convert(object) + raise RuntimeError, "Could not convert #{io} into IO!" + end + + monitor = Monitor.new(@selector.register(object, interests), self) + + @monitors.add(monitor) + + return monitor + end + + def deregister(monitor) + Async.logger.debug(self) {"Deregistering #{monitor.inspect}."} + + unless @monitors.delete?(monitor) + raise RuntimeError, "Trying to remove monitor for #{monitor.inspect} but it was not registered!" + end + end + + def wakeup + @selector.wakeup + end + + def close + if @monitors.any? + raise LeakError, @monitors + end + ensure + @selector.close + end + + def select(*arguments) + @selector.select(*arguments) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/logger.rb b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/logger.rb new file mode 100644 index 0000000..8f9ed9a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/logger.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'console' +require_relative 'task' + +module Async + extend Console +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/node.rb b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/node.rb new file mode 100644 index 0000000..a75c38b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/node.rb @@ -0,0 +1,352 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +module Async + # A double linked list. + class List + def initialize + # The list behaves like a list node, so @tail points to the next item (the first one) and head points to the previous item (the last one). This may be slightly confusing but it makes the interface more natural. + @head = nil + @tail = nil + @size = 0 + end + + attr :size + + attr_accessor :head + attr_accessor :tail + + # Inserts an item at the end of the list. + def insert(item) + unless @tail + @tail = item + @head = item + + # Consistency: + item.head = nil + item.tail = nil + else + @head.tail = item + item.head = @head + + # Consistency: + item.tail = nil + + @head = item + end + + @size += 1 + + return self + end + + def delete(item) + if @tail.equal?(item) + @tail = @tail.tail + else + item.head.tail = item.tail + end + + if @head.equal?(item) + @head = @head.head + else + item.tail.head = item.head + end + + item.head = nil + item.tail = nil + + @size -= 1 + + return self + end + + def each(&block) + return to_enum unless block_given? + + current = self + while node = current.tail + yield node + + # If the node has deleted itself or any subsequent node, it will no longer be the next node, so don't use it for continued traversal: + if current.tail.equal?(node) + current = node + end + end + end + + def include?(needle) + self.each do |item| + return true if needle.equal?(item) + end + + return false + end + + def first + @tail + end + + def last + @head + end + + def empty? + @tail.nil? + end + + def nil? + @tail.nil? + end + end + + private_constant :List + + class Children < List + def initialize + super + + @transient_count = 0 + end + + # Does this node have (direct) transient children? + def transients? + @transient_count > 0 + end + + def insert(item) + if item.transient? + @transient_count += 1 + end + + super + end + + def delete(item) + if item.transient? + @transient_count -= 1 + end + + super + end + + def finished? + @size == @transient_count + end + end + + # Represents a node in a tree, used for nested {Task} instances. + class Node + # Create a new node in the tree. + # @param parent [Node, nil] This node will attach to the given parent. + def initialize(parent = nil, annotation: nil, transient: false) + @parent = nil + @children = nil + + @annotation = annotation + @object_name = nil + + @transient = transient + + @head = nil + @tail = nil + + if parent + parent.add_child(self) + end + end + + # You should not directly rely on these pointers but instead use `#children`. + # List pointers: + attr_accessor :head + attr_accessor :tail + + # @attr parent [Node, nil] + attr :parent + + # @attr children [List] Optional list of children. + attr :children + + # A useful identifier for the current node. + attr :annotation + + # Whether there are children? + def children? + @children != nil && !@children.empty? + end + + # Is this node transient? + def transient? + @transient + end + + def annotate(annotation) + if block_given? + previous_annotation = @annotation + @annotation = annotation + yield + @annotation = previous_annotation + else + @annotation = annotation + end + end + + def description + @object_name ||= "#{self.class}:0x#{object_id.to_s(16)}#{@transient ? ' transient' : nil}" + + if @annotation + "#{@object_name} #{@annotation}" + else + @object_name + end + end + + def backtrace(*arguments) + nil + end + + def to_s + "\#<#{description}>" + end + + # Change the parent of this node. + # @param parent [Node, nil] the parent to attach to, or nil to detach. + # @return [self] + def parent=(parent) + return if @parent.equal?(parent) + + if @parent + @parent.delete_child(self) + @parent = nil + end + + if parent + parent.add_child(self) + end + + return self + end + + protected def set_parent parent + @parent = parent + end + + protected def add_child child + @children ||= Children.new + @children.insert(child) + child.set_parent(self) + end + + protected def delete_child(child) + @children.delete(child) + child.set_parent(nil) + end + + # Whether the node can be consumed safely. By default, checks if the + # children set is empty. + # @return [Boolean] + def finished? + @children.nil? || @children.finished? + end + + # If the node has a parent, and is {finished?}, then remove this node from + # the parent. + def consume + if parent = @parent and finished? + parent.delete_child(self) + + if @children + @children.each do |child| + if child.finished? + delete_child(child) + else + # In theory we don't need to do this... because we are throwing away the list. However, if you don't correctly update the list when moving the child to the parent, it foobars the enumeration, and subsequent nodes will be skipped, or in the worst case you might start enumerating the parents nodes. + delete_child(child) + parent.add_child(child) + end + end + + @children = nil + end + + parent.consume + end + end + + # Traverse the tree. + # @yield [node, level] The node and the level relative to the given root. + def traverse(level = 0, &block) + yield self, level + + @children&.each do |child| + child.traverse(level + 1, &block) + end + end + + # Immediately terminate all children tasks, including transient tasks. + # Internally invokes `stop(false)` on all children. + def terminate + # Attempt to stop the current task immediately, and all children: + stop(false) + + # If that doesn't work, take more serious action: + @children&.each do |child| + child.terminate + end + end + + # Attempt to stop the current node immediately, including all non-transient children. + # Invokes {#stop_children} to stop all children. + # @parameter later [Boolean] Whether to defer stopping until some point in the future. + def stop(later = false) + # The implementation of this method may defer calling `stop_children`. + stop_children(later) + end + + # Attempt to stop all non-transient children. + private def stop_children(later = false) + @children&.each do |child| + child.stop(later) unless child.transient? + end + end + + def print_hierarchy(out = $stdout, backtrace: true) + self.traverse do |node, level| + indent = "\t" * level + + out.puts "#{indent}#{node}" + + print_backtrace(out, indent, node) if backtrace + end + end + + private + + def print_backtrace(out, indent, node) + if backtrace = node.backtrace + backtrace.each_with_index do |line, index| + out.puts "#{indent}#{index.zero? ? "→ " : " "}#{line}" + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/notification.rb b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/notification.rb new file mode 100644 index 0000000..5ae057c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/notification.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'condition' + +module Async + # A synchronization primitive, which allows fibers to wait until a notification is received. Does not block the task which signals the notification. Waiting tasks are resumed on next iteration of the reactor. + class Notification < Condition + # Signal to a given task that it should resume operations. + # @return [void] + def signal(value = nil, task: Task.current) + return if @waiting.empty? + + task.reactor << Signal.new(@waiting, value) + + @waiting = [] + + return nil + end + + Signal = Struct.new(:waiting, :value) do + def alive? + true + end + + def resume + waiting.each do |fiber| + fiber.resume(value) if fiber.alive? + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/queue.rb b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/queue.rb new file mode 100644 index 0000000..3629ac4 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/queue.rb @@ -0,0 +1,123 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'notification' + +module Async + # A queue which allows items to be processed in order. + class Queue < Notification + def initialize(parent: nil) + super() + + @items = [] + @parent = parent + end + + attr :items + + def size + @items.size + end + + def empty? + @items.empty? + end + + def <<(item) + @items << item + + self.signal unless self.empty? + end + + def enqueue(*items) + @items.concat(items) + + self.signal unless self.empty? + end + + def dequeue + while @items.empty? + self.wait + end + + @items.shift + end + + def async(parent: (@parent or Task.current), &block) + while item = self.dequeue + parent.async(item, &block) + end + end + + def each + while item = self.dequeue + yield item + end + end + end + + class LimitedQueue < Queue + def initialize(limit = 1, **options) + super(**options) + + @limit = limit + + @full = Notification.new + end + + attr :limit + + # @return [Boolean] Whether trying to enqueue an item would block. + def limited? + @items.size >= @limit + end + + def <<(item) + while limited? + @full.wait + end + + super + end + + def enqueue *items + while !items.empty? + while limited? + @full.wait + end + + available = @limit - @items.size + @items.concat(items.shift(available)) + + self.signal unless self.empty? + end + end + + def dequeue + item = super + + @full.signal + + return item + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/reactor.rb b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/reactor.rb new file mode 100644 index 0000000..41caf91 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/reactor.rb @@ -0,0 +1,345 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'logger' +require_relative 'task' +require_relative 'wrapper' +require_relative 'scheduler' + +require 'nio' +require 'timers' +require 'forwardable' + +module Async + # Raised if a timeout occurs on a specific Fiber. Handled gracefully by `Task`. + class TimeoutError < StandardError + end + + # An asynchronous, cooperatively scheduled event reactor. + class Reactor < Node + extend Forwardable + + # The preferred method to invoke asynchronous behavior at the top level. + # + # - When invoked within an existing reactor task, it will run the given block + # asynchronously. Will return the task once it has been scheduled. + # - When invoked at the top level, will create and run a reactor, and invoke + # the block as an asynchronous task. Will block until the reactor finishes + # running. + def self.run(*arguments, **options, &block) + if current = Task.current? + return current.async(*arguments, **options, &block) + else + reactor = self.new + + begin + return reactor.run(*arguments, **options, &block) + ensure + reactor.close + end + end + end + + def self.selector + if backend = ENV['ASYNC_BACKEND']&.to_sym + if NIO::Selector.backends.include?(backend) + return NIO::Selector.new(backend) + else + warn "Could not find ASYNC_BACKEND=#{backend}!" + end + end + + return NIO::Selector.new + end + + def initialize(parent = nil, selector: self.class.selector, logger: nil) + super(parent) + + @selector = selector + @timers = Timers::Group.new + @logger = logger + + @ready = [] + @running = [] + + if Scheduler.supported? + @scheduler = Scheduler.new(self) + else + @scheduler = nil + end + + @interrupted = false + @guard = Mutex.new + @blocked = 0 + @unblocked = [] + end + + attr :scheduler + attr :logger + + # @reentrant Not thread safe. + def block(blocker, timeout) + fiber = Fiber.current + + if timeout + timer = @timers.after(timeout) do + if fiber.alive? + fiber.resume(false) + end + end + end + + begin + @blocked += 1 + Task.yield + ensure + @blocked -= 1 + end + ensure + timer&.cancel + end + + # @reentrant Thread safe. + def unblock(blocker, fiber) + @guard.synchronize do + @unblocked << fiber + @selector.wakeup + end + end + + def fiber(&block) + if @scheduler + Fiber.new(blocking: false, &block) + else + Fiber.new(&block) + end + end + + def to_s + "\#<#{self.description} #{@children&.size || 0} children (#{stopped? ? 'stopped' : 'running'})>" + end + + def stopped? + @children.nil? + end + + # Start an asynchronous task within the specified reactor. The task will be + # executed until the first blocking call, at which point it will yield and + # and this method will return. + # + # This is the main entry point for scheduling asynchronus tasks. + # + # @yield [Task] Executed within the task. + # @return [Task] The task that was scheduled into the reactor. + def async(*arguments, **options, &block) + task = Task.new(self, **options, &block) + + # I want to take a moment to explain the logic of this. + # When calling an async block, we deterministically execute it until the + # first blocking operation. We don't *have* to do this - we could schedule + # it for later execution, but it's useful to: + # - Fail at the point of the method call where possible. + # - Execute determinstically where possible. + # - Avoid scheduler overhead if no blocking operation is performed. + task.run(*arguments) + + # Console.logger.debug "Initial execution of task #{fiber} complete (#{result} -> #{fiber.alive?})..." + return task + end + + def register(io, interest, value = Fiber.current) + monitor = @selector.register(io, interest) + monitor.value = value + + return monitor + end + + # Interrupt the reactor at the earliest convenience. Can be called from a different thread safely. + def interrupt + @guard.synchronize do + unless @interrupted + @interrupted = true + @selector.wakeup + end + end + end + + # Schedule a fiber (or equivalent object) to be resumed on the next loop through the reactor. + # @param fiber [#resume] The object to be resumed on the next iteration of the run-loop. + def << fiber + @ready << fiber + end + + # Yield the current fiber and resume it on the next iteration of the event loop. + def yield(fiber = Fiber.current) + @ready << fiber + + Task.yield + end + + def finished? + # TODO I'm not sure if checking `@running.empty?` is really required. + super && @ready.empty? && @running.empty? && @blocked.zero? + end + + # Run one iteration of the event loop. + # @param timeout [Float | nil] the maximum timeout, or if nil, indefinite. + # @return [Boolean] whether there is more work to do. + def run_once(timeout = nil) + # Console.logger.debug(self) {"@ready = #{@ready} @running = #{@running}"} + + if @ready.any? + # running used to correctly answer on `finished?`, and to reuse Array object. + @running, @ready = @ready, @running + + @running.each do |fiber| + fiber.resume if fiber.alive? + end + + @running.clear + end + + if @unblocked.any? + unblocked = Array.new + + @guard.synchronize do + unblocked, @unblocked = @unblocked, unblocked + end + + while fiber = unblocked.pop + fiber.resume if fiber.alive? + end + end + + if @ready.empty? + interval = @timers.wait_interval + else + # if there are tasks ready to execute, don't sleep: + interval = 0 + end + + # If we are finished, we stop the task tree and exit: + if self.finished? + return false + end + + # If there is no interval to wait (thus no timers), and no tasks, we could be done: + if interval.nil? + # Allow the user to specify a maximum interval if we would otherwise be sleeping indefinitely: + interval = timeout + elsif interval < 0 + # We have timers ready to fire, don't sleep in the selctor: + interval = 0 + elsif timeout and interval > timeout + interval = timeout + end + + # Console.logger.info(self) {"Selecting with #{@children&.size} children with interval = #{interval ? interval.round(2) : 'infinite'}..."} + if monitors = @selector.select(interval) + monitors.each do |monitor| + monitor.value.resume + end + end + + @timers.fire + + # We check and clear the interrupted flag here: + if @interrupted + @guard.synchronize do + @interrupted = false + end + + return false + end + + # The reactor still has work to do: + return true + end + + # Run the reactor until all tasks are finished. Proxies arguments to {#async} immediately before entering the loop, if a block is provided. + def run(*arguments, **options, &block) + raise RuntimeError, 'Reactor has been closed' if @selector.nil? + + @scheduler&.set! + + initial_task = self.async(*arguments, **options, &block) if block_given? + + while self.run_once + # Round and round we go! + end + + return initial_task + ensure + @scheduler&.clear! + Console.logger.debug(self) {"Exiting run-loop because #{$! ? $! : 'finished'}."} + end + + # Stop each of the children tasks and close the selector. + def close + # This is a critical step. Because tasks could be stored as instance variables, and since the reactor is (probably) going out of scope, we need to ensure they are stopped. Otherwise, the tasks will belong to a reactor that will never run again and are not stopped: + self.terminate + + @selector.close + @selector = nil + end + + # Check if the selector has been closed. + # @returns [Boolean] + def closed? + @selector.nil? + end + + # Put the calling fiber to sleep for a given ammount of time. + # @parameter duration [Numeric] The time in seconds, to sleep for. + def sleep(duration) + fiber = Fiber.current + + timer = @timers.after(duration) do + if fiber.alive? + fiber.resume + end + end + + Task.yield + ensure + timer.cancel if timer + end + + # Invoke the block, but after the specified timeout, raise {TimeoutError} in any currenly blocking operation. If the block runs to completion before the timeout occurs or there are no non-blocking operations after the timeout expires, the code will complete without any exception. + # @param duration [Numeric] The time in seconds, in which the task should + # complete. + def with_timeout(timeout, exception = TimeoutError) + fiber = Fiber.current + + timer = @timers.after(timeout) do + if fiber.alive? + error = exception.new("execution expired") + fiber.resume(error) + end + end + + yield timer + ensure + timer.cancel if timer + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/scheduler.rb b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/scheduler.rb new file mode 100644 index 0000000..02bdc01 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/scheduler.rb @@ -0,0 +1,112 @@ +# Copyright, 2020, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'clock' + +module Async + class Scheduler + if Fiber.respond_to?(:set_scheduler) + def self.supported? + true + end + else + def self.supported? + false + end + end + + def initialize(reactor) + @reactor = reactor + end + + attr :wrappers + + def set! + Fiber.set_scheduler(self) + end + + def clear! + Fiber.set_scheduler(nil) + end + + private def from_io(io) + Wrapper.new(io, @reactor) + end + + def io_wait(io, events, timeout = nil) + wrapper = from_io(io) + + if events == ::IO::READABLE + if wrapper.wait_readable(timeout) + return ::IO::READABLE + end + elsif events == ::IO::WRITABLE + if wrapper.wait_writable(timeout) + return ::IO::WRITABLE + end + else + if wrapper.wait_any(timeout) + return events + end + end + + return false + rescue TimeoutError + return nil + ensure + wrapper&.reactor = nil + end + + # Wait for the specified process ID to exit. + # @parameter pid [Integer] The process ID to wait for. + # @parameter flags [Integer] A bit-mask of flags suitable for `Process::Status.wait`. + # @returns [Process::Status] A process status instance. + def process_wait(pid, flags) + Thread.new do + ::Process::Status.wait(pid, flags) + end.value + end + + def kernel_sleep(duration) + self.block(nil, duration) + end + + def block(blocker, timeout) + @reactor.block(blocker, timeout) + end + + def unblock(blocker, fiber) + @reactor.unblock(blocker, fiber) + end + + def close + end + + def fiber(&block) + task = Task.new(@reactor, &block) + + fiber = task.fiber + + task.run + + return fiber + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/semaphore.rb b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/semaphore.rb new file mode 100644 index 0000000..87ad49c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/semaphore.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +module Async + # A semaphore is used to control access to a common resource in a concurrent system. A useful way to think of a semaphore as used in the real-world systems is as a record of how many units of a particular resource are available, coupled with operations to adjust that record safely (i.e. to avoid race conditions) as units are required or become free, and, if necessary, wait until a unit of the resource becomes available. + class Semaphore + def initialize(limit = 1, parent: nil) + @count = 0 + @limit = limit + @waiting = [] + + @parent = parent + end + + # The current number of tasks that have acquired the semaphore. + attr :count + + # The maximum number of tasks that can acquire the semaphore. + attr :limit + + # The tasks waiting on this semaphore. + attr :waiting + + # Is the semaphore currently acquired? + def empty? + @count.zero? + end + + # Whether trying to acquire this semaphore would block. + def blocking? + @count >= @limit + end + + # Run an async task. Will wait until the semaphore is ready until spawning and running the task. + def async(*arguments, parent: (@parent or Task.current), **options) + wait + + parent.async(**options) do |task| + @count += 1 + + begin + yield task, *arguments + ensure + self.release + end + end + end + + # Acquire the semaphore, block if we are at the limit. + # If no block is provided, you must call release manually. + # @yield when the semaphore can be acquired + # @return the result of the block if invoked + def acquire + wait + + @count += 1 + + return unless block_given? + + begin + return yield + ensure + self.release + end + end + + # Release the semaphore. Must match up with a corresponding call to `acquire`. Will release waiting fibers in FIFO order. + def release + @count -= 1 + + while (@limit - @count) > 0 and fiber = @waiting.shift + if fiber.alive? + fiber.resume + end + end + end + + private + + # Wait until the semaphore becomes available. + def wait + fiber = Fiber.current + + if blocking? + @waiting << fiber + Task.yield while blocking? + end + rescue Exception + @waiting.delete(fiber) + raise + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/task.rb b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/task.rb new file mode 100644 index 0000000..9aa2267 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/task.rb @@ -0,0 +1,298 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'fiber' +require 'forwardable' + +require_relative 'node' +require_relative 'condition' + +module Async + # Raised when a task is explicitly stopped. + class Stop < Exception + class Later + def initialize(task) + @task = task + end + + def alive? + true + end + + def resume + @task.stop + end + end + end + + # A task represents the state associated with the execution of an asynchronous + # block. + class Task < Node + extend Forwardable + + # Yield the unerlying `result` for the task. If the result + # is an Exception, then that result will be raised an its + # exception. + # @return [Object] result of the task + # @raise [Exception] if the result is an exception + # @yield [result] result of the task if a block if given. + def self.yield + if block_given? + result = yield + else + result = Fiber.yield + end + + if result.is_a? Exception + raise result + else + return result + end + end + + # Create a new task. + # @param reactor [Async::Reactor] the reactor this task will run within. + # @param parent [Async::Task] the parent task. + def initialize(reactor, parent = Task.current?, logger: nil, finished: nil, **options, &block) + super(parent || reactor, **options) + + @reactor = reactor + + @status = :initialized + @result = nil + @finished = finished + + @logger = logger || @parent.logger + + @fiber = make_fiber(&block) + end + + attr :logger + + if Fiber.current.respond_to?(:backtrace) + def backtrace(*arguments) + @fiber&.backtrace(*arguments) + end + end + + def to_s + "\#<#{self.description} (#{@status})>" + end + + # @attr ios [Reactor] The reactor the task was created within. + attr :reactor + + def_delegators :@reactor, :with_timeout, :sleep + + # Yield back to the reactor and allow other fibers to execute. + def yield + Task.yield{reactor.yield} + end + + # @attr fiber [Fiber] The fiber which is being used for the execution of this task. + attr :fiber + + def alive? + @fiber&.alive? + end + + # @attr status [Symbol] The status of the execution of the fiber, one of `:initialized`, `:running`, `:complete`, `:stopped` or `:failed`. + attr :status + + # Begin the execution of the task. + def run(*arguments) + if @status == :initialized + @status = :running + + @fiber.resume(*arguments) + else + raise RuntimeError, "Task already running!" + end + end + + def async(*arguments, **options, &block) + task = Task.new(@reactor, self, **options, &block) + + task.run(*arguments) + + return task + end + + # Retrieve the current result of the task. Will cause the caller to wait until result is available. + # @raise [RuntimeError] if the task's fiber is the current fiber. + # @return [Object] the final expression/result of the task's block. + def wait + raise RuntimeError, "Cannot wait on own fiber" if Fiber.current.equal?(@fiber) + + if running? + @finished ||= Condition.new + @finished.wait + else + Task.yield{@result} + end + end + + # Deprecated. + alias result wait + # Soon to become attr :result + + # Stop the task and all of its children. + def stop(later = false) + if self.stopped? + # If we already stopped this task... don't try to stop it again: + return + end + + if self.running? + if self.current? + if later + @reactor << Stop::Later.new(self) + else + raise Stop, "Stopping current task!" + end + elsif @fiber&.alive? + begin + @fiber.resume(Stop.new) + rescue FiberError + @reactor << Stop::Later.new(self) + end + end + else + # We are not running, but children might be, so transition directly into stopped state: + stop! + end + end + + # Lookup the {Task} for the current fiber. Raise `RuntimeError` if none is available. + # @return [Async::Task] + # @raise [RuntimeError] if task was not {set!} for the current fiber. + def self.current + Thread.current[:async_task] or raise RuntimeError, "No async task available!" + end + + # Check if there is a task defined for the current fiber. + # @return [Async::Task, nil] + def self.current? + Thread.current[:async_task] + end + + def current? + self.equal?(Thread.current[:async_task]) + end + + # Check if the task is running. + # @return [Boolean] + def running? + @status == :running + end + + # Whether we can remove this node from the reactor graph. + # @return [Boolean] + def finished? + super && @status != :running + end + + def failed? + @status == :failed + end + + def stopping? + @status == :stopping + end + + def stopped? + @status == :stopped + end + + def complete? + @status == :complete + end + + private + + # This is a very tricky aspect of tasks to get right. I've modelled it after `Thread` but it's slightly different in that the exception can propagate back up through the reactor. If the user writes code which raises an exception, that exception should always be visible, i.e. cause a failure. If it's not visible, such code fails silently and can be very difficult to debug. + def fail!(exception = false, propagate = true) + @status = :failed + @result = exception + + if exception + if propagate + raise exception + elsif @finished.nil? + # If no one has called wait, we log this as a warning: + Console.logger.warn(self, "Task may have ended with unhandled exception.", exception) + else + Console.logger.debug(self, exception) + end + end + end + + def stop! + # logger.debug(self) {"Task was stopped with #{@children&.size.inspect} children!"} + @status = :stopped + + stop_children(true) + end + + def make_fiber(&block) + Fiber.new do |*arguments| + set! + + begin + @result = yield(self, *arguments) + @status = :complete + # Console.logger.debug(self) {"Task was completed with #{@children.size} children!"} + rescue Stop + stop! + rescue StandardError => error + fail!(error, false) + rescue Exception => exception + fail!(exception, true) + ensure + # Console.logger.debug(self) {"Task ensure $!=#{$!} with #{@children.size} children!"} + finish! + end + end + end + + # Finish the current task, and all bound bound IO objects. + def finish! + # Allow the fiber to be recycled. + @fiber = nil + + # Attempt to remove this node from the task tree. + consume + + # If this task was being used as a future, signal completion here: + if @finished + @finished.signal(@result) + end + end + + # Set the current fiber's `:async_task` to this task. + def set! + # This is actually fiber-local: + Thread.current[:async_task] = self + Console.logger = @logger if @logger + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/variable.rb b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/variable.rb new file mode 100644 index 0000000..95eb672 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/variable.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'condition' + +module Async + class Variable + def initialize(condition = Condition.new) + @condition = condition + @value = nil + end + + def resolve(value = true) + @value = value + condition = @condition + @condition = nil + + self.freeze + + condition.signal(value) + end + + def resolved? + @condition.nil? + end + + def value + @condition&.wait + return @value + end + + def wait + self.value + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/version.rb b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/version.rb new file mode 100644 index 0000000..7c64d43 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/version.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +module Async + VERSION = "1.30.3" +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/wrapper.rb b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/wrapper.rb new file mode 100644 index 0000000..0eeec35 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/async/wrapper.rb @@ -0,0 +1,239 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'nio' + +module Async + # Represents an asynchronous IO within a reactor. + class Wrapper + class Cancelled < StandardError + class From + def initialize + @backtrace = caller[5..-1] + end + + attr :backtrace + + def cause + nil + end + + def message + "Cancelled" + end + end + + def initialize + super "The operation has been cancelled!" + + @cause = From.new + end + + attr :cause + end + + # wait_readable, wait_writable and wait_any are not re-entrant, and will raise this failure. + class WaitError < StandardError + def initialize + super "A fiber is already waiting!" + end + end + + # @param io the native object to wrap. + # @param reactor [Reactor] the reactor that is managing this wrapper, or not specified, it's looked up by way of {Task.current}. + def initialize(io, reactor = nil) + @io = io + + @reactor = reactor + @monitor = nil + + @readable = nil + @writable = nil + @any = nil + end + + def dup + self.class.new(@io.dup, @reactor) + end + + def resume(*arguments) + # It's possible that the monitor was closed before calling resume. + return unless @monitor + + readiness = @monitor.readiness + + if @readable and (readiness == :r or readiness == :rw) + @readable.resume(*arguments) + end + + if @writable and (readiness == :w or readiness == :rw) + @writable.resume(*arguments) + end + + if @any + @any.resume(*arguments) + end + end + + # The underlying native `io`. + attr :io + + # The reactor this wrapper is associated with, if any. + attr :reactor + + # The monitor for this wrapper, if any. + attr :monitor + + # Bind this wrapper to a different reactor. Assign nil to convert to an unbound wrapper (can be used from any reactor/task but with slightly increased overhead.) + # Binding to a reactor is purely a performance consideration. Generally, I don't like APIs that exist only due to optimisations. This is borderline, so consider this functionality semi-private. + def reactor= reactor + return if @reactor.equal?(reactor) + + cancel_monitor + + @reactor = reactor + end + + # Wait for the io to become readable. + def wait_readable(timeout = nil) + raise WaitError if @readable + + self.reactor = Task.current.reactor + + begin + @readable = Fiber.current + wait_for(timeout) + ensure + @readable = nil + @monitor.interests = interests if @monitor + end + end + + # Wait for the io to become writable. + def wait_writable(timeout = nil) + raise WaitError if @writable + + self.reactor = Task.current.reactor + + begin + @writable = Fiber.current + wait_for(timeout) + ensure + @writable = nil + @monitor.interests = interests if @monitor + end + end + + # Wait fo the io to become either readable or writable. + # @param duration [Float] timeout after the given duration if not `nil`. + def wait_any(timeout = nil) + raise WaitError if @any + + self.reactor = Task.current.reactor + + begin + @any = Fiber.current + wait_for(timeout) + ensure + @any = nil + @monitor.interests = interests if @monitor + end + end + + # Close the io and monitor. + def close + cancel_monitor + + @io.close + end + + def closed? + @io.closed? + end + + private + + # What an abomination. + def interests + if @any + return :rw + elsif @readable + if @writable + return :rw + else + return :r + end + elsif @writable + return :w + end + + return nil + end + + def cancel_monitor + if @readable + readable = @readable + @readable = nil + + readable.resume(Cancelled.new) + end + + if @writable + writable = @writable + @writable = nil + + writable.resume(Cancelled.new) + end + + if @any + any = @any + @any = nil + + any.resume(Cancelled.new) + end + + if @monitor + @monitor.close + @monitor = nil + end + end + + def wait_for(timeout) + if @monitor + @monitor.interests = interests + else + @monitor = @reactor.register(@io, interests, self) + end + + # If the user requested an explicit timeout for this operation: + if timeout + @reactor.with_timeout(timeout) do + Task.yield + end + else + Task.yield + end + + return true + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/kernel/async.rb b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/kernel/async.rb new file mode 100644 index 0000000..3ba253e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/kernel/async.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative "../async/reactor" + +module Kernel + # Run the given block of code in a task, asynchronously, creating a reactor if necessary. + def Async(*arguments, **options, &block) + ::Async::Reactor.run(*arguments, **options, &block) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/kernel/sync.rb b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/kernel/sync.rb new file mode 100644 index 0000000..7203aa6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-1.30.3/lib/kernel/sync.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative "../async/reactor" + +module Kernel + # Run the given block of code synchronously, but within a reactor if not already in one. + def Sync(&block) + if task = ::Async::Task.current? + yield task + else + ::Async::Reactor.run( + finished: ::Async::Condition.new, + &block + ).wait + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/bake/async/http.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/bake/async/http.rb new file mode 100644 index 0000000..8093f97 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/bake/async/http.rb @@ -0,0 +1,91 @@ + +# Fetch the specified URL and print the response. +# @param url [String] the URL to parse and fetch. +# @param method [String] the HTTP method to use. +def fetch(url, method:) + require 'async/http/internet' + require 'kernel/sync' + + terminal = Console::Terminal.for($stdout) + terminal[:request] = terminal.style(:blue, nil, :bold) + terminal[:response] = terminal.style(:green, nil, :bold) + terminal[:length] = terminal.style(nil, nil, :bold) + + terminal[:key] = terminal.style(nil, nil, :bold) + + terminal[:chunk_0] = terminal.style(:blue) + terminal[:chunk_1] = terminal.style(:cyan) + + align = 20 + + format_body = proc do |body, terminal| + if body + if length = body.length + terminal.print(:body, "body with length ", :length, length, "B") + else + terminal.print(:body, "body without length") + end + else + terminal.print(:body, "no body") + end + end.curry + + Sync do + internet = Async::HTTP::Internet.new + + response = internet.send(method.downcase.to_sym, url) + + terminal.print_line( + :request, method.rjust(align), :reset, ": ", url + ) + + terminal.print_line( + :response, "version".rjust(align), :reset, ": ", response.version + ) + + terminal.print_line( + :response, "status".rjust(align), :reset, ": ", response.status, + ) + + terminal.print_line( + :response, "body".rjust(align), :reset, ": ", format_body[response.body], + ) + + response.headers.each do |key, value| + terminal.print_line( + :key, key.rjust(align), :reset, ": ", :value, value.inspect + ) + end + + if body = response.body + index = 0 + style = [:chunk_0, :chunk_1] + response.body.each do |chunk| + terminal.print(style[index % 2], chunk) + index += 1 + end + end + + response.finish + + if trailer = response.headers.trailer + trailer.each do |key, value| + terminal.print_line( + :key, key.rjust(align), :reset, ": ", :value, value.inspect + ) + end + end + + internet.close + end +end + +# GET the specified URL and print the response. +def get(url) + self.fetch(url, method: "GET") +end + +# HEAD the specified URL and print the response. +def head(url) + self.fetch(url, method: "HEAD") +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/bake/async/http/h2spec.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/bake/async/http/h2spec.rb new file mode 100644 index 0000000..3944ef1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/bake/async/http/h2spec.rb @@ -0,0 +1,44 @@ + +def build + # Fetch the code: + system "go get github.com/spf13/cobra" + system "go get github.com/summerwind/h2spec" + + # This builds `h2spec` into the current directory + system "go build ~/go/src/github.com/summerwind/h2spec/cmd/h2spec/h2spec.go" +end + +def test + server do + system("./h2spec", "-p", "7272") + end +end + +private + +def server + require 'async' + require 'async/container' + require 'async/http/server' + require 'async/io/host_endpoint' + + endpoint = Async::IO::Endpoint.tcp('127.0.0.1', 7272) + + container = Async::Container.new + + Console.logger.info(self){"Starting server..."} + + container.run(count: 1) do + server = Async::HTTP::Server.for(endpoint, protocol: Async::HTTP::Protocol::HTTP2, scheme: "https") do |request| + Protocol::HTTP::Response[200, {'content-type' => 'text/plain'}, ["Hello World"]] + end + + Async do + server.run + end + end + + yield if block_given? +ensure + container&.stop +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http.rb new file mode 100644 index 0000000..751173b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true +# +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'http/version' + +require_relative 'http/client' +require_relative 'http/server' + +require_relative 'http/endpoint' diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/body.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/body.rb new file mode 100644 index 0000000..deaa1f8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/body.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'protocol/http/body/buffered' +require_relative 'body/writable' + +module Async + module HTTP + module Body + include ::Protocol::HTTP::Body + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/body/delayed.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/body/delayed.rb new file mode 100644 index 0000000..ee2e78c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/body/delayed.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'protocol/http/body/wrapper' + +module Async + module HTTP + module Body + class Delayed < Protocol::HTTP::Body::Wrapper + def initialize(body, delay = 0.01) + super(body) + + @delay = delay + end + + def ready? + false + end + + def read + Async::Task.current.sleep(@delay) + + return super + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/body/hijack.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/body/hijack.rb new file mode 100644 index 0000000..443c122 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/body/hijack.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'protocol/http/body/readable' +require 'protocol/http/body/stream' + +require_relative 'writable' + +module Async + module HTTP + module Body + # A body which is designed for hijacked server responses - a response which uses a block to read and write the request and response bodies respectively. + class Hijack < ::Protocol::HTTP::Body::Readable + def self.response(request, status, headers, &block) + ::Protocol::HTTP::Response[status, headers, self.wrap(request, &block)] + end + + def self.wrap(request = nil, &block) + self.new(block, request&.body) + end + + def initialize(block, input = nil) + @block = block + @input = input + + @task = nil + @stream = nil + @output = nil + end + + # We prefer streaming directly as it's the lowest overhead. + def stream? + true + end + + def call(stream) + return @block.call(stream) + end + + attr :input + + # Has the producer called #finish and has the reader consumed the nil token? + def empty? + @output&.empty? + end + + def ready? + @output&.ready? + end + + # Read the next available chunk. + def read + unless @output + @output = Writable.new + @stream = ::Protocol::HTTP::Body::Stream.new(@input, @output) + + @task = Task.current.async do |task| + task.annotate "Streaming hijacked body." + + @block.call(@stream) + end + end + + return @output.read + end + + def inspect + "\#<#{self.class} #{@block.inspect}>" + end + + def to_s + "" + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/body/pipe.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/body/pipe.rb new file mode 100644 index 0000000..2c125c7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/body/pipe.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true +# +# Copyright, 2019, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'async/io/socket' +require 'async/io/stream' + +require_relative 'writable' + +module Async + module HTTP + module Body + class Pipe + # If the input stream is closed first, it's likely the output stream will also be closed. + def initialize(input, output = Writable.new, task: Task.current) + @input = input + @output = output + + head, tail = IO::Socket.pair(Socket::AF_UNIX, Socket::SOCK_STREAM) + + @head = IO::Stream.new(head) + @tail = tail + + @reader = nil + @writer = nil + + task.async(transient: true, &self.method(:reader)) + task.async(transient: true, &self.method(:writer)) + end + + def to_io + @tail + end + + def close + @reader&.stop + @writer&.stop + + @tail.close + end + + private + + # Read from the @input stream and write to the head of the pipe. + def reader(task) + @reader = task + + task.annotate "#{self.class} reader." + + while chunk = @input.read + @head.write(chunk) + @head.flush + end + + @head.close_write + ensure + @input.close($!) + + close_head if @writer&.finished? + end + + # Read from the head of the pipe and write to the @output stream. + # If the @tail is closed, this will cause chunk to be nil, which in turn will call `@output.close` and `@head.close` + def writer(task) + @writer = task + + task.annotate "#{self.class} writer." + + while chunk = @head.read_partial + @output.write(chunk) + end + ensure + @output.close($!) + + close_head if @reader&.finished? + end + + def close_head + @head.close + + # Both tasks are done, don't keep references: + @reader = nil + @writer = nil + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/body/slowloris.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/body/slowloris.rb new file mode 100644 index 0000000..a4f0247 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/body/slowloris.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'writable' + +require 'async/clock' + +module Async + module HTTP + module Body + # A dynamic body which you can write to and read from. + class Slowloris < Writable + class ThroughputError < StandardError + def initialize(throughput, minimum_throughput, time_since_last_write) + super("Slow write: #{throughput.round(1)}bytes/s less than required #{minimum_throughput.round}bytes/s.") + end + end + + # In order for this implementation to work correctly, you need to use a LimitedQueue. + # @param minimum_throughput [Integer] the minimum bytes per second otherwise this body will be forcefully closed. + def initialize(*arguments, minimum_throughput: 1024, **options) + super(*arguments, **options) + + @minimum_throughput = minimum_throughput + + @last_write_at = nil + @last_chunk_size = nil + end + + attr :minimum_throughput + + # If #read is called regularly to maintain throughput, that is good. If #read is not called, that is a problem. Throughput is dependent on data being available, from #write, so it doesn't seem particularly problimatic to do this check in #write. + def write(chunk) + if @last_chunk_size + time_since_last_write = Async::Clock.now - @last_write_at + throughput = @last_chunk_size / time_since_last_write + + if throughput < @minimum_throughput + error = ThroughputError.new(throughput, @minimum_throughput, time_since_last_write) + + self.close(error) + end + end + + super.tap do + @last_write_at = Async::Clock.now + @last_chunk_size = chunk&.bytesize + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/body/writable.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/body/writable.rb new file mode 100644 index 0000000..b2eedcb --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/body/writable.rb @@ -0,0 +1,123 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'protocol/http/body/readable' +require 'async/queue' + +module Async + module HTTP + module Body + include ::Protocol::HTTP::Body + + # A dynamic body which you can write to and read from. + class Writable < Readable + class Closed < StandardError + end + + # @param [Integer] length The length of the response body if known. + # @param [Async::Queue] queue Specify a different queue implementation, e.g. `Async::LimitedQueue.new(8)` to enable back-pressure streaming. + def initialize(length = nil, queue: Async::Queue.new) + @queue = queue + + @length = length + + @count = 0 + + @finished = false + + @closed = false + @error = nil + end + + def length + @length + end + + # Stop generating output; cause the next call to write to fail with the given error. + def close(error = nil) + unless @closed + @queue.enqueue(nil) + + @closed = true + @error = error + end + + super + end + + def closed? + @closed + end + + def ready? + !@queue.empty? + end + + # Has the producer called #finish and has the reader consumed the nil token? + def empty? + @finished + end + + # Read the next available chunk. + def read + return if @finished + + unless chunk = @queue.dequeue + @finished = true + end + + return chunk + end + + # Write a single chunk to the body. Signal completion by calling `#finish`. + def write(chunk) + # If the reader breaks, the writer will break. + # The inverse of this is less obvious (*) + if @closed + raise(@error || Closed) + end + + @count += 1 + @queue.enqueue(chunk) + end + + alias << write + + def inspect + "\#<#{self.class} #{@count} chunks written, #{status}>" + end + + private + + def status + if @finished + 'finished' + elsif @closed + 'closing' + else + 'waiting' + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/client.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/client.rb new file mode 100755 index 0000000..80b454d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/client.rb @@ -0,0 +1,204 @@ +# frozen_string_literal: true +# +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'async/io/endpoint' +require 'async/io/stream' + +require 'async/pool/controller' + +require 'protocol/http/body/completable' +require 'protocol/http/methods' + +require 'traces/provider' + +require_relative 'protocol' + +module Async + module HTTP + DEFAULT_RETRIES = 3 + DEFAULT_CONNECTION_LIMIT = nil + + class Client < ::Protocol::HTTP::Methods + # Provides a robust interface to a server. + # * If there are no connections, it will create one. + # * If there are already connections, it will reuse it. + # * If a request fails, it will retry it up to N times if it was idempotent. + # The client object will never become unusable. It internally manages persistent connections (or non-persistent connections if that's required). + # @param endpoint [Endpoint] the endpoint to connnect to. + # @param protocol [Protocol::HTTP1 | Protocol::HTTP2 | Protocol::HTTPS] the protocol to use. + # @param scheme [String] The default scheme to set to requests. + # @param authority [String] The default authority to set to requests. + def initialize(endpoint, protocol: endpoint.protocol, scheme: endpoint.scheme, authority: endpoint.authority, retries: DEFAULT_RETRIES, connection_limit: DEFAULT_CONNECTION_LIMIT) + @endpoint = endpoint + @protocol = protocol + + @retries = retries + @pool = make_pool(connection_limit) + + @scheme = scheme + @authority = authority + end + + attr :endpoint + attr :protocol + + attr :retries + attr :pool + + attr :scheme + attr :authority + + def secure? + @endpoint.secure? + end + + def self.open(*arguments, **options, &block) + client = self.new(*arguments, **options) + + return client unless block_given? + + begin + yield client + ensure + client.close + end + end + + def close + while @pool.busy? + Console.logger.warn(self) {"Waiting for #{@protocol} pool to drain: #{@pool}"} + @pool.wait + end + + @pool.close + end + + def call(request) + request.scheme ||= self.scheme + request.authority ||= self.authority + + attempt = 0 + + # We may retry the request if it is possible to do so. https://tools.ietf.org/html/draft-nottingham-httpbis-retry-01 is a good guide for how retrying requests should work. + begin + attempt += 1 + + # As we cache pool, it's possible these pool go bad (e.g. closed by remote host). In this case, we need to try again. It's up to the caller to impose a timeout on this. If this is the last attempt, we force a new connection. + connection = @pool.acquire + + response = make_response(request, connection) + + # This signals that the ensure block below should not try to release the connection, because it's bound into the response which will be returned: + connection = nil + + return response + rescue Protocol::RequestFailed + # This is a specific case where the entire request wasn't sent before a failure occurred. So, we can even resend non-idempotent requests. + if connection + @pool.release(connection) + connection = nil + end + + if attempt < @retries + retry + else + raise + end + rescue SocketError, IOError, EOFError, Errno::ECONNRESET, Errno::EPIPE + if connection + @pool.release(connection) + connection = nil + end + + if request.idempotent? and attempt < @retries + retry + else + raise + end + ensure + @pool.release(connection) if connection + end + end + + def inspect + "#<#{self.class} authority=#{@authority.inspect}>" + end + + Traces::Provider(self) do + def call(request) + attributes = { + 'http.method': request.method, + 'http.authority': request.authority || self.authority, + 'http.scheme': request.scheme || self.scheme, + 'http.path': request.path, + } + + if protocol = request.protocol + attributes['http.protocol'] = protocol + end + + if length = request.body&.length + attributes['http.request.length'] = length + end + + trace('async.http.client.call', attributes: attributes) do |span| + if context = self.trace_context + request.headers['traceparent'] = context.to_s + # request.headers['tracestate'] = context.state + end + + super.tap do |response| + if status = response&.status + span['http.status_code'] = status + end + + if length = response.body&.length + span['http.response.length'] = length + end + end + end + end + end + + protected + + def make_response(request, connection) + response = request.call(connection) + + # The connection won't be released until the body is completely read/released. + ::Protocol::HTTP::Body::Completable.wrap(response) do + @pool.release(connection) + end + + return response + end + + def make_pool(connection_limit) + Async::Pool::Controller.wrap(limit: connection_limit) do + Console.logger.debug(self) {"Making connection to #{@endpoint.inspect}"} + + @protocol.client(@endpoint.connect) + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/endpoint.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/endpoint.rb new file mode 100644 index 0000000..0f5e698 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/endpoint.rb @@ -0,0 +1,244 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'async/io/host_endpoint' +require 'async/io/ssl_endpoint' +require 'async/io/ssl_socket' + +require_relative 'protocol/http1' +require_relative 'protocol/https' + +module Async + module HTTP + # Represents a way to connect to a remote HTTP server. + class Endpoint < Async::IO::Endpoint + def self.parse(string, endpoint = nil, **options) + url = URI.parse(string).normalize + + return self.new(url, endpoint, **options) + end + + # Construct an endpoint with a specified scheme, hostname, optional path, and options. + def self.for(scheme, hostname, path = "/", **options) + # TODO: Consider using URI.for once it becomes available: + uri_klass = URI.scheme_list[scheme.upcase] || URI::HTTP + + self.new( + uri_klass.new(scheme, nil, hostname, nil, nil, path, nil, nil, nil).normalize, + **options + ) + end + + # @option scheme [String] the scheme to use, overrides the URL scheme. + # @option hostname [String] the hostname to connect to (or bind to), overrides the URL hostname (used for SNI). + # @option port [Integer] the port to bind to, overrides the URL port. + # @option ssl_context [OpenSSL::SSL::SSLContext] the context to use for TLS. + # @option alpn_protocols [Array] the alpn protocols to negotiate. + def initialize(url, endpoint = nil, **options) + super(**options) + + raise ArgumentError, "URL must be absolute (include scheme, host): #{url}" unless url.absolute? + + @url = url + + if endpoint + @endpoint = self.build_endpoint(endpoint) + else + @endpoint = nil + end + end + + def to_url + url = @url.dup + + unless default_port? + url.port = self.port + end + + return url + end + + def to_s + "\#<#{self.class} #{self.to_url} #{@options}>" + end + + def inspect + "\#<#{self.class} #{self.to_url} #{@options.inspect}>" + end + + attr :url + + def address + endpoint.address + end + + def secure? + ['https', 'wss'].include?(self.scheme) + end + + def protocol + @options.fetch(:protocol) do + if secure? + Protocol::HTTPS + else + Protocol::HTTP1 + end + end + end + + def default_port + secure? ? 443 : 80 + end + + def default_port? + port == default_port + end + + def port + @options[:port] || @url.port || default_port + end + + # The hostname is the server we are connecting to: + def hostname + @options[:hostname] || @url.hostname + end + + def scheme + @options[:scheme] || @url.scheme + end + + def authority(ignore_default_port = true) + if ignore_default_port and default_port? + @url.hostname + else + "#{@url.hostname}:#{port}" + end + end + + # Return the path and query components of the given URL. + def path + buffer = @url.path || "/" + + if query = @url.query + buffer = "#{buffer}?#{query}" + end + + return buffer + end + + def alpn_protocols + @options.fetch(:alpn_protocols) {self.protocol.names} + end + + def localhost? + @url.hostname =~ /^(.*?\.)?localhost\.?$/ + end + + # We don't try to validate peer certificates when talking to localhost because they would always be self-signed. + def ssl_verify_mode + if self.localhost? + OpenSSL::SSL::VERIFY_NONE + else + OpenSSL::SSL::VERIFY_PEER + end + end + + def ssl_context + @options[:ssl_context] || OpenSSL::SSL::SSLContext.new.tap do |context| + if alpn_protocols = self.alpn_protocols + context.alpn_protocols = alpn_protocols + end + + context.set_params( + verify_mode: self.ssl_verify_mode + ) + end + end + + def build_endpoint(endpoint = nil) + endpoint ||= tcp_endpoint + + if secure? + # Wrap it in SSL: + return Async::IO::SSLEndpoint.new(endpoint, + ssl_context: self.ssl_context, + hostname: @url.hostname, + timeout: self.timeout, + ) + end + + return endpoint + end + + def endpoint + @endpoint ||= build_endpoint + end + + def bind(*arguments, &block) + endpoint.bind(*arguments, &block) + end + + def connect(&block) + endpoint.connect(&block) + end + + def each + return to_enum unless block_given? + + self.tcp_endpoint.each do |endpoint| + yield self.class.new(@url, endpoint, **@options) + end + end + + def key + [@url, @options] + end + + def eql? other + self.key.eql? other.key + end + + def hash + self.key.hash + end + + protected + + def tcp_options + options = @options.dup + + options.delete(:scheme) + options.delete(:port) + options.delete(:hostname) + options.delete(:ssl_context) + options.delete(:alpn_protocols) + options.delete(:protocol) + + return options + end + + def tcp_endpoint + Async::IO::Endpoint.tcp(self.hostname, port, **tcp_options) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/internet.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/internet.rb new file mode 100644 index 0000000..c07ae9b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/internet.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'client' +require_relative 'endpoint' + +require 'protocol/http/middleware' +require 'protocol/http/body/buffered' +require 'protocol/http/accept_encoding' + +module Async + module HTTP + class Internet + def initialize(**options) + @clients = Hash.new + @options = options + end + + # A cache of clients. + # @attribute [Hash(URI, Client)] + attr :clients + + def client_for(endpoint) + key = host_key(endpoint) + + @clients.fetch(key) do + @clients[key] = self.make_client(endpoint) + end + end + + # Make a request to the internet with the given `method` and `url`. + # + # If you provide non-frozen headers, they may be mutated. + # + # @parameter method [String] The request method, e.g. `GET`. + # @parameter url [String] The URL to request, e.g. `https://www.codeotaku.com`. + # @parameter headers [Hash | Protocol::HTTP::Headers] The headers to send with the request. + # @parameter body [String | Protocol::HTTP::Body] The body to send with the request. + def call(method, url, headers = nil, body = nil) + endpoint = Endpoint.parse(url) + client = self.client_for(endpoint) + + body = Body::Buffered.wrap(body) + headers = ::Protocol::HTTP::Headers[headers] + + request = ::Protocol::HTTP::Request.new(endpoint.scheme, endpoint.authority, method, endpoint.path, nil, headers, body) + + return client.call(request) + end + + def close + # The order of operations here is to avoid a race condition between iterating over clients (#close may yield) and creating new clients. + clients = @clients.values + @clients.clear + + clients.each(&:close) + end + + ::Protocol::HTTP::Methods.each do |name, verb| + define_method(verb.downcase) do |url, headers = nil, body = nil| + self.call(verb, url.to_str, headers, body) + end + end + + protected + + def make_client(endpoint) + ::Protocol::HTTP::AcceptEncoding.new( + Client.new(endpoint, **@options) + ) + end + + def host_key(endpoint) + url = endpoint.url.dup + + url.path = "" + url.fragment = nil + url.query = nil + + return url + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/internet/instance.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/internet/instance.rb new file mode 100644 index 0000000..afae5d9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/internet/instance.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative '../internet' +require 'thread/local' + +module Async + module HTTP + class Internet + # Provide access to a shared thread-local instance. + extend ::Thread::Local + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol.rb new file mode 100644 index 0000000..90a5f4e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true +# +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'protocol/http1' +require_relative 'protocol/https' + +module Async + module HTTP + # A protocol specifies a way in which to communicate with a remote peer. + module Protocol + # A protocol must implement the following interface: + # class Protocol + # def client(stream) -> Connection + # def server(stream) -> Connection + # end + + # A connection must implement the following interface: + # class Connection + # def concurrency -> can invoke call 1 or more times simultaneously. + # def reusable? -> can be used again/persistent connection. + + # def viable? -> Boolean + + # def call(request) -> Response + # def each -> (yield(request) -> Response) + # end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http1.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http1.rb new file mode 100644 index 0000000..016c445 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http1.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true +# +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'http1/client' +require_relative 'http1/server' + +module Async + module HTTP + module Protocol + module HTTP1 + VERSION = "HTTP/1.1" + + def self.bidirectional? + true + end + + def self.trailer? + true + end + + def self.client(peer) + stream = IO::Stream.new(peer, sync: true) + + return HTTP1::Client.new(stream, VERSION) + end + + def self.server(peer) + stream = IO::Stream.new(peer, sync: true) + + return HTTP1::Server.new(stream, VERSION) + end + + def self.names + ["http/1.1", "http/1.0"] + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http1/client.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http1/client.rb new file mode 100644 index 0000000..fdeb424 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http1/client.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true +# +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'connection' + +module Async + module HTTP + module Protocol + module HTTP1 + class Client < Connection + # Used by the client to send requests to the remote server. + def call(request, task: Task.current) + # We need to keep track of connections which are not in the initial "ready" state. + @ready = false + + Console.logger.debug(self) {"#{request.method} #{request.path} #{request.headers.inspect}"} + + # Mark the start of the trailers: + trailer = request.headers.trailer! + + # We carefully interpret https://tools.ietf.org/html/rfc7230#section-6.3.1 to implement this correctly. + begin + write_request(request.authority, request.method, request.path, @version, request.headers) + rescue + # If we fail to fully write the request and body, we can retry this request. + raise RequestFailed + end + + if request.body? + body = request.body + + if protocol = request.protocol + # This is a very tricky apect of handling HTTP/1 upgrade connections. In theory, this approach is a bit inefficient, because we spin up a task just to handle writing to the underlying stream when we could be writing to the stream directly. But we need to maintain some level of compatibility with HTTP/2. Additionally, we don't know if the upgrade request will be accepted, so starting to write the body at this point needs to be handled with care. + task.async do |subtask| + subtask.annotate("Upgrading request.") + + # If this fails, this connection will be closed. + write_upgrade_body(protocol, body) + end + elsif request.connect? + task.async do |subtask| + subtask.annotate("Tunnelling body.") + + write_tunnel_body(@version, body) + end + else + task.async do |subtask| + subtask.annotate("Streaming body.") + + # Once we start writing the body, we can't recover if the request fails. That's because the body might be generated dynamically, streaming, etc. + write_body(@version, body, false, trailer) + end + end + elsif protocol = request.protocol + write_upgrade_body(protocol) + else + write_body(@version, body, false, trailer) + end + + response = Response.read(self, request) + @ready = true + + return response + rescue + # This will ensure that #reusable? returns false. + @stream.close + + raise + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http1/connection.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http1/connection.rb new file mode 100755 index 0000000..fd4edea --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http1/connection.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true +# +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'protocol/http1' + +require_relative 'request' +require_relative 'response' + +module Async + module HTTP + module Protocol + module HTTP1 + class Connection < ::Protocol::HTTP1::Connection + def initialize(stream, version) + super(stream) + + @ready = true + @version = version + end + + attr :version + + def http1? + true + end + + def http2? + false + end + + def read_line? + @stream.read_until(CRLF) + end + + def read_line + @stream.read_until(CRLF) or raise EOFError, "Could not read line!" + end + + def peer + @stream.io + end + + attr :count + + def concurrency + 1 + end + + # Can we use this connection to make requests? + def viable? + @ready && @stream&.connected? + end + + def reusable? + @ready && @persistent && @stream && !@stream.closed? + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http1/request.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http1/request.rb new file mode 100644 index 0000000..0c9b08b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http1/request.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative '../request' + +module Async + module HTTP + module Protocol + module HTTP1 + class Request < Protocol::Request + def self.read(connection) + if parts = connection.read_request + self.new(connection, *parts) + end + end + + UPGRADE = 'upgrade' + + def initialize(connection, authority, method, path, version, headers, body) + @connection = connection + + # HTTP/1 requests with an upgrade header (which can contain zero or more values) are extracted into the protocol field of the request, and we expect a response to select one of those protocols with a status code of 101 Switching Protocols. + protocol = headers.delete('upgrade') + + super(nil, authority, method, path, version, headers, body, protocol) + end + + def connection + @connection + end + + def hijack? + true + end + + def hijack! + @connection.hijack! + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http1/response.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http1/response.rb new file mode 100644 index 0000000..ede7eca --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http1/response.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative '../response' + +module Async + module HTTP + module Protocol + module HTTP1 + class Response < Protocol::Response + def self.read(connection, request) + if parts = connection.read_response(request.method) + self.new(connection, *parts) + end + end + + UPGRADE = 'upgrade' + + # @param reason [String] HTTP response line reason, ignored. + def initialize(connection, version, status, reason, headers, body) + @connection = connection + + protocol = headers.delete(UPGRADE) + + super(version, status, headers, body, protocol) + end + + def connection + @connection + end + + def hijack? + @body.nil? + end + + def hijack! + @connection.hijack! + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http1/server.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http1/server.rb new file mode 100644 index 0000000..3c2ac1b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http1/server.rb @@ -0,0 +1,128 @@ +# frozen_string_literal: true +# +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'connection' + +module Async + module HTTP + module Protocol + module HTTP1 + class Server < Connection + def fail_request(status) + @persistent = false + write_response(@version, status, {}, nil) + end + + def next_request + # The default is true. + return unless @persistent + + # Read an incoming request: + return unless request = Request.read(self) + + unless persistent?(request.version, request.method, request.headers) + @persistent = false + end + + return request + rescue Async::TimeoutError + # For an interesting discussion about this behaviour, see https://trac.nginx.org/nginx/ticket/1005 + # If you enable this, you will see some spec failures... + # fail_request(408) + raise + rescue + fail_request(400) + raise + end + + # Server loop. + def each(task: Task.current) + task.annotate("Reading #{self.version} requests for #{self.class}.") + + while request = next_request + response = yield(request, self) + body = response&.body + + if @stream.nil? and body.nil? + # Full hijack. + return + end + + begin + # If a response was generated, send it: + if response + trailer = response.headers.trailer! + + write_response(@version, response.status, response.headers) + + # Some operations in this method are long running, that is, it's expected that `body.call(stream)` could literally run indefinitely. In order to facilitate garbage collection, we want to nullify as many local variables before calling the streaming body. This ensures that the garbage collection can clean up as much state as possible during the long running operation, so we don't retain objects that are no longer needed. + + if body and protocol = response.protocol + stream = write_upgrade_body(protocol) + + # At this point, the request body is hijacked, so we don't want to call #finish below. + request = response = nil + + body.call(stream) + elsif request.connect? and response.success? + stream = write_tunnel_body(request.version) + + # Same as above: + request = response = nil + + body.call(stream) + else + head = request.head? + version = request.version + + # Same as above: + request = nil unless body + response = nil + + write_body(version, body, head, trailer) + end + + # We are done with the body, you shouldn't need to call close on it: + body = nil + else + # If the request failed to generate a response, it was an internal server error: + write_response(@version, 500, {}) + write_body(request.version, nil) + end + + # Gracefully finish reading the request body if it was not already done so. + request&.finish + + # This ensures we yield at least once every iteration of the loop and allow other fibers to execute. + task.yield + rescue => error + raise + ensure + body&.close(error) + end + end + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http10.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http10.rb new file mode 100755 index 0000000..285b4e3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http10.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'http1' + +module Async + module HTTP + module Protocol + module HTTP10 + VERSION = "HTTP/1.0" + + def self.bidirectional? + false + end + + def self.trailer? + false + end + + def self.client(peer) + stream = IO::Stream.new(peer, sync: true) + + return HTTP1::Client.new(stream, VERSION) + end + + def self.server(peer) + stream = IO::Stream.new(peer, sync: true) + + return HTTP1::Server.new(stream, VERSION) + end + + def self.names + ["http/1.0"] + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http11.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http11.rb new file mode 100644 index 0000000..aa45b2e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http11.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'http1' + +module Async + module HTTP + module Protocol + module HTTP11 + VERSION = "HTTP/1.1" + + def self.bidirectional? + true + end + + def self.trailer? + true + end + + def self.client(peer) + stream = IO::Stream.new(peer, sync: true) + + return HTTP1::Client.new(stream, VERSION) + end + + def self.server(peer) + stream = IO::Stream.new(peer, sync: true) + + return HTTP1::Server.new(stream, VERSION) + end + + def self.names + ["http/1.1"] + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2.rb new file mode 100644 index 0000000..124b590 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'http2/client' +require_relative 'http2/server' + +module Async + module HTTP + module Protocol + module HTTP2 + VERSION = "HTTP/2" + + def self.bidirectional? + true + end + + def self.trailer? + true + end + + CLIENT_SETTINGS = { + ::Protocol::HTTP2::Settings::ENABLE_PUSH => 0, + ::Protocol::HTTP2::Settings::MAXIMUM_FRAME_SIZE => 0x100000, + ::Protocol::HTTP2::Settings::INITIAL_WINDOW_SIZE => 0x800000, + } + + SERVER_SETTINGS = { + # We choose a lower maximum concurrent streams to avoid overloading a single connection/thread. + ::Protocol::HTTP2::Settings::MAXIMUM_CONCURRENT_STREAMS => 128, + ::Protocol::HTTP2::Settings::MAXIMUM_FRAME_SIZE => 0x100000, + ::Protocol::HTTP2::Settings::INITIAL_WINDOW_SIZE => 0x800000, + ::Protocol::HTTP2::Settings::ENABLE_CONNECT_PROTOCOL => 1, + } + + def self.client(peer, settings = CLIENT_SETTINGS) + stream = IO::Stream.new(peer, sync: true) + + client = Client.new(stream) + + client.send_connection_preface(settings) + client.start_connection + + return client + end + + def self.server(peer, settings = SERVER_SETTINGS) + stream = IO::Stream.new(peer, sync: true) + + server = Server.new(stream) + + server.read_connection_preface(settings) + server.start_connection + + return server + end + + def self.names + ["h2"] + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/client.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/client.rb new file mode 100644 index 0000000..9b26d0a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/client.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'connection' +require_relative 'response' + +require 'protocol/http2/client' + +module Async + module HTTP + module Protocol + module HTTP2 + class Client < ::Protocol::HTTP2::Client + include Connection + + def initialize(stream) + @stream = stream + + framer = ::Protocol::HTTP2::Framer.new(@stream) + + super(framer) + end + + def create_response + Response::Stream.create(self, self.next_stream_id).response + end + + # Used by the client to send requests to the remote server. + def call(request) + raise ::Protocol::HTTP2::Error, "Connection closed!" if self.closed? + + @count += 1 + + response = create_response + response.send_request(request) + response.wait + + return response + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/connection.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/connection.rb new file mode 100644 index 0000000..fa1378d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/connection.rb @@ -0,0 +1,154 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'stream' + +require 'async/semaphore' + +module Async + module HTTP + module Protocol + module HTTP2 + HTTPS = 'https'.freeze + SCHEME = ':scheme'.freeze + METHOD = ':method'.freeze + PATH = ':path'.freeze + AUTHORITY = ':authority'.freeze + STATUS = ':status'.freeze + PROTOCOL = ':protocol'.freeze + + CONTENT_LENGTH = 'content-length'.freeze + CONNECTION = 'connection'.freeze + TRAILER = 'trailer'.freeze + + module Connection + def initialize(*) + super + + @count = 0 + @reader = nil + + # Writing multiple frames at the same time can cause odd problems if frames are only partially written. So we use a semaphore to ensure frames are written in their entirety. + @write_frame_guard = Async::Semaphore.new(1) + end + + def to_s + "\#<#{self.class} #{@streams.count} active streams>" + end + + attr :stream + + def http1? + false + end + + def http2? + true + end + + def start_connection + @reader || read_in_background + end + + def close(error = nil) + @reader = nil + + super + end + + def write_frame(frame) + # We don't want to write multiple frames at the same time. + @write_frame_guard.acquire do + super + end + + @stream.flush + end + + def write_frames(&block) + @write_frame_guard.acquire do + super + end + + @stream.flush + end + + def read_in_background(parent: Task.current) + raise RuntimeError, "Connection is closed!" if closed? + + parent.async(transient: true) do |task| + @reader = task + + task.annotate("#{version} reading data for #{self.class}.") + + begin + while !self.closed? + self.consume_window + self.read_frame + end + rescue SocketError, IOError, EOFError, Errno::ECONNRESET, Errno::EPIPE, Async::Wrapper::Cancelled + # Ignore. + rescue ::Protocol::HTTP2::GoawayError => error + # Error is raised if a response is actively reading from the + # connection. The connection is silently closed if GOAWAY is + # received outside the request/response cycle. + if @reader + self.close(error) + end + ensure + # Don't call #close twice. + if @reader + self.close($!) + end + end + end + end + + attr :promises + + def peer + @stream.io + end + + attr :count + + def concurrency + self.maximum_concurrent_streams + end + + # Can we use this connection to make requests? + def viable? + @stream.connected? + end + + def reusable? + !self.closed? + end + + def version + VERSION + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/input.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/input.rb new file mode 100644 index 0000000..b47ad3a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/input.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative '../../body/writable' + +module Async + module HTTP + module Protocol + module HTTP2 + # A writable body which requests window updates when data is read from it. + class Input < Body::Writable + def initialize(stream, length) + super(length) + + @stream = stream + @remaining = length + end + + def read + if chunk = super + # If we read a chunk fron the stream, we want to extend the window if required so more data will be provided. + @stream.request_window_update + end + + # We track the expected length and check we got what we were expecting. + if @remaining + if chunk + @remaining -= chunk.bytesize + elsif @remaining > 0 + raise EOFError, "Expected #{self.length} bytes, #{@remaining} bytes short!" + elsif @remaining < 0 + raise EOFError, "Expected #{self.length} bytes, #{@remaining} bytes over!" + end + end + + return chunk + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/output.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/output.rb new file mode 100644 index 0000000..611b360 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/output.rb @@ -0,0 +1,133 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'protocol/http/body/stream' + +module Async + module HTTP + module Protocol + module HTTP2 + class Output + def initialize(stream, body, trailer = nil) + @stream = stream + @body = body + @trailer = trailer + + @task = nil + + @window_updated = Async::Condition.new + end + + attr :trailer + + def start(parent: Task.current) + raise "Task already started!" if @task + + if @body.stream? + @task = parent.async(&self.method(:stream)) + else + @task = parent.async(&self.method(:passthrough)) + end + end + + def window_updated(size) + @window_updated.signal + end + + def write(chunk) + until chunk.empty? + maximum_size = @stream.available_frame_size + + while maximum_size <= 0 + @window_updated.wait + + maximum_size = @stream.available_frame_size + end + + break unless chunk = send_data(chunk, maximum_size) + end + end + + # This method should only be called from within the context of the output task. + def close(error = nil) + if @stream + @stream.finish_output(error) + @stream = nil + end + end + + # This method should only be called from within the context of the HTTP/2 stream. + def stop(error) + @task&.stop + @task = nil + end + + private + + def stream(task) + task.annotate("Streaming #{@body} to #{@stream}.") + + input = @stream.wait_for_input + + @body.call(::Protocol::HTTP::Body::Stream.new(input, self)) + rescue Async::Stop + # Ignore. + end + + # Reads chunks from the given body and writes them to the stream as fast as possible. + def passthrough(task) + task.annotate("Writing #{@body} to #{@stream}.") + + while chunk = @body&.read + self.write(chunk) + # TODO this reduces memory usage? + # chunk.clear unless chunk.frozen? + # GC.start + end + + self.close + ensure + @body&.close($!) + @body = nil + end + + # Send `maximum_size` bytes of data using the specified `stream`. If the buffer has no more chunks, `END_STREAM` will be sent on the final chunk. + # @param maximum_size [Integer] send up to this many bytes of data. + # @param stream [Stream] the stream to use for sending data frames. + # @return [String, nil] any data that could not be written. + def send_data(chunk, maximum_size) + if chunk.bytesize <= maximum_size + @stream.send_data(chunk, maximum_size: maximum_size) + else + @stream.send_data(chunk.byteslice(0, maximum_size), maximum_size: maximum_size) + + # The window was not big enough to send all the data, so we save it for next time: + return chunk.byteslice(maximum_size, chunk.bytesize - maximum_size) + end + + return nil + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/request.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/request.rb new file mode 100644 index 0000000..d519fbd --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/request.rb @@ -0,0 +1,165 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative '../request' +require_relative 'stream' + +module Async + module HTTP + module Protocol + module HTTP2 + # Typically used on the server side to represent an incoming request, and write the response. + class Request < Protocol::Request + class Stream < HTTP2::Stream + def initialize(*) + super + + @enqueued = false + @request = Request.new(self) + end + + attr :request + + def receive_initial_headers(headers, end_stream) + headers.each do |key, value| + if key == SCHEME + raise ::Protocol::HTTP2::HeaderError, "Request scheme already specified!" if @request.scheme + + @request.scheme = value + elsif key == AUTHORITY + raise ::Protocol::HTTP2::HeaderError, "Request authority already specified!" if @request.authority + + @request.authority = value + elsif key == METHOD + raise ::Protocol::HTTP2::HeaderError, "Request method already specified!" if @request.method + + @request.method = value + elsif key == PATH + raise ::Protocol::HTTP2::HeaderError, "Request path is empty!" if value.empty? + raise ::Protocol::HTTP2::HeaderError, "Request path already specified!" if @request.path + + @request.path = value + elsif key == PROTOCOL + raise ::Protocol::HTTP2::HeaderError, "Request protocol already specified!" if @request.protocol + + @request.protocol = value + elsif key == CONTENT_LENGTH + raise ::Protocol::HTTP2::HeaderError, "Request content length already specified!" if @length + + @length = Integer(value) + elsif key == CONNECTION + raise ::Protocol::HTTP2::HeaderError, "Connection header is not allowed!" + elsif key.start_with? ':' + raise ::Protocol::HTTP2::HeaderError, "Invalid pseudo-header #{key}!" + elsif key =~ /[A-Z]/ + raise ::Protocol::HTTP2::HeaderError, "Invalid characters in header #{key}!" + else + add_header(key, value) + end + end + + @request.headers = @headers + + unless @request.valid? + raise ::Protocol::HTTP2::HeaderError, "Request is missing required headers!" + else + # We only construct the input/body if data is coming. + unless end_stream + @request.body = prepare_input(@length) + end + + # We are ready for processing: + @connection.requests.enqueue(@request) + end + + return headers + end + + def closed(error) + @request = nil + + super + end + end + + def initialize(stream) + super(nil, nil, nil, nil, VERSION, nil) + + @stream = stream + end + + attr :stream + + def connection + @stream.connection + end + + def valid? + @scheme and @method and @path + end + + def hijack? + false + end + + NO_RESPONSE = [ + [STATUS, '500'], + ] + + def send_response(response) + if response.nil? + return @stream.send_headers(nil, NO_RESPONSE, ::Protocol::HTTP2::END_STREAM) + end + + protocol_headers = [ + [STATUS, response.status], + ] + + if protocol = response.protocol + protocol_headers << [PROTOCOL, protocol] + end + + if length = response.body&.length + protocol_headers << [CONTENT_LENGTH, length] + end + + headers = ::Protocol::HTTP::Headers::Merged.new(protocol_headers, response.headers) + + if body = response.body and !self.head? + # This function informs the headers object that any subsequent headers are going to be trailer. Therefore, it must be called *before* sending the headers, to avoid any race conditions. + trailer = response.headers.trailer! + + @stream.send_headers(nil, headers) + + @stream.send_body(body, trailer) + else + # Ensure the response body is closed if we are ending the stream: + response.close + + @stream.send_headers(nil, headers, ::Protocol::HTTP2::END_STREAM) + end + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/response.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/response.rb new file mode 100644 index 0000000..786992a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/response.rb @@ -0,0 +1,227 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative '../response' +require_relative 'stream' + +module Async + module HTTP + module Protocol + module HTTP2 + # Typically used on the client side for writing a request and reading the incoming response. + class Response < Protocol::Response + class Stream < HTTP2::Stream + def initialize(*) + super + + @response = Response.new(self) + + @notification = Async::Notification.new + @exception = nil + end + + attr :response + + def wait_for_input + # The input isn't ready until the response headers have been received: + @response.wait + + # There is a possible race condition if you try to access @input - it might already be closed and nil. + return @response.body + end + + def accept_push_promise_stream(promised_stream_id, headers) + raise ProtocolError, "Cannot accept push promise stream!" + end + + # This should be invoked from the background reader, and notifies the task waiting for the headers that we are done. + def receive_initial_headers(headers, end_stream) + headers.each do |key, value| + if key == STATUS + @response.status = Integer(value) + elsif key == PROTOCOL + @response.protocol = value + elsif key == CONTENT_LENGTH + @length = Integer(value) + else + add_header(key, value) + end + end + + @response.headers = @headers + + if @response.valid? + if !end_stream + # We only construct the input/body if data is coming. + @response.body = prepare_input(@length) + elsif @response.head? + @response.body = ::Protocol::HTTP::Body::Head.new(@length) + end + else + send_reset_stream(::Protocol::HTTP2::Error::PROTOCOL_ERROR) + end + + self.notify! + + return headers + end + + # Notify anyone waiting on the response headers to be received (or failure). + def notify! + if notification = @notification + @notification = nil + notification.signal + end + end + + # Wait for the headers to be received or for stream reset. + def wait + # If you call wait after the headers were already received, it should return immediately: + @notification&.wait + + if @exception + raise @exception + end + end + + def closed(error) + super + + if @response + @response = nil + end + + @exception = error + + notify! + end + end + + def initialize(stream) + super(stream.connection.version, nil, nil) + + @stream = stream + @request = nil + end + + attr :stream + attr :request + + def connection + @stream.connection + end + + def wait + @stream.wait + end + + def head? + @request&.head? + end + + def valid? + !!@status + end + + def build_request(headers) + request = ::Protocol::HTTP::Request.new + request.headers = ::Protocol::HTTP::Headers.new + + headers.each do |key, value| + if key == SCHEME + raise ::Protocol::HTTP2::HeaderError, "Request scheme already specified!" if request.scheme + + request.scheme = value + elsif key == AUTHORITY + raise ::Protocol::HTTP2::HeaderError, "Request authority already specified!" if request.authority + + request.authority = value + elsif key == METHOD + raise ::Protocol::HTTP2::HeaderError, "Request method already specified!" if request.method + + request.method = value + elsif key == PATH + raise ::Protocol::HTTP2::HeaderError, "Request path is empty!" if value.empty? + raise ::Protocol::HTTP2::HeaderError, "Request path already specified!" if request.path + + request.path = value + elsif key.start_with? ':' + raise ::Protocol::HTTP2::HeaderError, "Invalid pseudo-header #{key}!" + else + request.headers[key] = value + end + end + + @request = request + end + + # Send a request and read it into this response. + def send_request(request) + @request = request + + # https://http2.github.io/http2-spec/#rfc.section.8.1.2.3 + # All HTTP/2 requests MUST include exactly one valid value for the :method, :scheme, and :path pseudo-header fields, unless it is a CONNECT request (Section 8.3). An HTTP request that omits mandatory pseudo-header fields is malformed (Section 8.1.2.6). + pseudo_headers = [ + [SCHEME, request.scheme], + [METHOD, request.method], + [PATH, request.path], + ] + + # To ensure that the HTTP/1.1 request line can be reproduced accurately, this pseudo-header field MUST be omitted when translating from an HTTP/1.1 request that has a request target in origin or asterisk form (see [RFC7230], Section 5.3). Clients that generate HTTP/2 requests directly SHOULD use the :authority pseudo-header field instead of the Host header field. + if authority = request.authority + pseudo_headers << [AUTHORITY, authority] + end + + if protocol = request.protocol + pseudo_headers << [PROTOCOL, protocol] + end + + headers = ::Protocol::HTTP::Headers::Merged.new( + pseudo_headers, + request.headers + ) + + if request.body.nil? + @stream.send_headers(nil, headers, ::Protocol::HTTP2::END_STREAM) + else + if length = request.body.length + # This puts it at the end of the pseudo-headers: + pseudo_headers << [CONTENT_LENGTH, length] + end + + # This function informs the headers object that any subsequent headers are going to be trailer. Therefore, it must be called *before* sending the headers, to avoid any race conditions. + trailer = request.headers.trailer! + + begin + @stream.send_headers(nil, headers) + rescue + raise RequestFailed + end + + @stream.send_body(request.body, trailer) + end + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/server.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/server.rb new file mode 100644 index 0000000..3db051f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/server.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'connection' +require_relative 'request' + +require 'protocol/http2/server' + +module Async + module HTTP + module Protocol + module HTTP2 + class Server < ::Protocol::HTTP2::Server + include Connection + + def initialize(stream) + # Used by some generic methods in Connetion: + @stream = stream + + framer = ::Protocol::HTTP2::Framer.new(stream) + + super(framer) + + @requests = Async::Queue.new + end + + attr :requests + + def accept_stream(stream_id) + super do + Request::Stream.create(self, stream_id) + end + end + + def close(error = nil) + if @requests + # Stop the request loop: + @requests.enqueue(nil) + @requests = nil + end + + super + end + + def each(task: Task.current) + task.annotate("Reading #{version} requests for #{self.class}.") + + # It's possible the connection has died before we get here... + @requests&.async do |task, request| + task.annotate("Incoming request: #{request.method} #{request.path.inspect}.") + + @count += 1 + + begin + response = yield(request) + rescue + # We need to close the stream if the user code blows up while generating a response: + request.stream.send_reset_stream(::Protocol::HTTP2::INTERNAL_ERROR) + + raise + else + request.send_response(response) + end + end + + # Maybe we should add some synchronisation here - i.e. only exit once all requests are finished. + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/stream.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/stream.rb new file mode 100644 index 0000000..0520241 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/http2/stream.rb @@ -0,0 +1,185 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'protocol/http2/stream' + +require_relative 'input' +require_relative 'output' + +module Async + module HTTP + module Protocol + module HTTP2 + class Stream < ::Protocol::HTTP2::Stream + def initialize(*) + super + + @headers = nil + + # Input buffer, reading request body, or response body (receive_data): + @length = nil + @input = nil + + # Output buffer, writing request body or response body (window_updated): + @output = nil + end + + attr_accessor :headers + + attr :input + + def add_header(key, value) + if key == CONNECTION + raise ::Protocol::HTTP2::HeaderError, "Connection header is not allowed!" + elsif key.start_with? ':' + raise ::Protocol::HTTP2::HeaderError, "Invalid pseudo-header #{key}!" + elsif key =~ /[A-Z]/ + raise ::Protocol::HTTP2::HeaderError, "Invalid upper-case characters in header #{key}!" + else + @headers.add(key, value) + end + end + + def receive_trailing_headers(headers, end_stream) + headers.each do |key, value| + add_header(key, value) + end + end + + def process_headers(frame) + if @headers.nil? + @headers = ::Protocol::HTTP::Headers.new + self.receive_initial_headers(super, frame.end_stream?) + elsif frame.end_stream? + self.receive_trailing_headers(super, frame.end_stream?) + else + raise ::Protocol::HTTP2::HeaderError, "Unable to process headers!" + end + + # TODO this might need to be in an ensure block: + if @input and frame.end_stream? + @input.close($!) + @input = nil + end + rescue ::Protocol::HTTP2::HeaderError => error + Console.logger.error(self, error) + + send_reset_stream(error.code) + end + + def wait_for_input + return @input + end + + # Prepare the input stream which will be used for incoming data frames. + # @return [Input] the input body. + def prepare_input(length) + if @input.nil? + @input = Input.new(self, length) + else + raise ArgumentError, "Input body already prepared!" + end + end + + def update_local_window(frame) + consume_local_window(frame) + + # This is done on demand in `Input#read`: + # request_window_update + end + + def process_data(frame) + data = frame.unpack + + if @input + unless data.empty? + @input.write(data) + end + + if frame.end_stream? + @input.close + @input = nil + end + end + + return data + rescue ::Protocol::HTTP2::ProtocolError + raise + rescue # Anything else... + send_reset_stream(::Protocol::HTTP2::Error::INTERNAL_ERROR) + end + + # Set the body and begin sending it. + def send_body(body, trailer = nil) + @output = Output.new(self, body, trailer) + + @output.start + end + + # Called when the output terminates normally. + def finish_output(error = nil) + trailer = @output&.trailer + + @output = nil + + if error + send_reset_stream(::Protocol::HTTP2::Error::INTERNAL_ERROR) + else + # Write trailer? + if trailer&.any? + send_headers(nil, trailer, ::Protocol::HTTP2::END_STREAM) + else + send_data(nil, ::Protocol::HTTP2::END_STREAM) + end + end + end + + def window_updated(size) + super + + @output&.window_updated(size) + end + + # When the stream transitions to the closed state, this method is called. There are roughly two ways this can happen: + # - A frame is received which causes this stream to enter the closed state. This method will be invoked from the background reader task. + # - A frame is sent which causes this stream to enter the closed state. This method will be invoked from that task. + # While the input stream is relatively straight forward, the output stream can trigger the second case above + def closed(error) + super + + if @input + @input.close(error) + @input = nil + end + + if @output + @output.stop(error) + @output = nil + end + + return self + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/https.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/https.rb new file mode 100644 index 0000000..33b5816 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/https.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true +# +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'http10' +require_relative 'http11' + +require_relative 'http2' + +require 'openssl' + +unless OpenSSL::SSL::SSLContext.instance_methods.include? :alpn_protocols= + warn "OpenSSL implementation doesn't support ALPN." + + class OpenSSL::SSL::SSLContext + def alpn_protocols= names + return names + end + end + + class OpenSSL::SSL::SSLSocket + def alpn_protocol + return nil + end + end +end + +module Async + module HTTP + module Protocol + # A server that supports both HTTP1.0 and HTTP1.1 semantics by detecting the version of the request. + module HTTPS + HANDLERS = { + "h2" => HTTP2, + "http/1.1" => HTTP11, + "http/1.0" => HTTP10, + nil => HTTP11, + } + + def self.protocol_for(peer) + # alpn_protocol is only available if openssl v1.0.2+ + name = peer.alpn_protocol + + Console.logger.debug(self) {"Negotiating protocol #{name.inspect}..."} + + if protocol = HANDLERS[name] + return protocol + else + raise ArgumentError, "Could not determine protocol for connection (#{name.inspect})." + end + end + + def self.client(peer) + protocol_for(peer).client(peer) + end + + def self.server(peer) + protocol_for(peer).server(peer) + end + + # Supported Application Layer Protocol Negotiation names: + def self.names + HANDLERS.keys.compact + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/request.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/request.rb new file mode 100644 index 0000000..0b0c469 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/request.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true +# +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'protocol/http/request' +require 'protocol/http/headers' + +require_relative '../body/writable' + +module Async + module HTTP + module Protocol + # Failed to send the request. The request body has NOT been consumed (i.e. #read) and you should retry the request. + class RequestFailed < StandardError + end + + # This is generated by server protocols. + class Request < ::Protocol::HTTP::Request + def connection + nil + end + + def hijack? + false + end + + def peer + if connection = self.connection + connection.peer + end + end + + def remote_address + @remote_address ||= peer.remote_address + end + + def remote_address= value + @remote_address = value + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/response.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/response.rb new file mode 100644 index 0000000..db7941d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/protocol/response.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true +# +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'protocol/http/response' + +require_relative '../body/writable' + +module Async + module HTTP + module Protocol + # This is generated by client protocols. + class Response < ::Protocol::HTTP::Response + def connection + nil + end + + def hijack? + false + end + + def peer + if connection = self.connection + connection.peer + end + end + + def remote_address + @remote_address ||= peer.remote_address + end + + def remote_address= value + @remote_address = value + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/proxy.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/proxy.rb new file mode 100644 index 0000000..645f7eb --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/proxy.rb @@ -0,0 +1,130 @@ +# frozen_string_literal: true +# +# Copyright, 2019, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'client' +require_relative 'endpoint' + +require_relative 'body/pipe' + +module Async + module HTTP + # Wraps a client, address and headers required to initiate a connectio to a remote host using the CONNECT verb. + # Behaves like a TCP endpoint for the purposes of connecting to a remote host. + class Proxy + class ConnectFailure < StandardError + def initialize(response) + super "Failed to connect: #{response.status}" + @response = response + end + + attr :response + end + + module Client + def proxy(endpoint, headers = nil) + Proxy.new(self, endpoint.authority(false), headers) + end + + # Create a client that will proxy requests through the current client. + def proxied_client(endpoint, headers = nil) + proxy = self.proxy(endpoint, headers) + + return self.class.new(proxy.wrap_endpoint(endpoint)) + end + + def proxied_endpoint(endpoint, headers = nil) + proxy = self.proxy(endpoint, headers) + + return proxy.wrap_endpoint(endpoint) + end + end + + # Prepare and endpoint which can establish a TCP connection to the remote system. + # @param client [Async::HTTP::Client] the client which will be used as a proxy server. + # @param host [String] the hostname or address to connect to. + # @param port [String] the port number to connect to. + # @param headers [Array] an optional list of headers to use when establishing the connection. + # @see Async::IO::Endpoint#tcp + def self.tcp(client, host, port, headers = nil) + self.new(client, "#{host}:#{port}", headers) + end + + # Construct a endpoint that will use the given client as a proxy for HTTP requests. + # @param client [Async::HTTP::Client] the client which will be used as a proxy server. + # @param endpoint [Async::HTTP::Endpoint] the endpoint to connect to. + # @param headers [Array] an optional list of headers to use when establishing the connection. + def self.endpoint(client, endpoint, headers = nil) + proxy = self.new(client, endpoint.authority(false), headers) + + return proxy.endpoint(endpoint.url) + end + + # @param client [Async::HTTP::Client] the client which will be used as a proxy server. + # @param address [String] the address to connect to. + # @param headers [Array] an optional list of headers to use when establishing the connection. + def initialize(client, address, headers = nil) + @client = client + @address = address + @headers = ::Protocol::HTTP::Headers[headers].freeze + end + + attr :client + + # Close the underlying client connection. + def close + @client.close + end + + # Establish a TCP connection to the specified host. + # @return [Socket] a connected bi-directional socket. + def connect(&block) + input = Body::Writable.new + + response = @client.connect(@address.to_s, @headers, input) + + if response.success? + pipe = Body::Pipe.new(response.body, input) + + return pipe.to_io unless block_given? + + begin + yield pipe.to_io + ensure + pipe.close + end + else + # This ensures we don't leave a response dangling: + response.close + + raise ConnectFailure, response + end + end + + # @return [Async::HTTP::Endpoint] an endpoint that connects via the specified proxy. + def wrap_endpoint(endpoint) + Endpoint.new(endpoint.url, self, **endpoint.options) + end + end + + Client.prepend(Proxy::Client) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/reference.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/reference.rb new file mode 100644 index 0000000..85af112 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/reference.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true +# +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'protocol/http/reference' + +module Async + module HTTP + Reference = ::Protocol::HTTP::Reference + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/relative_location.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/relative_location.rb new file mode 100644 index 0000000..df6ba8a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/relative_location.rb @@ -0,0 +1,80 @@ +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'client' +require_relative 'endpoint' +require_relative 'reference' + +require 'protocol/http/middleware' + +module Async + module HTTP + class TooManyRedirects < StandardError + end + + # A client wrapper which transparently handles both relative and absolute redirects to a given maximum number of hops. + class RelativeLocation < ::Protocol::HTTP::Middleware + DEFAULT_METHOD = GET + + # maximum_hops is the max number of redirects. Set to 0 to allow 1 request with no redirects. + def initialize(app, maximum_hops = 3) + super(app) + + @maximum_hops = maximum_hops + end + + # The maximum number of hops which will limit the number of redirects until an error is thrown. + attr :maximum_hops + + def call(request) + hops = 0 + + # We need to cache the body as it might be submitted multiple times. + request.finish + + while hops <= @maximum_hops + response = super(request) + + if response.redirection? + hops += 1 + response.finish + + location = response.headers['location'] + uri = URI.parse(location) + + if uri.absolute? + return response + else + request.path = Reference[request.path] + location + end + + unless response.preserve_method? + request.method = DEFAULT_METHOD + end + else + return response + end + end + + raise TooManyRedirects, "Redirected #{hops} times, exceeded maximum!" + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/server.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/server.rb new file mode 100755 index 0000000..33e9a5b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/server.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true +# +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'async/io/endpoint' +require 'async/io/stream' + +require 'protocol/http/middleware' + +require 'traces/provider' + +require_relative 'protocol' + +module Async + module HTTP + class Server < ::Protocol::HTTP::Middleware + def self.for(*arguments, **options, &block) + self.new(block, *arguments, **options) + end + + def initialize(app, endpoint, protocol: endpoint.protocol, scheme: endpoint.scheme) + super(app) + + @endpoint = endpoint + @protocol = protocol + @scheme = scheme + end + + attr :endpoint + attr :protocol + attr :scheme + + def accept(peer, address, task: Task.current) + connection = @protocol.server(peer) + + Console.logger.debug(self) {"Incoming connnection from #{address.inspect} to #{@protocol}"} + + connection.each do |request| + # We set the default scheme unless it was otherwise specified. + # https://tools.ietf.org/html/rfc7230#section-5.5 + request.scheme ||= self.scheme + + # This is a slight optimization to avoid having to get the address from the socket. + request.remote_address = address + + # Console.logger.debug(self) {"Incoming request from #{address.inspect}: #{request.method} #{request.path}"} + + # If this returns nil, we assume that the connection has been hijacked. + self.call(request) + end + ensure + connection&.close + end + + def run + @endpoint.accept(&self.method(:accept)) + end + + Traces::Provider(self) do + def call(request) + if trace_parent = request.headers['traceparent'] + self.trace_context = Traces::Context.parse(trace_parent.join, request.headers['tracestate'], remote: true) + end + + attributes = { + 'http.method': request.method, + 'http.authority': request.authority, + 'http.scheme': request.scheme, + 'http.path': request.path, + 'http.user_agent': request.headers['user-agent'], + } + + if length = request.body&.length + attributes['http.request.length'] = length + end + + if protocol = request.protocol + attributes['http.protocol'] = protocol + end + + trace('async.http.server.call', resource: "#{request.method} #{request.path}", attributes: attributes) do |span| + super.tap do |response| + if status = response&.status + span['http.status_code'] = status + end + + if length = response&.body&.length + span['http.response.length'] = length + end + end + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/statistics.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/statistics.rb new file mode 100644 index 0000000..c954250 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/statistics.rb @@ -0,0 +1,134 @@ +# frozen_string_literal: true +# +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'protocol/http/body/wrapper' + +require 'async/clock' + +module Async + module HTTP + class Statistics + def self.start + self.new(Clock.now) + end + + def initialize(start_time) + @start_time = start_time + end + + def wrap(response, &block) + if response and response.body + response.body = Body::Statistics.new(@start_time, response.body, block) + end + + return response + end + end + + module Body + # Invokes a callback once the body has finished reading. + class Statistics < ::Protocol::HTTP::Body::Wrapper + def initialize(start_time, body, callback) + super(body) + + @sent = 0 + + @start_time = start_time + @first_chunk_time = nil + @end_time = nil + + @callback = callback + end + + attr :start_time + attr :first_chunk_time + attr :end_time + + attr :sent + + def total_duration + if @end_time + @end_time - @start_time + end + end + + def first_chunk_duration + if @first_chunk_time + @first_chunk_time - @start_time + end + end + + def close(error = nil) + complete_statistics(error) + + super + end + + def read + chunk = super + + @first_chunk_time ||= Clock.now + + if chunk + @sent += chunk.bytesize + end + + return chunk + end + + def to_s + parts = ["sent #{@sent} bytes"] + + if duration = self.total_duration + parts << "took #{format_duration(duration)} in total" + end + + if duration = self.first_chunk_duration + parts << "took #{format_duration(duration)} until first chunk" + end + + return parts.join('; ') + end + + def inspect + "#{super} | \#<#{self.class} #{self.to_s}>" + end + + private + + def complete_statistics(error = nil) + @end_time = Clock.now + + @callback.call(self, error) if @callback + end + + def format_duration(seconds) + if seconds < 1.0 + return "#{(seconds * 1000.0).round(2)}ms" + else + return "#{seconds.round(1)}s" + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/version.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/version.rb new file mode 100644 index 0000000..4733844 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-0.59.3/lib/async/http/version.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true +# +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +module Async + module HTTP + VERSION = "0.59.3" + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-faraday-0.11.0/examples/topics.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-faraday-0.11.0/examples/topics.rb new file mode 100644 index 0000000..12bf4b7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-faraday-0.11.0/examples/topics.rb @@ -0,0 +1,33 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +$LOAD_PATH.unshift File.expand_path("../lib", __dir__) + +require 'async' +require 'faraday' +require 'async/http/faraday' + +# Async.logger.debug! + +module TestAsync + URL = 'https://www.google.com/search' + TOPICS = %W{ruby python lisp javascript cobol} + + def self.fetch_topics_async + TOPICS.map do |topic| + Async do + Faraday.get("#{URL}?q=#{topic}") + end + end.map(&:wait) + end +end + +Faraday.default_adapter = :async_http + +Async do + pp TestAsync.fetch_topics_async +ensure + # This line is fairly essential if you intend to exit from the async block. + Faraday.default_connection.close +end + diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-faraday-0.11.0/lib/.DS_Store b/vendor/bundle/ruby/2.7.0/gems/async-http-faraday-0.11.0/lib/.DS_Store new file mode 100644 index 0000000..7687ea4 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/gems/async-http-faraday-0.11.0/lib/.DS_Store differ diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-faraday-0.11.0/lib/async/http/faraday.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-faraday-0.11.0/lib/async/http/faraday.rb new file mode 100644 index 0000000..fcc3d7a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-faraday-0.11.0/lib/async/http/faraday.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative "faraday/version" +require_relative "faraday/adapter" + +Faraday::Adapter.register_middleware :async_http => Async::HTTP::Faraday::Adapter diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-faraday-0.11.0/lib/async/http/faraday/adapter.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-faraday-0.11.0/lib/async/http/faraday/adapter.rb new file mode 100644 index 0000000..c2196c6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-faraday-0.11.0/lib/async/http/faraday/adapter.rb @@ -0,0 +1,154 @@ +# frozen_string_literal: true + +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'faraday' +require 'faraday/adapter' +require 'kernel/sync' + +require 'async/http/client' +require 'async/http/proxy' + +require_relative 'agent' + +module Async + module HTTP + module Faraday + class Adapter < ::Faraday::Adapter + CONNECTION_EXCEPTIONS = [ + Errno::EADDRNOTAVAIL, + Errno::ECONNABORTED, + Errno::ECONNREFUSED, + Errno::ECONNRESET, + Errno::EHOSTUNREACH, + Errno::EINVAL, + Errno::ENETUNREACH, + Errno::EPIPE, + IOError, + SocketError + ].freeze + + def initialize(*arguments, timeout: nil, **options, &block) + super(*arguments, **options) + + @timeout = timeout + + @clients = {} + + @options = options + end + + def make_client(endpoint) + Client.new(endpoint, **@connection_options) + end + + def host_key(endpoint) + url = endpoint.url.dup + + url.path = "" + url.fragment = nil + url.query = nil + + return url + end + + def client_for(endpoint) + key = host_key(endpoint) + + @clients.fetch(key) do + @clients[key] = make_client(endpoint) + end + end + + def proxy_client_for(proxy_endpoint, endpoint) + key = [host_key(proxy_endpoint), host_key(endpoint)] + + @clients.fetch(key) do + client = client_for(proxy_endpoint) + @clients[key] = client.proxied_client(endpoint) + end + end + + def close + # The order of operations here is to avoid a race condition between iterating over clients (#close may yield) and creating new clients. + clients = @clients.values + + @clients.clear + + clients.each(&:close) + end + + def call(env) + super + + Sync do + endpoint = Endpoint.new(env.url) + + if proxy = env.request.proxy + proxy_endpoint = Endpoint.new(proxy.uri) + client = self.proxy_client_for(proxy_endpoint, endpoint) + else + client = self.client_for(endpoint) + end + + if body = env.body + body = Body::Buffered.wrap(body) + end + + if headers = env.request_headers + headers = ::Protocol::HTTP::Headers[headers] + end + + method = env.method.upcase + + request = ::Protocol::HTTP::Request.new(endpoint.scheme, endpoint.authority, method, endpoint.path, nil, headers, body) + + with_timeout do + response = client.call(request) + + save_response(env, response.status, response.read, response.headers) + end + end + + return @app.call(env) + rescue Errno::ETIMEDOUT, Async::TimeoutError => e + raise ::Faraday::TimeoutError, e + rescue OpenSSL::SSL::SSLError => e + raise ::Faraday::SSLError, e + rescue *CONNECTION_EXCEPTIONS => e + raise ::Faraday::ConnectionFailed, e + end + + private + + def with_timeout(task: Async::Task.current) + if @timeout + task.with_timeout(@timeout, ::Faraday::TimeoutError) do + yield + end + else + yield + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-faraday-0.11.0/lib/async/http/faraday/agent.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-faraday-0.11.0/lib/async/http/faraday/agent.rb new file mode 100644 index 0000000..f3aa338 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-faraday-0.11.0/lib/async/http/faraday/agent.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +# Copyright, 2020, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +begin + require 'sawyer/agent' + + # This is a nasty hack until https://github.com/lostisland/sawyer/pull/67 is resolved: + unless Sawyer::Agent.instance_methods.include?(:close) + class Sawyer::Agent + def close + @conn.close if @conn.respond_to?(:close) + end + end + end +rescue LoadError + # Ignore. +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-faraday-0.11.0/lib/async/http/faraday/default.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-faraday-0.11.0/lib/async/http/faraday/default.rb new file mode 100644 index 0000000..7826931 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-faraday-0.11.0/lib/async/http/faraday/default.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +# Copyright, 2020, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'adapter' + +::Faraday.default_adapter = :async_http diff --git a/vendor/bundle/ruby/2.7.0/gems/async-http-faraday-0.11.0/lib/async/http/faraday/version.rb b/vendor/bundle/ruby/2.7.0/gems/async-http-faraday-0.11.0/lib/async/http/faraday/version.rb new file mode 100644 index 0000000..5e870ef --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-http-faraday-0.11.0/lib/async/http/faraday/version.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +module Async + module HTTP + module Faraday + VERSION = "0.11.0" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io.rb new file mode 100644 index 0000000..f8ed3ed --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'async' + +require_relative "io/generic" +require_relative "io/socket" +require_relative "io/version" + +require_relative "io/endpoint" +require_relative "io/endpoint/each" + +module Async + module IO + def self.file_descriptor_limit + Process.getrlimit(Process::RLIMIT_NOFILE).first + end + + def self.buffer? + ::IO.const_defined?(:Buffer) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/address.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/address.rb new file mode 100644 index 0000000..a4d7a9a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/address.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'socket' + +module Async + module IO + Address = Addrinfo + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/address_endpoint.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/address_endpoint.rb new file mode 100644 index 0000000..1651db0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/address_endpoint.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'endpoint' + +module Async + module IO + # This class will open and close the socket automatically. + class AddressEndpoint < Endpoint + def initialize(address, **options) + super(**options) + + @address = address + end + + def to_s + "\#<#{self.class} #{@address.inspect}>" + end + + attr :address + + # Bind a socket to the given address. If a block is given, the socket will be automatically closed when the block exits. + # @yield [Socket] the bound socket + # @return [Socket] the bound socket + def bind(&block) + Socket.bind(@address, **@options, &block) + end + + # Connects a socket to the given address. If a block is given, the socket will be automatically closed when the block exits. + # @return [Socket] the connected socket + def connect(&block) + Socket.connect(@address, **@options, &block) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/binary_string.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/binary_string.rb new file mode 100644 index 0000000..5f57df0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/binary_string.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'buffer' + +module Async + module IO + # This is deprecated. + BinaryString = Buffer + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/buffer.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/buffer.rb new file mode 100644 index 0000000..b36ef16 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/buffer.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +module Async + module IO + class Buffer < String + BINARY = Encoding::BINARY + + def initialize + super + + force_encoding(BINARY) + end + + def << string + if string.encoding == BINARY + super(string) + else + super(string.b) + end + + return self + end + + alias concat << + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/composite_endpoint.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/composite_endpoint.rb new file mode 100644 index 0000000..c50af4a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/composite_endpoint.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'endpoint' + +module Async + module IO + class CompositeEndpoint < Endpoint + def initialize(endpoints, **options) + super(**options) + @endpoints = endpoints + end + + def each(&block) + @endpoints.each(&block) + end + + def connect(&block) + error = nil + + @endpoints.each do |endpoint| + begin + return endpoint.connect(&block) + rescue => error + end + end + + raise error + end + + def bind(&block) + @endpoints.map(&:bind) + end + end + + class Endpoint + def self.composite(*endpoints, **options) + CompositeEndpoint.new(endpoints, **options) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/endpoint.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/endpoint.rb new file mode 100644 index 0000000..0a5d79e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/endpoint.rb @@ -0,0 +1,130 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'address' +require_relative 'socket' + +require 'uri' + +module Async + module IO + # Endpoints represent a way of connecting or binding to an address. + class Endpoint + def initialize(**options) + @options = options.freeze + end + + def with(**options) + dup = self.dup + + dup.options = @options.merge(options) + + return dup + end + + attr_accessor :options + + # @return [String] The hostname of the bound socket. + def hostname + @options[:hostname] + end + + # If `SO_REUSEPORT` is enabled on a socket, the socket can be successfully bound even if there are existing sockets bound to the same address, as long as all prior bound sockets also had `SO_REUSEPORT` set before they were bound. + # @return [Boolean, nil] The value for `SO_REUSEPORT`. + def reuse_port + @options[:reuse_port] + end + + # If `SO_REUSEADDR` is enabled on a socket prior to binding it, the socket can be successfully bound unless there is a conflict with another socket bound to exactly the same combination of source address and port. Additionally, when set, binding a socket to the address of an existing socket in `TIME_WAIT` is not an error. + # @return [Boolean] The value for `SO_REUSEADDR`. + def reuse_address + @options[:reuse_address] + end + + # Controls SO_LINGER. The amount of time the socket will stay in the `TIME_WAIT` state after being closed. + # @return [Integer, nil] The value for SO_LINGER. + def linger + @options[:linger] + end + + # @return [Numeric] The default timeout for socket operations. + def timeout + @options[:timeout] + end + + # @return [Address] the address to bind to before connecting. + def local_address + @options[:local_address] + end + + # Endpoints sometimes have multiple paths. + # @yield [Endpoint] Enumerate all discrete paths as endpoints. + def each + return to_enum unless block_given? + + yield self + end + + # Accept connections from the specified endpoint. + # @param backlog [Integer] the number of connections to listen for. + def accept(backlog = Socket::SOMAXCONN, &block) + bind do |server| + server.listen(backlog) + + server.accept_each(&block) + end + end + + # Map all endpoints by invoking `#bind`. + # @yield the bound wrapper. + def bound + wrappers = [] + + self.each do |endpoint| + wrapper = endpoint.bind + wrappers << wrapper + + yield wrapper + end + + return wrappers + ensure + wrappers.each(&:close) if $! + end + + # Create an Endpoint instance by URI scheme. The host and port of the URI will be passed to the Endpoint factory method, along with any options. + # + # @param string [String] URI as string. Scheme will decide implementation used. + # @param options keyword arguments passed through to {#initialize} + # + # @see Endpoint.ssl ssl - invoked when parsing a URL with the ssl scheme "ssl://127.0.0.1" + # @see Endpoint.tcp tcp - invoked when parsing a URL with the tcp scheme: "tcp://127.0.0.1" + # @see Endpoint.udp udp - invoked when parsing a URL with the udp scheme: "udp://127.0.0.1" + # @see Endpoint.unix unix - invoked when parsing a URL with the unix scheme: "unix://127.0.0.1" + def self.parse(string, **options) + uri = URI.parse(string) + + self.public_send(uri.scheme, uri.host, uri.port, **options) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/endpoint/each.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/endpoint/each.rb new file mode 100644 index 0000000..7595195 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/endpoint/each.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative "../host_endpoint" +require_relative "../socket_endpoint" +require_relative "../ssl_endpoint" + +module Async + module IO + class Endpoint + def self.try_convert(specification) + if specification.is_a? self + specification + elsif specification.is_a? Array + self.send(*specification) + elsif specification.is_a? String + self.parse(specification) + elsif specification.is_a? ::BasicSocket + self.socket(specification) + elsif specification.is_a? Generic + self.new(specification) + else + raise ArgumentError.new("Not sure how to convert #{specification} to endpoint!") + end + end + + # Generate a list of endpoints from an array. + def self.each(specifications, &block) + return to_enum(:each, specifications) unless block_given? + + specifications.each do |specification| + yield try_convert(specification) + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/generic.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/generic.rb new file mode 100644 index 0000000..7008464 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/generic.rb @@ -0,0 +1,230 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'async/wrapper' +require 'forwardable' + +module Async + module IO + # The default block size for IO buffers. Defaults to 64KB (typical pipe buffer size). + BLOCK_SIZE = ENV.fetch('ASYNC_IO_BLOCK_SIZE', 1024*64).to_i + + # The maximum read size when appending to IO buffers. Defaults to 8MB. + MAXIMUM_READ_SIZE = ENV.fetch('ASYNC_IO_MAXIMUM_READ_SIZE', BLOCK_SIZE * 128).to_i + + # Convert a Ruby ::IO object to a wrapped instance: + def self.try_convert(io, &block) + if wrapper_class = Generic::WRAPPERS[io.class] + wrapper_class.new(io, &block) + else + raise ArgumentError.new("Unsure how to wrap #{io.class}!") + end + end + + def self.pipe + ::IO.pipe.map(&Generic.method(:new)) + end + + # Represents an asynchronous IO within a reactor. + class Generic < Wrapper + extend Forwardable + + WRAPPERS = {} + + class << self + # @!macro [attach] wrap_blocking_method + # @method $1 + # Invokes `$2` on the underlying {io}. If the operation would block, the current task is paused until the operation can succeed, at which point it's resumed and the operation is completed. + def wrap_blocking_method(new_name, method_name, invert: true, &block) + if block_given? + define_method(new_name, &block) + else + define_method(new_name) do |*args| + async_send(method_name, *args) + end + end + + if invert + # We wrap the original _nonblock method, ignoring options. + define_method(method_name) do |*args, exception: false| + async_send(method_name, *args) + end + end + end + + attr :wrapped_klass + + def wraps(klass, *additional_methods) + @wrapped_klass = klass + WRAPPERS[klass] = self + + # These are methods implemented by the wrapped class, that we aren't overriding, that may be of interest: + # fallback_methods = klass.instance_methods(false) - instance_methods + # puts "Forwarding #{klass} methods #{fallback_methods} to @io" + + def_delegators :@io, *additional_methods + end + + # Instantiate a wrapped instance of the class, and optionally yield it to a given block, closing it afterwards. + def wrap(*args) + wrapper = self.new(@wrapped_klass.new(*args)) + + return wrapper unless block_given? + + begin + yield wrapper + ensure + wrapper.close + end + end + end + + wraps ::IO, :external_encoding, :internal_encoding, :autoclose?, :autoclose=, :pid, :stat, :binmode, :flush, :set_encoding, :set_encoding_by_bom, :to_io, :to_i, :reopen, :fileno, :fsync, :fdatasync, :sync, :sync=, :tell, :seek, :rewind, :pos, :pos=, :eof, :eof?, :close_on_exec?, :close_on_exec=, :closed?, :close_read, :close_write, :isatty, :tty?, :binmode?, :sysseek, :advise, :ioctl, :fcntl, :nread, :ready?, :pread, :pwrite, :pathconf + + # Read the specified number of bytes from the input stream. This is fast path. + # @example + # data = io.sysread(512) + wrap_blocking_method :sysread, :read_nonblock + + alias readpartial read_nonblock + + # Read `length` bytes of data from the underlying I/O. If length is unspecified, read everything. + def read(length = nil, buffer = nil) + if buffer + buffer.clear + else + buffer = String.new + end + + if length + return String.new(encoding: Encoding::BINARY) if length <= 0 + + # Fast path: + if buffer = self.sysread(length, buffer) + + # Slow path: + while buffer.bytesize < length + # Slow path: + if chunk = self.sysread(length - buffer.bytesize) + buffer << chunk + else + break + end + end + + return buffer + else + return nil + end + else + buffer = self.sysread(BLOCK_SIZE, buffer) + + while chunk = self.sysread(BLOCK_SIZE) + buffer << chunk + end + + return buffer + end + end + + # Write entire buffer to output stream. This is fast path. + # @example + # io.syswrite("Hello World") + wrap_blocking_method :syswrite, :write_nonblock + + def write(buffer) + # Fast path: + written = self.syswrite(buffer) + remaining = buffer.bytesize - written + + while remaining > 0 + # Slow path: + length = self.syswrite(buffer.byteslice(written, remaining)) + + remaining -= length + written += length + end + + return written + end + + def << buffer + write(buffer) + return self + end + + def dup + super.tap do |copy| + copy.timeout = self.timeout + end + end + + def wait(timeout = self.timeout, mode = :read) + case mode + when :read + wait_readable(timeout) + when :write + wait_writable(timeout) + else + wait_any(:rw, timeout) + end + rescue TimeoutError + return nil + end + + def nonblock + true + end + + def nonblock= value + true + end + + def nonblock? + true + end + + def connected? + !@io.closed? + end + + attr_accessor :timeout + + protected + + def async_send(*arguments, timeout: self.timeout) + while true + result = @io.__send__(*arguments, exception: false) + + case result + when :wait_readable + wait_readable(timeout) + when :wait_writable + wait_writable(timeout) + else + return result + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/host_endpoint.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/host_endpoint.rb new file mode 100644 index 0000000..28f690f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/host_endpoint.rb @@ -0,0 +1,117 @@ +# frozen_string_literal: true + +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'address_endpoint' + +module Async + module IO + class HostEndpoint < Endpoint + def initialize(specification, **options) + super(**options) + + @specification = specification + end + + def to_s + nodename, service, family, socktype, protocol, flags = @specification + + "\#<#{self.class} name=#{nodename.inspect} service=#{service.inspect} family=#{family.inspect} type=#{socktype.inspect} protocol=#{protocol.inspect} flags=#{flags.inspect}>" + end + + def address + @specification + end + + def hostname + @specification.first + end + + # Try to connect to the given host by connecting to each address in sequence until a connection is made. + # @yield [Socket] the socket which is being connected, may be invoked more than once + # @return [Socket] the connected socket + # @raise if no connection could complete successfully + def connect + last_error = nil + + task = Task.current + + Addrinfo.foreach(*@specification) do |address| + begin + wrapper = Socket.connect(address, **@options, task: task) + rescue Errno::ECONNREFUSED, Errno::ENETUNREACH, Errno::EAGAIN + last_error = $! + else + return wrapper unless block_given? + + begin + return yield wrapper, task + ensure + wrapper.close + end + end + end + + raise last_error + end + + # Invokes the given block for every address which can be bound to. + # @yield [Socket] the bound socket + # @return [Array] an array of bound sockets + def bind(&block) + Addrinfo.foreach(*@specification).map do |address| + Socket.bind(address, **@options, &block) + end + end + + # @yield [AddressEndpoint] address endpoints by resolving the given host specification + def each + return to_enum unless block_given? + + Addrinfo.foreach(*@specification) do |address| + yield AddressEndpoint.new(address, **@options) + end + end + end + + class Endpoint + # @param args nodename, service, family, socktype, protocol, flags. `socktype` will be set to Socket::SOCK_STREAM. + # @param options keyword arguments passed on to {HostEndpoint#initialize} + # + # @return [HostEndpoint] + def self.tcp(*args, **options) + args[3] = ::Socket::SOCK_STREAM + + HostEndpoint.new(args, **options) + end + + # @param args nodename, service, family, socktype, protocol, flags. `socktype` will be set to Socket::SOCK_DGRAM. + # @param options keyword arguments passed on to {HostEndpoint#initialize} + # + # @return [HostEndpoint] + def self.udp(*args, **options) + args[3] = ::Socket::SOCK_DGRAM + + HostEndpoint.new(args, **options) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/notification.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/notification.rb new file mode 100644 index 0000000..dd4e635 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/notification.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'generic' + +module Async + module IO + # A cross-reactor/process notification pipe. + class Notification + def initialize + pipe = ::IO.pipe + + # We could call wait and signal from different reactors/threads/processes, so we don't create wrappers here, because they are not thread safe by design. + @input = pipe.first + @output = pipe.last + end + + def close + @input.close + @output.close + end + + # Wait for signal to be called. + # @return [Object] + def wait + wrapper = Async::IO::Generic.new(@input) + wrapper.read(1) + ensure + # Remove the wrapper from the reactor. + wrapper.reactor = nil + end + + # Signal to a given task that it should resume operations. + # @return [void] + def signal + wrapper = Async::IO::Generic.new(@output) + wrapper.write(".") + ensure + wrapper.reactor = nil + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/peer.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/peer.rb new file mode 100644 index 0000000..f6f09de --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/peer.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'socket' + +module Async + module IO + module Peer + include ::Socket::Constants + + # Is it likely that the socket is still connected? + # May return false positive, but won't return false negative. + def connected? + return false if @io.closed? + + # If we can wait for the socket to become readable, we know that the socket may still be open. + result = to_io.recv_nonblock(1, MSG_PEEK, exception: false) + + # Either there was some data available, or we can wait to see if there is data avaialble. + return !result.empty? || result == :wait_readable + + rescue Errno::ECONNRESET + # This might be thrown by recv_nonblock. + return false + end + + def eof + !connected? + end + + def eof? + !connected? + end + + # Best effort to set *_NODELAY if it makes sense. Swallows errors where possible. + def sync=(value) + super + + case self.protocol + when 0, IPPROTO_TCP + self.setsockopt(IPPROTO_TCP, TCP_NODELAY, value ? 1 : 0) + else + Console.logger.warn(self) {"Unsure how to sync=#{value} for #{self.protocol}!"} + end + rescue Errno::EINVAL + # On Darwin, sometimes occurs when the connection is not yet fully formed. Empirically, TCP_NODELAY is enabled despite this result. + rescue Errno::EOPNOTSUPP + # Some platforms may simply not support the operation. + # Console.logger.warn(self) {"Unable to set sync=#{value}!"} + end + + def sync + case self.protocol + when IPPROTO_TCP + self.getsockopt(IPPROTO_TCP, TCP_NODELAY).bool + else + true + end && super + end + + def type + self.local_address.socktype + end + + def protocol + self.local_address.protocol + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/protocol/generic.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/protocol/generic.rb new file mode 100644 index 0000000..4943070 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/protocol/generic.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative '../stream' + +module Async + module IO + module Protocol + class Generic + def initialize(stream) + @stream = stream + end + + def closed? + @stream.closed? + end + + def close + @stream.close + end + + attr :stream + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/protocol/line.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/protocol/line.rb new file mode 100755 index 0000000..957c599 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/protocol/line.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'generic' + +module Async + module IO + module Protocol + class Line < Generic + def initialize(stream, eol = $/) + super(stream) + + @eol = eol + end + + attr :eol + + def write_lines(*args) + if args.empty? + @stream.write(@eol) + else + args.each do |arg| + @stream.write(arg) + @stream.write(@eol) + end + end + + @stream.flush + end + + def read_line + @stream.read_until(@eol) or @stream.eof! + end + + def peek_line + @stream.peek do |read_buffer| + if index = read_buffer.index(@eol) + return read_buffer.slice(0, index) + end + end + + raise EOFError + end + + def each_line + return to_enum(:each_line) unless block_given? + + while line = @stream.read_until(@eol) + yield line + end + end + + def read_lines + @stream.read.split(@eol) + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/server.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/server.rb new file mode 100644 index 0000000..f291348 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/server.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'async/task' + +module Async + module IO + module Server + def accept_each(timeout: nil, task: Task.current) + task.annotate "accepting connections #{self.local_address.inspect} [fd=#{self.fileno}]" + + callback = lambda do |io, address| + yield io, address, task: task + end + + while true + self.accept(timeout: timeout, task: task, &callback) + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/shared_endpoint.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/shared_endpoint.rb new file mode 100644 index 0000000..457c134 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/shared_endpoint.rb @@ -0,0 +1,136 @@ +# frozen_string_literal: true + +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'endpoint' +require_relative 'composite_endpoint' + +module Async + module IO + # Pre-connect and pre-bind sockets so that it can be used between processes. + class SharedEndpoint < Endpoint + # Create a new `SharedEndpoint` by binding to the given endpoint. + def self.bound(endpoint, backlog: Socket::SOMAXCONN, close_on_exec: false) + wrappers = endpoint.bound do |server| + # This is somewhat optional. We want to have a generic interface as much as possible so that users of this interface can just call it without knowing a lot of internal details. Therefore, we ignore errors here if it's because the underlying socket does not support the operation. + begin + server.listen(backlog) + rescue Errno::EOPNOTSUPP + # Ignore. + end + + server.close_on_exec = close_on_exec + server.reactor = nil + end + + return self.new(endpoint, wrappers) + end + + # Create a new `SharedEndpoint` by connecting to the given endpoint. + def self.connected(endpoint, close_on_exec: false) + wrapper = endpoint.connect + + wrapper.close_on_exec = close_on_exec + wrapper.reactor = nil + + return self.new(endpoint, [wrapper]) + end + + def initialize(endpoint, wrappers, **options) + super(**options) + + @endpoint = endpoint + @wrappers = wrappers + end + + attr :endpoint + attr :wrappers + + def local_address_endpoint(**options) + endpoints = @wrappers.map do |wrapper| + AddressEndpoint.new(wrapper.to_io.local_address) + end + + return CompositeEndpoint.new(endpoints, **options) + end + + def remote_address_endpoint(**options) + endpoints = @wrappers.map do |wrapper| + AddressEndpoint.new(wrapper.to_io.remote_address) + end + + return CompositeEndpoint.new(endpoints, **options) + end + + # Close all the internal wrappers. + def close + @wrappers.each(&:close) + @wrappers.clear + end + + def bind + task = Async::Task.current + + @wrappers.each do |server| + server = server.dup + + task.async do |task| + task.annotate "binding to #{server.inspect}" + + begin + yield server, task + ensure + server.close + end + end + end + end + + def connect + task = Async::Task.current + + @wrappers.each do |peer| + peer = peer.dup + + task.async do |task| + task.annotate "connected to #{peer.inspect} [#{peer.fileno}]" + + begin + yield peer, task + ensure + peer.close + end + end + end + end + + def accept(backlog = nil, &block) + bind do |server| + server.accept_each(&block) + end + end + + def to_s + "\#<#{self.class} #{@wrappers.size} descriptors for #{@endpoint}>" + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/socket.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/socket.rb new file mode 100644 index 0000000..845e699 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/socket.rb @@ -0,0 +1,198 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'socket' +require 'async/task' + +require_relative 'peer' +require_relative 'server' +require_relative 'generic' + +module Async + module IO + class BasicSocket < Generic + wraps ::BasicSocket, :setsockopt, :connect_address, :close_read, :close_write, :local_address, :remote_address, :do_not_reverse_lookup, :do_not_reverse_lookup=, :shutdown, :getsockopt, :getsockname, :getpeername, :getpeereid + + wrap_blocking_method :recv, :recv_nonblock + wrap_blocking_method :recvmsg, :recvmsg_nonblock + + wrap_blocking_method :sendmsg, :sendmsg_nonblock + wrap_blocking_method :send, :sendmsg_nonblock, invert: false + + include Peer + end + + class Socket < BasicSocket + wraps ::Socket, :bind, :ipv6only!, :listen + + wrap_blocking_method :recvfrom, :recvfrom_nonblock + + # @raise Errno::EAGAIN the connection failed due to the remote end being overloaded. + def connect(*args) + begin + async_send(:connect_nonblock, *args) + rescue Errno::EISCONN + # We are now connected. + end + end + + alias connect_nonblock connect + + # @param duration [Numeric] the maximum time to wait for accepting a connection, if specified. + def accept(timeout: nil, task: Task.current) + peer, address = async_send(:accept_nonblock, timeout: timeout) + wrapper = Socket.new(peer, task.reactor) + + wrapper.timeout = self.timeout + + return wrapper, address unless block_given? + + task.async do |task| + task.annotate "incoming connection #{address.inspect} [fd=#{wrapper.fileno}]" + + begin + yield wrapper, address + ensure + wrapper.close + end + end + end + + alias accept_nonblock accept + alias sysaccept accept + + # Build and wrap the underlying io. + # @option reuse_port [Boolean] Allow this port to be bound in multiple processes. + # @option reuse_address [Boolean] Allow this port to be bound in multiple processes. + def self.build(*args, timeout: nil, reuse_address: true, reuse_port: nil, linger: nil, task: Task.current) + socket = wrapped_klass.new(*args) + + if reuse_address + socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) + end + + if reuse_port + socket.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1) + end + + if linger + socket.setsockopt(SOL_SOCKET, SO_LINGER, linger) + end + + yield socket + + wrapper = self.new(socket, task.reactor) + wrapper.timeout = timeout + + return wrapper + rescue Exception + socket.close if socket + + raise + end + + # Establish a connection to a given `remote_address`. + # @example + # socket = Async::IO::Socket.connect(Async::IO::Address.tcp("8.8.8.8", 53)) + # @param remote_address [Address] The remote address to connect to. + # @option local_address [Address] The local address to bind to before connecting. + def self.connect(remote_address, local_address: nil, task: Task.current, **options) + Console.logger.debug(self) {"Connecting to #{remote_address.inspect}"} + + task.annotate "connecting to #{remote_address.inspect}" + + wrapper = build(remote_address.afamily, remote_address.socktype, remote_address.protocol, **options) do |socket| + if local_address + if defined?(IP_BIND_ADDRESS_NO_PORT) + # Inform the kernel (Linux 4.2+) to not reserve an ephemeral port when using bind(2) with a port number of 0. The port will later be automatically chosen at connect(2) time, in a way that allows sharing a source port as long as the 4-tuple is unique. + socket.setsockopt(SOL_IP, IP_BIND_ADDRESS_NO_PORT, 1) + end + + socket.bind(local_address.to_sockaddr) + end + end + + begin + wrapper.connect(remote_address.to_sockaddr) + task.annotate "connected to #{remote_address.inspect} [fd=#{wrapper.fileno}]" + rescue Exception + wrapper.close + raise + end + + return wrapper unless block_given? + + begin + yield wrapper, task + ensure + wrapper.close + end + end + + # Bind to a local address. + # @example + # socket = Async::IO::Socket.bind(Async::IO::Address.tcp("0.0.0.0", 9090)) + # @param local_address [Address] The local address to bind to. + # @option protocol [Integer] The socket protocol to use. + def self.bind(local_address, protocol: 0, task: Task.current, **options, &block) + Console.logger.debug(self) {"Binding to #{local_address.inspect}"} + + wrapper = build(local_address.afamily, local_address.socktype, protocol, **options) do |socket| + socket.bind(local_address.to_sockaddr) + end + + return wrapper unless block_given? + + task.async do |task| + task.annotate "binding to #{wrapper.local_address.inspect}" + + begin + yield wrapper, task + ensure + wrapper.close + end + end + end + + # Bind to a local address and accept connections in a loop. + def self.accept(*args, backlog: SOMAXCONN, &block) + bind(*args) do |server, task| + server.listen(backlog) if backlog + + server.accept_each(task: task, &block) + end + end + + include Server + + def self.pair(*args) + ::Socket.pair(*args).map(&self.method(:new)) + end + end + + class IPSocket < BasicSocket + wraps ::IPSocket, :addr, :peeraddr + + wrap_blocking_method :recvfrom, :recvfrom_nonblock + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/socket_endpoint.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/socket_endpoint.rb new file mode 100644 index 0000000..ddd4d71 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/socket_endpoint.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'endpoint' + +module Async + module IO + # This class doesn't exert ownership over the specified socket, wraps a native ::IO. + class SocketEndpoint < Endpoint + def initialize(socket, **options) + super(**options) + + # This socket should already be in the required state. + @socket = Async::IO.try_convert(socket) + end + + def to_s + "\#<#{self.class} #{@socket.inspect}>" + end + + attr :socket + + def bind(&block) + if block_given? + begin + yield @socket + ensure + @socket.reactor = nil + end + else + return @socket + end + end + + def connect(&block) + if block_given? + begin + yield @socket + ensure + @socket.reactor = nil + end + else + return @socket + end + end + end + + class Endpoint + def self.socket(socket, **options) + SocketEndpoint.new(socket, **options) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/ssl_endpoint.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/ssl_endpoint.rb new file mode 100644 index 0000000..3a08242 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/ssl_endpoint.rb @@ -0,0 +1,119 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'host_endpoint' +require_relative 'ssl_socket' + +module Async + module IO + class SSLEndpoint < Endpoint + def initialize(endpoint, **options) + super(**options) + + @endpoint = endpoint + + if ssl_context = options[:ssl_context] + @context = build_context(ssl_context) + else + @context = nil + end + end + + def to_s + "\#<#{self.class} #{@endpoint}>" + end + + def address + @endpoint.address + end + + def hostname + @options[:hostname] || @endpoint.hostname + end + + attr :endpoint + attr :options + + def params + @options[:ssl_params] + end + + def build_context(context = OpenSSL::SSL::SSLContext.new) + if params = self.params + context.set_params(params) + end + + context.setup + context.freeze + + return context + end + + def context + @context ||= build_context + end + + # Connect to the underlying endpoint and establish a SSL connection. + # @yield [Socket] the socket which is being connected + # @return [Socket] the connected socket + def bind + if block_given? + @endpoint.bind do |server| + yield SSLServer.new(server, context) + end + else + return SSLServer.new(@endpoint.bind, context) + end + end + + # Connect to the underlying endpoint and establish a SSL connection. + # @yield [Socket] the socket which is being connected + # @return [Socket] the connected socket + def connect(&block) + SSLSocket.connect(@endpoint.connect, context, hostname, &block) + end + + def each + return to_enum unless block_given? + + @endpoint.each do |endpoint| + yield self.class.new(endpoint, **@options) + end + end + end + + # Backwards compatibility. + SecureEndpoint = SSLEndpoint + + class Endpoint + # @param args + # @param ssl_context [OpenSSL::SSL::SSLContext, nil] + # @param hostname [String, nil] + # @param options keyword arguments passed through to {Endpoint.tcp} + # + # @return [SSLEndpoint] + def self.ssl(*args, ssl_context: nil, hostname: nil, **options) + SSLEndpoint.new(self.tcp(*args, **options), ssl_context: ssl_context, hostname: hostname) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/ssl_socket.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/ssl_socket.rb new file mode 100644 index 0000000..2134f39 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/ssl_socket.rb @@ -0,0 +1,155 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'socket' + +require 'openssl' + +module Async + module IO + SSLError = OpenSSL::SSL::SSLError + + # Asynchronous TCP socket wrapper. + class SSLSocket < Generic + wraps OpenSSL::SSL::SSLSocket, :alpn_protocol, :cert, :cipher, :client_ca, :context, :finished_message, :peer_finished_message, :getsockopt, :hostname, :hostname=, :npn_protocol, :peer_cert, :peer_cert_chain, :pending, :post_connection_check, :setsockopt, :session, :session=, :session_reused?, :ssl_version, :state, :sync_close, :sync_close=, :sysclose, :verify_result, :tmp_key + + wrap_blocking_method :accept, :accept_nonblock + wrap_blocking_method :connect, :connect_nonblock + + def self.connect(socket, context, hostname = nil, &block) + client = self.new(socket, context) + + # Used for SNI: + if hostname + client.hostname = hostname + end + + begin + client.connect + rescue + # If the connection fails (e.g. certificates are invalid), the caller never sees the socket, so we close it and raise the exception up the chain. + client.close + + raise + end + + return client unless block_given? + + begin + yield client + ensure + client.close + end + end + + include Peer + + def initialize(socket, context) + if socket.is_a?(self.class.wrapped_klass) + super + else + io = self.class.wrapped_klass.new(socket.to_io, context) + super(io, socket.reactor) + + # We detach the socket from the reactor, otherwise it's possible to add the file descriptor to the selector twice, which is bad. + socket.reactor = nil + + # This ensures that when the internal IO is closed, it also closes the internal socket: + io.sync_close = true + + @timeout = socket.timeout + end + end + + def local_address + @io.to_io.local_address + end + + def remote_address + @io.to_io.remote_address + end + + def close_write + self.shutdown(Socket::SHUT_WR) + end + + def close_read + self.shutdown(Socket::SHUT_RD) + end + + def shutdown(how) + @io.flush + @io.to_io.shutdown(how) + end + end + + # We reimplement this from scratch because the native implementation doesn't expose the underlying server/context that we need to implement non-blocking accept. + class SSLServer + extend Forwardable + + def initialize(server, context) + @server = server + @context = context + end + + def fileno + @server.fileno + end + + def dup + self.class.new(@server.dup, @context) + end + + def_delegators :@server, :local_address, :setsockopt, :getsockopt, :close, :close_on_exec=, :reactor=, :timeout, :timeout= + + attr :server + attr :context + + def listen(*args) + @server.listen(*args) + end + + def accept(task: Task.current, **options) + peer, address = @server.accept(**options) + + wrapper = SSLSocket.new(peer, @context) + + return wrapper, address unless block_given? + + task.async do + task.annotate "accepting secure connection #{address.inspect}" + + begin + # You want to do this in a nested async task or you might suffer from head-of-line blocking. + wrapper.accept + + yield wrapper, address + ensure + wrapper.close + end + end + end + + include Server + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/standard.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/standard.rb new file mode 100644 index 0000000..7ca0fc1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/standard.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'generic' + +module Async + module IO + class StandardInput < Generic + def initialize(io = $stdin) + super(io) + end + end + + class StandardOutput < Generic + def initialize(io = $stdout) + super(io) + end + end + + class StandardError < Generic + def initialize(io = $stderr) + super(io) + end + end + + STDIN = StandardInput.new + STDOUT = StandardOutput.new + STDERR = StandardError.new + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/stream.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/stream.rb new file mode 100644 index 0000000..24f121b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/stream.rb @@ -0,0 +1,307 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'buffer' +require_relative 'generic' + +require 'async/semaphore' + +module Async + module IO + class Stream + BLOCK_SIZE = IO::BLOCK_SIZE + + def self.open(path, mode = "r+", **options) + stream = self.new(File.open(path, mode), **options) + + return stream unless block_given? + + begin + yield stream + ensure + stream.close + end + end + + def initialize(io, block_size: BLOCK_SIZE, maximum_read_size: MAXIMUM_READ_SIZE, sync: true, deferred: false) + @io = io + @eof = false + + @pending = 0 + # This field is ignored, but used to mean, try to buffer packets in a single iteration of the reactor. + # @deferred = deferred + + @writing = Async::Semaphore.new(1) + + # We don't want Ruby to do any IO buffering. + @io.sync = sync + + @block_size = block_size + @maximum_read_size = maximum_read_size + + @read_buffer = Buffer.new + @write_buffer = Buffer.new + @drain_buffer = Buffer.new + + # Used as destination buffer for underlying reads. + @input_buffer = Buffer.new + end + + attr :io + + attr :block_size + + # Reads `size` bytes from the stream. If size is not specified, read until end of file. + def read(size = nil) + return String.new(encoding: Encoding::BINARY) if size == 0 + + if size + until @eof or @read_buffer.bytesize >= size + # Compute the amount of data we need to read from the underlying stream: + read_size = size - @read_buffer.bytesize + + # Don't read less than @block_size to avoid lots of small reads: + fill_read_buffer(read_size > @block_size ? read_size : @block_size) + end + else + until @eof + fill_read_buffer + end + end + + return consume_read_buffer(size) + end + + # Read at most `size` bytes from the stream. Will avoid reading from the underlying stream if possible. + def read_partial(size = nil) + return String.new(encoding: Encoding::BINARY) if size == 0 + + if !@eof and @read_buffer.empty? + fill_read_buffer + end + + return consume_read_buffer(size) + end + + def read_exactly(size, exception: EOFError) + if buffer = read(size) + if buffer.bytesize != size + raise exception, "could not read enough data" + end + + return buffer + end + + raise exception, "encountered eof while reading data" + end + + alias readpartial read_partial + + # Efficiently read data from the stream until encountering pattern. + # @param pattern [String] The pattern to match. + # @return [String] The contents of the stream up until the pattern, which is consumed but not returned. + def read_until(pattern, offset = 0, chomp: true) + # We don't want to split on the pattern, so we subtract the size of the pattern. + split_offset = pattern.bytesize - 1 + + until index = @read_buffer.index(pattern, offset) + offset = @read_buffer.bytesize - split_offset + + offset = 0 if offset < 0 + + return unless fill_read_buffer + end + + @read_buffer.freeze + matched = @read_buffer.byteslice(0, index+(chomp ? 0 : pattern.bytesize)) + @read_buffer = @read_buffer.byteslice(index+pattern.bytesize, @read_buffer.bytesize) + + return matched + end + + def peek + until yield(@read_buffer) or @eof + fill_read_buffer + end + end + + def gets(separator = $/, **options) + read_until(separator, **options) + end + + # Flushes buffered data to the stream. + def flush + return if @write_buffer.empty? + + @writing.acquire do + # Flip the write buffer and drain buffer: + @write_buffer, @drain_buffer = @drain_buffer, @write_buffer + + begin + @io.write(@drain_buffer) + ensure + # If the write operation fails, we still need to clear this buffer, and the data is essentially lost. + @drain_buffer.clear + end + end + end + + # Writes `string` to the buffer. When the buffer is full or #sync is true the + # buffer is flushed to the underlying `io`. + # @param string the string to write to the buffer. + # @return the number of bytes appended to the buffer. + def write(string) + @write_buffer << string + + if @write_buffer.bytesize >= @block_size + flush + end + + return string.bytesize + end + + # Writes `string` to the stream and returns self. + def <<(string) + write(string) + + return self + end + + def puts(*arguments, separator: $/) + arguments.each do |argument| + @write_buffer << argument << separator + end + + flush + end + + def connected? + @io.connected? + end + + def closed? + @io.closed? + end + + def close_read + @io.close_read + end + + def close_write + flush + ensure + @io.close_write + end + + # Best effort to flush any unwritten data, and then close the underling IO. + def close + return if @io.closed? + + begin + flush + rescue + # We really can't do anything here unless we want #close to raise exceptions. + ensure + @io.close + end + end + + # Returns true if the stream is at file which means there is no more data to be read. + def eof? + if !@read_buffer.empty? + return false + elsif @eof + return true + else + return @io.eof? + end + end + + alias eof eof? + + def eof! + @read_buffer.clear + @eof = true + + raise EOFError + end + + private + + # Fills the buffer from the underlying stream. + def fill_read_buffer(size = @block_size) + # We impose a limit because the underlying `read` system call can fail if we request too much data in one go. + if size > @maximum_read_size + size = @maximum_read_size + end + + # This effectively ties the input and output stream together. + flush + + if @read_buffer.empty? + if @io.read_nonblock(size, @read_buffer, exception: false) + # Console.logger.debug(self, name: "read") {@read_buffer.inspect} + return true + end + else + if chunk = @io.read_nonblock(size, @input_buffer, exception: false) + @read_buffer << chunk + # Console.logger.debug(self, name: "read") {@read_buffer.inspect} + + return true + end + end + + # else for both cases above: + @eof = true + return false + end + + # Consumes at most `size` bytes from the buffer. + # @param size [Integer|nil] The amount of data to consume. If nil, consume entire buffer. + def consume_read_buffer(size = nil) + # If we are at eof, and the read buffer is empty, we can't consume anything. + return nil if @eof && @read_buffer.empty? + + result = nil + + if size.nil? or size >= @read_buffer.bytesize + # Consume the entire read buffer: + result = @read_buffer + @read_buffer = Buffer.new + else + # This approach uses more memory. + # result = @read_buffer.slice!(0, size) + + # We know that we are not going to reuse the original buffer. + # But byteslice will generate a hidden copy. So let's freeze it first: + @read_buffer.freeze + + result = @read_buffer.byteslice(0, size) + @read_buffer = @read_buffer.byteslice(size, @read_buffer.bytesize) + end + + return result + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/tcp_socket.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/tcp_socket.rb new file mode 100644 index 0000000..93f1e3a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/tcp_socket.rb @@ -0,0 +1,117 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'socket' +require_relative 'stream' +require 'fcntl' + +module Async + module IO + # Asynchronous TCP socket wrapper. + class TCPSocket < IPSocket + wraps ::TCPSocket + + def initialize(remote_host, remote_port = nil, local_host = nil, local_port = nil) + if remote_host.is_a? ::TCPSocket + super(remote_host) + else + remote_address = Addrinfo.tcp(remote_host, remote_port) + local_address = Addrinfo.tcp(local_host, local_port) if local_host + + # We do this unusual dance to avoid leaking an "open" socket instance. + socket = Socket.connect(remote_address, local_address: local_address) + fd = socket.fcntl(Fcntl::F_DUPFD) + Console.logger.debug(self) {"Connected to #{remote_address.inspect}: #{fd}"} + socket.close + + super(::TCPSocket.for_fd(fd)) + + # The equivalent blocking operation. Unfortunately there is no trivial way to make this non-blocking. + # super(::TCPSocket.new(remote_host, remote_port, local_host, local_port)) + end + + @stream = Stream.new(self) + end + + class << self + alias open new + end + + def close + @stream.flush + super + end + + include Peer + + attr :stream + + # The way this buffering works is pretty atrocious. + def_delegators :@stream, :gets, :puts + + def sysread(size, buffer = nil) + data = @stream.read_partial(size) + + if buffer + buffer.replace(data) + end + + return data + end + end + + # Asynchronous TCP server wrappper. + class TCPServer < TCPSocket + wraps ::TCPServer, :listen + + def initialize(*args) + if args.first.is_a? ::TCPServer + super(args.first) + else + # We assume this operation doesn't block (for long): + super(::TCPServer.new(*args)) + end + end + + def accept(timeout: nil, task: Task.current) + peer, address = async_send(:accept_nonblock, timeout: timeout) + + wrapper = TCPSocket.new(peer) + + wrapper.timeout = self.timeout + + return wrapper, address unless block_given? + + begin + yield wrapper, address + ensure + wrapper.close + end + end + + alias accept_nonblock accept + alias sysaccept accept + + include Server + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/threads.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/threads.rb new file mode 100644 index 0000000..0519ec8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/threads.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +# Copyright, 2020, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'notification' + +module Async + module IO + class Threads + def initialize(parent: nil) + @parent = parent + end + + if Async::Scheduler.supported? + def async(parent: (@parent or Task.current)) + parent.async do + thread = ::Thread.new do + yield + end + + thread.join + rescue Stop + if thread&.alive? + thread.raise(Stop) + end + + begin + thread.join + rescue Stop + # Ignore. + end + end + end + else + def async(parent: (@parent or Task.current)) + parent.async do |task| + notification = Async::IO::Notification.new + + thread = ::Thread.new do + yield + ensure + notification.signal + end + + task.annotate "Waiting for thread to finish..." + + notification.wait + + thread.value + ensure + if thread&.alive? + thread.raise(Stop) + + begin + thread.join + rescue Stop + # Ignore. + end + end + + notification&.close + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/trap.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/trap.rb new file mode 100644 index 0000000..5801fff --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/trap.rb @@ -0,0 +1,108 @@ +# frozen_string_literal: true + +# Copyright, 2018, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'notification' + +require 'thread' + +module Async + module IO + # A cross-reactor/process notification pipe. + class Trap + def initialize(name) + @name = name + @notifications = [] + + @installed = false + @mutex = Mutex.new + end + + def to_s + "\#<#{self.class} #{@name}>" + end + + # Ignore the trap within the current process. Can be invoked before forking and/or invoking `install!` to assert default behaviour. + def ignore! + Signal.trap(@name, :IGNORE) + end + + def default! + Signal.trap(@name, :DEFAULT) + end + + # Install the trap into the current process. Thread safe. + # @return [Boolean] whether the trap was installed or not. If the trap was already installed, returns nil. + def install! + return if @installed + + @mutex.synchronize do + return if @installed + + Signal.trap(@name, &self.method(:trigger)) + + @installed = true + end + + return true + end + + # Wait until the signal occurs. If a block is given, execute in a loop. + # @yield [Async::Task] the current task. + def wait(task: Task.current, &block) + task.annotate("waiting for signal #{@name}") + + notification = Notification.new + @notifications << notification + + if block_given? + while true + notification.wait + yield task + end + else + notification.wait + end + ensure + if notification + notification.close + @notifications.delete(notification) + end + end + + # Deprecated. + alias trap wait + + # In order to avoid blocking the reactor, specify `transient: true` as an option. + def async(parent: Task.current, **options, &block) + parent.async(**options) do |task| + self.wait(task: task, &block) + end + end + + # Signal all waiting tasks that the trap occurred. + # @return [void] + def trigger(signal_number = nil) + @notifications.each(&:signal) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/udp_socket.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/udp_socket.rb new file mode 100644 index 0000000..50b901b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/udp_socket.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'socket' + +module Async + module IO + # Asynchronous UDP socket wrapper. + class UDPSocket < IPSocket + wraps ::UDPSocket, :bind + + def initialize(family) + if family.is_a? ::UDPSocket + super(family) + else + super(::UDPSocket.new(family)) + end + end + + # We pass `send` through directly, but in theory it might block. Internally, it uses sendto. + def_delegators :@io, :send, :connect + + # This function is so fucked. Why does `UDPSocket#recvfrom` return the remote address as an array, but `Socket#recfrom` return it as an `Addrinfo`? You should prefer `recvmsg`. + wrap_blocking_method :recvfrom, :recvfrom_nonblock + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/unix_endpoint.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/unix_endpoint.rb new file mode 100644 index 0000000..a123bbc --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/unix_endpoint.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +# Copyright, 2019, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'address_endpoint' + +module Async + module IO + # This class doesn't exert ownership over the specified unix socket and ensures exclusive access by using `flock` where possible. + class UNIXEndpoint < AddressEndpoint + def initialize(path, type, **options) + # I wonder if we should implement chdir behaviour in here if path is longer than 104 characters. + super(Address.unix(path, type), **options) + + @path = path + end + + def to_s + "\#<#{self.class} #{@path.inspect}>" + end + + attr :path + + def bound? + self.connect do + return true + end + rescue Errno::ECONNREFUSED + return false + end + + def bind(&block) + Socket.bind(@address, **@options, &block) + rescue Errno::EADDRINUSE + # If you encounter EADDRINUSE from `bind()`, you can check if the socket is actually accepting connections by attempting to `connect()` to it. If the socket is still bound by an active process, the connection will succeed. Otherwise, it should be safe to `unlink()` the path and try again. + if !bound? && File.exist?(@path) + File.unlink(@path) + retry + else + raise + end + end + end + + class Endpoint + # @param path [String] + # @param type Socket type + # @param options keyword arguments passed through to {UNIXEndpoint#initialize} + # + # @return [UNIXEndpoint] + def self.unix(path = "", type = ::Socket::SOCK_STREAM, **options) + UNIXEndpoint.new(path, type, **options) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/unix_socket.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/unix_socket.rb new file mode 100644 index 0000000..051cccc --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/unix_socket.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative 'socket' + +module Async + module IO + class UNIXSocket < BasicSocket + # `send_io`, `recv_io` and `recvfrom` may block but no non-blocking implementation available. + wraps ::UNIXSocket, :path, :addr, :peeraddr, :send_io, :recv_io, :recvfrom + + include Peer + end + + class UNIXServer < UNIXSocket + wraps ::UNIXServer, :listen + + def accept + peer = async_send(:accept_nonblock) + wrapper = UNIXSocket.new(peer, self.reactor) + + return wrapper unless block_given? + + begin + yield wrapper + ensure + wrapper.close + end + end + + alias sysaccept accept + alias accept_nonblock accept + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/version.rb b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/version.rb new file mode 100644 index 0000000..8448264 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-io-1.34.0/lib/async/io/version.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +# Copyright, 2017, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +module Async + module IO + VERSION = "1.34.0" + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-pool-0.3.12/lib/async/pool.rb b/vendor/bundle/ruby/2.7.0/gems/async-pool-0.3.12/lib/async/pool.rb new file mode 100644 index 0000000..e134e64 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-pool-0.3.12/lib/async/pool.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. + +require_relative 'pool/version' +require_relative 'pool/controller' diff --git a/vendor/bundle/ruby/2.7.0/gems/async-pool-0.3.12/lib/async/pool/controller.rb b/vendor/bundle/ruby/2.7.0/gems/async-pool-0.3.12/lib/async/pool/controller.rb new file mode 100644 index 0000000..4fb91e5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-pool-0.3.12/lib/async/pool/controller.rb @@ -0,0 +1,305 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. +# Copyright, 2020, by Simon Perepelitsa. + +require 'console/logger' + +require 'async' +require 'async/notification' +require 'async/semaphore' + +module Async + module Pool + class Controller + def self.wrap(**options, &block) + self.new(block, **options) + end + + def initialize(constructor, limit: nil, concurrency: nil) + # All available resources: + @resources = {} + + # Resources which may be available to be acquired: + # This list may contain false positives, or resources which were okay but have since entered a state which is unusuable. + @available = [] + + @notification = Async::Notification.new + + @limit = limit + + @constructor = constructor + + # Set the concurrency to be the same as the limit for maximum performance: + if limit + concurrency ||= limit + else + concurrency ||= 1 + end + + @guard = Async::Semaphore.new(concurrency) + + @gardener = nil + end + + # @attribute [Hash(Resource, Integer)] all allocated resources, and their associated usage. + attr :resources + + def size + @resources.size + end + + # Whether the pool has any active resources. + def active? + !@resources.empty? + end + + # Whether there are resources which are currently in use. + def busy? + @resources.collect do |_, usage| + return true if usage > 0 + end + + return false + end + + # Whether there are available resources, i.e. whether {#acquire} can reuse an existing resource. + def available? + @available.any? + end + + # Wait until a pool resource has been freed. + def wait + @notification.wait + end + + def empty? + @resources.empty? + end + + def acquire + resource = wait_for_resource + + return resource unless block_given? + + begin + yield resource + ensure + release(resource) + end + end + + # Make the resource resources and let waiting tasks know that there is something resources. + def release(resource) + processed = false + + # A resource that is not good should also not be reusable. + if resource.reusable? + processed = reuse(resource) + end + ensure + retire(resource) unless processed + end + + def close + @available.clear + + while pair = @resources.shift + resource, usage = pair + + if usage > 0 + Console.logger.warn(self, resource: resource, usage: usage) {"Closing resource while still in use!"} + end + + resource.close + end + + @gardener&.stop + end + + def to_s + if @resources.empty? + "\#<#{self.class}(#{usage_string})>" + else + "\#<#{self.class}(#{usage_string}) #{availability_string}>" + end + end + + # Retire (and close) all unused resources. If a block is provided, it should implement the desired functionality for unused resources. + # @param retain [Integer] the minimum number of resources to retain. + # @yield resource [Resource] unused resources. + def prune(retain = 0) + unused = [] + + # This code must not context switch: + @resources.each do |resource, usage| + if usage.zero? + unused << resource + end + end + + # It's okay for this to context switch: + unused.each do |resource| + if block_given? + yield resource + else + retire(resource) + end + + break if @resources.size <= retain + end + + # Update availability list: + @available.clear + @resources.each do |resource, usage| + if usage < resource.concurrency and resource.reusable? + @available << resource + end + end + + return unused.size + end + + def retire(resource) + Console.logger.debug(self) {"Retire #{resource}"} + + @resources.delete(resource) + + resource.close + + @notification.signal + + return true + end + + protected + + def start_gardener + return if @gardener + + Async(transient: true, annotation: "#{self.class} Gardener") do |task| + @gardener = task + + Task.yield + ensure + @gardener = nil + self.close + end + end + + def usage_string + "#{@resources.size}/#{@limit || '∞'}" + end + + def availability_string + @resources.collect do |resource,usage| + "#{usage}/#{resource.concurrency}#{resource.viable? ? nil : '*'}/#{resource.count}" + end.join(";") + end + + def usage + @resources.count{|resource, usage| usage > 0} + end + + def free + @resources.count{|resource, usage| usage == 0} + end + + def reuse(resource) + Console.logger.debug(self) {"Reuse #{resource}"} + usage = @resources[resource] + + if usage.zero? + raise "Trying to reuse unacquired resource: #{resource}!" + end + + # If the resource was fully utilized, it now becomes available: + if usage == resource.concurrency + @available.push(resource) + end + + @resources[resource] = usage - 1 + + @notification.signal + + return true + end + + def wait_for_resource + # If we fail to create a resource (below), we will end up waiting for one to become resources. + until resource = available_resource + @notification.wait + end + + Console.logger.debug(self) {"Wait for resource -> #{resource}"} + + # if resource.concurrency > 1 + # @notification.signal + # end + + return resource + end + + # @returns [Object] A new resource in a "used" state. + def create_resource + self.start_gardener + + # This might return nil, which means creating the resource failed. + if resource = @constructor.call + @resources[resource] = 1 + + # Make the resource available if it can be used multiple times: + if resource.concurrency > 1 + @available.push(resource) + end + end + + return resource + end + + # @returns [Object] An existing resource in a "used" state. + def available_resource + resource = nil + + @guard.acquire do + resource = get_resource + end + + return resource + rescue Exception + reuse(resource) if resource + raise + end + + private def get_resource + while resource = @available.last + if usage = @resources[resource] and usage < resource.concurrency + if resource.viable? + usage = (@resources[resource] += 1) + + if usage == resource.concurrency + # The resource is used up to it's limit: + @available.pop + end + + return resource + else + retire(resource) + @available.pop + end + else + # The resource has been removed already, so skip it and remove it from the availability list. + @available.pop + end + end + + if @limit.nil? or @resources.size < @limit + Console.logger.debug(self) {"No available resources, allocating new one..."} + + return create_resource + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-pool-0.3.12/lib/async/pool/resource.rb b/vendor/bundle/ruby/2.7.0/gems/async-pool-0.3.12/lib/async/pool/resource.rb new file mode 100644 index 0000000..026c38a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-pool-0.3.12/lib/async/pool/resource.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. + +require 'console/logger' + +require 'async/notification' +require 'async/semaphore' + +module Async + module Pool + # The basic interface required by a pool resource. + class Resource + # Constructs a resource. + def self.call + self.new + end + + def initialize(concurrency = 1) + @concurrency = concurrency + @closed = false + @count = 0 + end + + # @attr [Integer] The concurrency of this resource, 1 (singleplex) or more (multiplex). + attr :concurrency + + # @attr [Integer] The number of times this resource has been used. + attr :count + + # Whether this resource can be acquired. + # @return [Boolean] whether the resource can actually be used. + def viable? + !@closed + end + + # Whether the resource has been closed by the user. + # @return [Boolean] whether the resource has been closed or has failed. + def closed? + @closed + end + + # Close the resource explicitly, e.g. the pool is being closed. + def close + @closed = true + end + + # Whether this resource can be reused. Used when releasing resources back into the pool. + def reusable? + !@closed + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-pool-0.3.12/lib/async/pool/version.rb b/vendor/bundle/ruby/2.7.0/gems/async-pool-0.3.12/lib/async/pool/version.rb new file mode 100644 index 0000000..d4480a2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-pool-0.3.12/lib/async/pool/version.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. + +module Async + module Pool + VERSION = "0.3.12" + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/async-pool-0.3.12/license.md b/vendor/bundle/ruby/2.7.0/gems/async-pool-0.3.12/license.md new file mode 100644 index 0000000..3fafea7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-pool-0.3.12/license.md @@ -0,0 +1,23 @@ +# MIT License + +Copyright, 2019-2022, by Samuel Williams. +Copyright, 2020, by Simon Perepelitsa. +Copyright, 2021, by Olle Jonsson. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/bundle/ruby/2.7.0/gems/async-pool-0.3.12/readme.md b/vendor/bundle/ruby/2.7.0/gems/async-pool-0.3.12/readme.md new file mode 100644 index 0000000..c50bba9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/async-pool-0.3.12/readme.md @@ -0,0 +1,54 @@ +# Async::Pool + +Provides support for connection pooling both singleplex and multiplex resources. + +[![Development Status](https://github.com/socketry/async-pool/workflows/Test/badge.svg)](https://github.com/socketry/async-pool/actions?workflow=Test) + +## Installation + +Add this line to your application's Gemfile: + +``` ruby +gem 'async-pool' +``` + +And then execute: + +``` bash +$ bundle +``` + +Or install it yourself as: + +``` bash +$ gem install async-pool +``` + +## Usage + +`Async::Pool::Controller` provides support for both singleplex (one stream at a time) and multiplex resources (multiple streams at a time). + +`Async::Pool::Resource` is provided as an interface and to document how to use the pools. However, you wouldn't need to use this in practice and just implement the appropriate interface on your own objects. + +``` ruby +pool = Async::Pool::Controller.new(Async::Pool::Resource) + +pool.acquire do |resource| + # resource is implicitly released when exiting the block. +end + +resource = pool.acquire + +# Return the resource back to the pool: +pool.release(resource) +``` + +## Contributing + +We welcome contributions to this project. + +1. Fork it. +2. Create your feature branch (`git checkout -b my-new-feature`). +3. Commit your changes (`git commit -am 'Add some feature'`). +4. Push to the branch (`git push origin my-new-feature`). +5. Create new Pull Request. diff --git a/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/.gemtest b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/.gemtest new file mode 100644 index 0000000..e69de29 diff --git a/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/.github/workflows/test.yml b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/.github/workflows/test.yml new file mode 100644 index 0000000..17f5852 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/.github/workflows/test.yml @@ -0,0 +1,103 @@ +name: Test + +on: + push: + branches: [ master ] + schedule: + - cron: '0 0 11,25 * *' # roughly every two weeks to run on new Ruby versions + pull_request: + branches: [ master ] + workflow_dispatch: + +jobs: + test: + name: "Unit" + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + ruby: + - "2.1" + - "2.2" + - "2.3" + - "2.4" + - "2.5" + - "2.6" + - "2.7" + - "3.0" + + steps: + + - uses: actions/checkout@v2 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + + - name: Test + run: bundle exec rspec -f doc + + system: + name: "System" + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - macos-latest + - ubuntu-latest + - windows-latest + ruby: + - "2" + - "3.0" + - "jruby" + - "truffleruby" + exclude: + # Windows releases of jruby and truffleruby have issues. Skip them for now. + - { ruby: "jruby", os: "windows-latest" } + - { ruby: "truffleruby", os: "windows-latest" } + + steps: + + - uses: actions/checkout@v2 + + - name: Determine ruby version name + id: ruby_version + run: | + if [[ $OS == 'windows-latest' && $RUBY == '3.0' ]]; then + # Windows doesn't have 3.0, so run head there but nowhere else. + echo "::set-output name=release::head" + else + echo "::set-output name=release::$RUBY" + fi + shell: bash + env: + OS: ${{ matrix.os }} + RUBY: ${{ matrix.ruby }} + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ steps.ruby_version.outputs.release }} + bundler-cache: true + + - name: Install gem + run: bundle exec rake install + + - name: Create directory for gem test + run: mkdir -p tmp/gem-test + + - name: Create test Gemfile + run: echo "gem 'binding_of_caller'" > Gemfile + working-directory: ./tmp/gem-test + + - name: Test gem load + run: bundle exec ruby -e "require 'binding_of_caller'" + + - name: Test gem functionality + if: ${{ matrix.ruby != 'jruby' && matrix.ruby != 'truffleruby' }} + run: bundle exec ruby -e "require 'binding_of_caller'; binding.of_caller(0).eval('var = :hello')" + env: + JRUBY_OPTS: "--dev" # This will support JRuby once the gem is updated to support JRuby 9.x diff --git a/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/.gitignore b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/.gitignore new file mode 100644 index 0000000..01e9c6a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/.gitignore @@ -0,0 +1,13 @@ +Gemfile.lock + +/.bundle/ +/.yardoc +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +/tmp/ + +# rspec failure tracking +.rspec_status diff --git a/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/.yardopts b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/.yardopts new file mode 100644 index 0000000..9c0d33f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/.yardopts @@ -0,0 +1 @@ +--markup markdown diff --git a/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/Gemfile b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/Gemfile new file mode 100644 index 0000000..187d250 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/Gemfile @@ -0,0 +1,7 @@ +source "https://rubygems.org" + +# Specify your gem's dependencies in binding_of_caller.gemspec +gemspec + +gem "rake" +gem "rspec" diff --git a/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/HISTORY b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/HISTORY new file mode 100644 index 0000000..5dd79bb --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/HISTORY @@ -0,0 +1,35 @@ +## 0.7.3.pre1 / 2014-08-20 + +This release includes compatibility fixes for different ruby versions +and some minor enhancements. + +* C Extensions are only compiled on MRI 1.9.x. + + For MRI >= 2 the native API is used, so this should speedup + installation. + + For JRuby this will avoid crashes on gem installation. + + This was alredy being checked for Rubinius. + + *Amadeus Folego* + +* Added experimental JRuby support for 1.7.x series. + + Only the main API is implemented and `Binding#eval` is + monkey-patched as it is private on JRuby. + + This requires the compiler to be run on interpreted mode, + otherwise an exception will be thrown when `of_caller` is called. + + *Charles Nutter* + +* Remove executability from non-executable files. + + *David Celis* + +* Test and notice MRI 2.1 as a working Ruby implementation. + + *Lennart Fridén* + +## 0.7.2 / 2013-06-07 diff --git a/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/LICENSE b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/LICENSE new file mode 100644 index 0000000..acb63c7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/LICENSE @@ -0,0 +1,25 @@ +License +------- + +(The MIT License) + +Copyright (c) 2011 John Mair (banisterfiend) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/README.md b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/README.md new file mode 100644 index 0000000..671dbb2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/README.md @@ -0,0 +1,100 @@ +[![Build Status](https://github.com/banister/binding_of_caller/workflows/Test/badge.svg?branch=master&event=push)](https://github.com/banister/binding_of_caller/actions?query=branch%3Amaster) +[![Gem Version](https://img.shields.io/gem/v/binding_of_caller.svg)](https://rubygems.org/gems/binding_of_caller) + +binding_of_caller +================= + +(C) John Mair (banisterfiend) 2012 + +_Retrieve the binding of a method's caller in MRI (>= 2.0.0) and RBX (Rubinius)_ + +The `binding_of_caller` gem provides the `Binding#of_caller` method. + +Using `binding_of_caller` we can grab bindings from higher up the call +stack and evaluate code in that context. Allows access to bindings arbitrarily far up the +call stack, not limited to just the immediate caller. + +**Recommended for use only in debugging situations. Do not use this in production apps.** + +**Works in MRI Ruby (>= 2.0) and RBX (Rubinius)** + +* Install the [gem](https://rubygems.org/gems/binding_of_caller): `gem install binding_of_caller` +* See the [source code](http://github.com/banister/binding_of_caller) + +Example: Modifying a local inside the caller of a caller +-------- + +```ruby +def a + var = 10 + b + puts var +end + +def b + c +end + +def c + binding.of_caller(2).eval('var = :hello') +end + +a() + +# OUTPUT +# => hello +``` + +Spinoff project +------- + +This project is a spinoff from the [Pry REPL project.](http://pry.github.com) + +Features and limitations +------------------------- + +* Works in MRI (>= 2.0.0) and RBX (Rubinius) +* For MRI 1.9.x, use version "~> 0.8" of the gem, which included support for MRI before 2.0. +* Does not work in 1.8.7, but there is a well known (continuation-based) hack to get a `Binding#of_caller` there. +* There is experimental support for jruby 1.7.x, but it only works in interpreted +mode (i.e. use the option `-Djruby.compile.mode=OFF` or append +`compile.mode=OFF` to your `.jrubyrc`) + +Development +----------- + +After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. + +To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). + +Contact +------- + +Problems or questions contact me at [github](http://github.com/banister) + + +License +------- + +(The MIT License) + +Copyright (c) 2012 (John Mair) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/Rakefile b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/Rakefile new file mode 100644 index 0000000..b7e9ed5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/Rakefile @@ -0,0 +1,6 @@ +require "bundler/gem_tasks" +require "rspec/core/rake_task" + +RSpec::Core::RakeTask.new(:spec) + +task :default => :spec diff --git a/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/binding_of_caller.gemspec b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/binding_of_caller.gemspec new file mode 100644 index 0000000..a40068f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/binding_of_caller.gemspec @@ -0,0 +1,35 @@ +require_relative 'lib/binding_of_caller/version' + +Gem::Specification.new do |spec| + spec.name = "binding_of_caller" + spec.version = BindingOfCaller::VERSION + spec.authors = ["John Mair (banisterfiend)"] + spec.email = ["jrmair@gmail.com"] + + spec.summary = %q{Retrieve the binding of a method's caller, or further up the stack.} + spec.description = <<-TXT +Provides the Binding#of_caller method. + +Using binding_of_caller we can grab bindings from higher up the call stack and evaluate code in that context. +Allows access to bindings arbitrarily far up the call stack, not limited to just the immediate caller. + +Recommended for use only in debugging situations. Do not use this in production apps. +TXT + spec.homepage = "https://github.com/banister/binding_of_caller" + spec.license = "MIT" + spec.required_ruby_version = Gem::Requirement.new(">= 2.0.0") + + spec.metadata = { + "changelog_uri" => "https://github.com/banister/binding_of_caller/releases", + } + + # 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`.split("\x0").reject { |f| f.match(%r{^(test|spec|examples|bin)/}) } + end + + spec.require_paths = ["lib"] + + spec.add_dependency "debug_inspector", ">= 0.0.1" +end diff --git a/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/lib/binding_of_caller.rb b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/lib/binding_of_caller.rb new file mode 100644 index 0000000..e673541 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/lib/binding_of_caller.rb @@ -0,0 +1,13 @@ +require "binding_of_caller/version" + +if defined?(RUBY_ENGINE) && RUBY_ENGINE == "ruby" + if RUBY_VERSION =~ /^[23]/ + require 'binding_of_caller/mri' + else + puts "This version of binding_of_caller doesn't support this version of Ruby" + end +elsif defined?(Rubinius) + require 'binding_of_caller/rubinius' +elsif defined?(JRuby) + require 'binding_of_caller/jruby_interpreted' +end diff --git a/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/lib/binding_of_caller/jruby_interpreted.rb b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/lib/binding_of_caller/jruby_interpreted.rb new file mode 100644 index 0000000..f814ddc --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/lib/binding_of_caller/jruby_interpreted.rb @@ -0,0 +1,123 @@ +module BindingOfCaller + class JRubyBindingHolder + java_import org.jruby.RubyBinding + + def initialize(binding) + @binding = binding + end + + def eval(code, file = nil, line = nil) + b = JRuby.dereference(RubyBinding.new(JRuby.runtime, Binding, @binding)) + if (file == nil) + Kernel.eval code, b + else + Kernel.eval code, b, file, line + end + end + + def frame_type + case + when block? + :block + when eval? + :eval + when top? + :top + else + :method + end + end + + def frame_description + "#{block_desc}#{method_desc}" + end + + private + + def block? + @binding.getDynamicScope().getStaticScope().isBlockScope() + end + + def eval? + @binding.getFrame().getKlazz().nil? && @binding.getLine() != 0 + end + + def top? + @binding.getFrame().getKlazz().nil? && @binding.getLine() == 0 + end + + def block_desc + if frame_type == :block + "block in " + end + end + + def method_desc + @binding.getFrame().getName() || "
" + end + end + + module BindingExtensions + def of_caller(index = 1) + index += 1 # always omit this frame + JRuby.runtime.current_context.binding_of_caller(index) + end + + def callers + ary = [] + n = 2 + while binding = of_caller(n) + ary << binding + n += 1 + end + ary + end + + def frame_count + callers.count - 1 + end + + def frame_type + nil + end + + def frame_description + nil + end + end +end + + +class org::jruby::runtime::ThreadContext + java_import org.jruby.runtime.Binding + java_import org.jruby.RubyInstanceConfig::CompileMode + + field_accessor :frameStack, :frameIndex, + :scopeStack, :scopeIndex, + :backtrace, :backtraceIndex + + def binding_of_caller(index) + unless JRuby.runtime.instance_config.compile_mode == CompileMode::OFF + raise RuntimeError, "caller binding only supported in interpreter" + end + + index += 1 # always omit this frame + + return nil if index > frameIndex + + frame = frameStack[frameIndex - index] + + return binding_of_caller(index - 1) if index > scopeIndex + + scope = scopeStack[scopeIndex - index] + element = backtrace[backtraceIndex - index] + + binding = Binding.new(frame, scope.static_scope.module, scope, element.clone) + + BindingOfCaller::JRubyBindingHolder.new(binding) + end +end + +class ::Binding + include BindingOfCaller::BindingExtensions +end \ No newline at end of file diff --git a/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/lib/binding_of_caller/mri.rb b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/lib/binding_of_caller/mri.rb new file mode 100644 index 0000000..3bc2437 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/lib/binding_of_caller/mri.rb @@ -0,0 +1,63 @@ +require 'debug_inspector' + +module BindingOfCaller + module BindingExtensions + # Retrieve the binding of the nth caller of the current frame. + # @return [Binding] + def of_caller(n) + c = callers.drop(1) + if n > (c.size - 1) + raise "No such frame, gone beyond end of stack!" + else + c[n] + end + end + + # Return bindings for all caller frames. + # @return [Array] + def callers + ary = [] + + RubyVM::DebugInspector.open do |dc| + locs = dc.backtrace_locations + + locs.size.times do |i| + b = dc.frame_binding(i) + if b + b.instance_variable_set(:@iseq, dc.frame_iseq(i)) + ary << b + end + end + end + + ary.drop(1) + end + + # Number of parent frames available at the point of call. + # @return [Fixnum] + def frame_count + callers.size - 1 + end + + # The type of the frame. + # @return [Symbol] + def frame_type + return nil if !@iseq + + # apparently the 9th element of the iseq array holds the frame type + # ...not sure how reliable this is. + @frame_type ||= @iseq.to_a[9] + end + + # The description of the frame. + # @return [String] + def frame_description + return nil if !@iseq + @frame_description ||= @iseq.label + end + end +end + +class ::Binding + include BindingOfCaller::BindingExtensions +end diff --git a/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/lib/binding_of_caller/rubinius.rb b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/lib/binding_of_caller/rubinius.rb new file mode 100644 index 0000000..6cc27bf --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/lib/binding_of_caller/rubinius.rb @@ -0,0 +1,67 @@ +module BindingOfCaller + module BindingExtensions + + # Retrieve the binding of the nth caller of the current frame. + # @return [Binding] + def of_caller(n) + location = Rubinius::VM.backtrace(1 + n, true).first + + raise RuntimeError, "Invalid frame, gone beyond end of stack!" if location.nil? + + setup_binding_from_location(location) + end + + # The description of the frame. + # @return [String] + def frame_description + @frame_description + end + + # Return bindings for all caller frames. + # @return [Array] + def callers + Rubinius::VM.backtrace(1, true).map &(method(:setup_binding_from_location). + to_proc) + end + + # Number of parent frames available at the point of call. + # @return [Fixnum] + def frame_count + Rubinius::VM.backtrace(1).count + end + + # The type of the frame. + # @return [Symbol] + def frame_type + if compiled_code.for_module_body? + :class + elsif compiled_code.for_eval? + :eval + elsif compiled_code.is_block? + :block + else + :method + end + end + + protected + + def setup_binding_from_location(location) + binding = Binding.setup location.variables, + location.variables.method, + location.constant_scope, + location.variables.self, + location + + binding.instance_variable_set :@frame_description, + location.describe.gsub("{ } in", "block in") + + binding + end + end +end + +class ::Binding + include BindingOfCaller::BindingExtensions + extend BindingOfCaller::BindingExtensions +end diff --git a/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/lib/binding_of_caller/version.rb b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/lib/binding_of_caller/version.rb new file mode 100644 index 0000000..912cd82 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/binding_of_caller-1.0.0/lib/binding_of_caller/version.rb @@ -0,0 +1,3 @@ +module BindingOfCaller + VERSION = "1.0.0" +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/CHANGELOG.md b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/CHANGELOG.md new file mode 100644 index 0000000..f36a15a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/CHANGELOG.md @@ -0,0 +1,934 @@ +# Changelog + +## [Unreleased] + +## [11.1.3] - 2020-04-23 + +### Fixed + +* [#674](https://github.com/deivid-rodriguez/byebug/pull/674): crash when using byebug on ruby 2.7.0 on Windows. + +## [11.1.2] - 2020-04-17 + +### Fixed + +* [#657](https://github.com/deivid-rodriguez/byebug/pull/657): crash when hitting \ due to IRB completion mechanism included in the default ruby 2.7 version of the `irb` gem ([@terceiro]). + +## [11.1.1] - 2020-01-24 + +### Fixed + +* [#635](https://github.com/deivid-rodriguez/byebug/pull/635): usage on Fedora 31 or any other `byebug` installation performed by a `rubygems` copy customized by setting `Gem.install_extension_in_lib` to false. + +## [11.1.0] - 2020-01-20 + +### Added + +* Official support for MRI 2.7 ([@yui-knk]). + +### Fixed + +* [#562](https://github.com/deivid-rodriguez/byebug/pull/562): post mortem mode landing in the wrong line when coming from an exception inside a `Kernel.load` call. + +### Removed + +* Support for MRI 2.3. Byebug no longer installs on this platform. + +## [11.0.1] - 2019-03-18 + +### Fixed + +* [#546](https://github.com/deivid-rodriguez/byebug/pull/546): `continue!` to ignore further `byebug` calls. +* [#545](https://github.com/deivid-rodriguez/byebug/pull/545): `skip` autolisting code for intermediate skipped breakpoints. + +## [11.0.0] - 2019-02-15 + +### Added + +* [#377](https://github.com/deivid-rodriguez/byebug/pull/377): `skip` to continue until the next breakpoint as long as it is different from the current one. You can use this command to get out of loops, for example ([@tacnoman]). +* [#524](https://github.com/deivid-rodriguez/byebug/pull/524): `continue!` (or `continue unconditionally`) to continue until the end of the program regardless of the currently enabled breakpoints ([@tacnoman]). + +### Fixed + +* [#527](https://github.com/deivid-rodriguez/byebug/pull/527): `break` help text to clarify placeholders from literals. +* [#528](https://github.com/deivid-rodriguez/byebug/pull/528): `quit!` help to not show a space between "quit" and "!". + +### Removed + +* Support for MRI 2.2. Byebug no longer installs on this platform. + +## [10.0.2] - 2018-03-30 + +### Fixed + +* [#447](https://github.com/deivid-rodriguez/byebug/pull/447): Error when using byebug with `debase` gem ([@tzmfreedom]). + +## [10.0.1] - 2018-03-21 + +### Fixed + +* [#443](https://github.com/deivid-rodriguez/byebug/pull/443): Error when using byebug with `debase` gem ([@tzmfreedom]). + +## [10.0.0] - 2018-01-26 + +### Changed + +* Breaking on methods now stops on the first effective line of a method, not on the line containing the `def` keyword. + +### Added + +* [#393](https://github.com/deivid-rodriguez/byebug/pull/393): Show valid breakpoint locations when invalid location given ([@ko1]). +* [#397](https://github.com/deivid-rodriguez/byebug/pull/397): Ruby 2.5.0 support ([@yui-knk]). +* Log host & port when launching byebug's client in remote mode. +* [#82](https://github.com/deivid-rodriguez/byebug/issues/82): Some love & tests to remote debugging. +* [#141](https://github.com/deivid-rodriguez/byebug/issues/141): `remote_byebug` shortcut to start the most common case for remote debugging. + +### Fixed + +* [#419](https://github.com/deivid-rodriguez/byebug/pull/419): Properly ignore ruby fullpath executable when passed to byebug script. +* [#141](https://github.com/deivid-rodriguez/byebug/issues/141): Remote server crash when interrupting client. +* [#274](https://github.com/deivid-rodriguez/byebug/issues/274): Remote server crash when interrupting client. +* [#239](https://github.com/deivid-rodriguez/byebug/issues/239): Control server thread being able to `interrupt` main thread only a single time. + +## [9.1.0] - 2017-08-22 + +### Added + +* Better UI messages for breakpoint management. + +### Fixed + +* `where` command failing on instance_exec block stack frames. +* [#321](https://github.com/deivid-rodriguez/byebug/pull/321): `restart` command crashing in certain cases because of a missing `require "English"` ([@akaneko3]). +* [#321](https://github.com/deivid-rodriguez/byebug/pull/321): `restart` command crashing when debugged script is not executable or has no shebang ([@akaneko3]). + +### Removed + +* Ruby 2.0 and Ruby 2.1 official & unofficial support. Byebug no longer installs on these platforms. + +## [9.0.6] - 2016-09-29 + +### Fixed + +* [#241](https://github.com/deivid-rodriguez/byebug/issues/241): Error when using `byebug` with a ruby compiled against libedit. +* [#277](https://github.com/deivid-rodriguez/byebug/pull/277): Allow `Byebug.start_server` to yield the block passed to it when the actual port is already known ([@cben]). +* [#275](https://github.com/deivid-rodriguez/byebug/pull/275): Use a standard license name so it can be more reliably used by tools. + +## [9.0.5] - 2016-05-28 + +### Fixed + +* Error loading rc file when `ENV["HOME"]` is unset. + +## [9.0.4] - 2016-05-19 + +### Fixed + +* Errors in rc file not being displayed to the user. + +## [9.0.3] - 2016-05-16 + +### Fixed + +* [#256](https://github.com/deivid-rodriguez/byebug/issues/256): Unfriendly output in byebug's executable when no script specified. +* Unfriendly output in byebug's executable when script doesn't exist. +* Unfriendly output in byebug's executable when script has invalid code. + +## [9.0.2] - 2016-05-15 + +### Fixed + +* [#263](https://github.com/deivid-rodriguez/byebug/pull/263): Skip to get a line in eval context ([@k0kubun]). +* [#264](https://github.com/deivid-rodriguez/byebug/pull/264): Debugger getting disabled after `continue` even when linetrace is enabled ([@k0kubun]). + +## [9.0.1] - 2016-05-14 + +### Fixed + +* [#201](https://github.com/deivid-rodriguez/byebug/issues/201): `quit` never exiting when remote debugging. + +## [9.0.0] - 2016-05-11 + +### Fixed + +* `irb` command unintentionally changing $PROGRAM_NAME. +* `pry` command failing. +* Unrelated error message when using `pry` command and Pry not installed. +* [#239](https://github.com/deivid-rodriguez/byebug/issues/239): Interrupting program execution from remote control interface ([@izaera]). + +### Removed + +* Official Ruby 2.0.0 support. `var local` no longer works in Ruby 2.0. The rest of the commands should still work as before, but `byebug` is no longer tested against this version so they might start breaking in the future. + +## [8.2.5] - 2016-04-27 + +### Fixed + +* [#244](https://github.com/deivid-rodriguez/byebug/pull/244): Allows paths with spaces ([@HookyQR]). +* [#244](https://github.com/deivid-rodriguez/byebug/pull/244): Allows paths with colons ([@HookyQR]). + +## [8.2.4] - 2016-04-08 + +### Fixed + +* Reverts [#211](https://github.com/deivid-rodriguez/byebug/pull/211) which leads to an unusable debugger. + +## [8.2.3] - 2016-04-07 + +### Fixed + +* Better interaction with utilities like RSpec when hitting Ctrl-C. +* [#197](https://github.com/deivid-rodriguez/byebug/issues/197): `irb` command when original program modified ARGV ([@josephks]). +* [#211](https://github.com/deivid-rodriguez/byebug/pull/211): Unusable debugger when stdin redirected ([@sethk]). +* [#223](https://github.com/deivid-rodriguez/byebug/issues/223): RC file loading when no explicit flag included. +* [#175](https://github.com/deivid-rodriguez/byebug/issues/175): Installation on some Windows systems. +* [#226](https://github.com/deivid-rodriguez/byebug/issues/226): Installation on some Windows systems. + +## [8.2.2] - 2016-02-01 + +### Fixed + +* Bug in rc file loading where most initialization commands would not be run. + +## [8.2.1] - 2015-11-26 + +### Fixed + +* Bug in evaluations using "eval". + +## [8.2.0] - 2015-11-12 + +### Fixed + +* [#184](https://github.com/deivid-rodriguez/byebug/issues/184): Due to the way of running evaluations in a separate thread. +* [#188](https://github.com/deivid-rodriguez/byebug/issues/188): Due to the way of running evaluations in a separate thread. + +### Added + +* `debug` command to evaluate things in a separate thread, since this behavior was removed from default `eval` to fix the above issues. + +## [8.1.0] - 2015-11-09 + +### Fixed + +* Command history should be specific per project. +* Better error message in certain edge cases when printing the backtrace. +* Bug in evaluator which would show information about having stopped at a breakpoint in some cases. + +### Added + +* Ability to autolist source code after `frame` command. +* Ability to stop at lines where methods return. + +## [8.0.1] - 2015-11-07 + +### Fixed + +* Error stream wouldn't be properly reset when using standalone `byebug`. +* Confusing error message for invalid breakpoint locations. + +## [8.0.0] - 2015-11-05 + +### Fixed + +* [#183](https://github.com/deivid-rodriguez/byebug/issues/183). Compilation in Ruby 2.0. Regression introduced in [7.0.0]. +* "Return value is: nil" would be displayed when stopping right before the end of a class definition. We want to avoid showing anything instead. + +### Changed + +* Plugins now need to implement an `at_end` method (separate from `at_return`) in their custom processors. + +## [7.0.0] - 2015-11-04 + +### Fixed + +* [#177](https://github.com/deivid-rodriguez/byebug/issues/177). Some issues with formatting results of evaluations. +* [#144](https://github.com/deivid-rodriguez/byebug/issues/144). Ruby process after using byebug does no longer get slow. +* [#121](https://github.com/deivid-rodriguez/byebug/issues/121). `byebug` commands inside code evaluated from debugger's prompt are now properly working. +* Another evaluation bug in autocommands. +* `finish 0` command would sometimes fail to stop right before exiting the current frame. +* Runner's `--[no-]stop` option now works ([@windwiny]). +* Change variable name `bool`, avoid conflict clang's predefined macro. + +### Removed + +* `ps` command. + +### Changed + +* [#166](https://github.com/deivid-rodriguez/byebug/issues/166). Don't load the entire library on require, but only when a `byebug` call is issued ([@bquorning]). +* The above fix to the `finish 0` command cause `byebug`'s entrypoint to require 3 steps out instead of 2. In general, plugins using `Byebug::Context.step_out` will need to be changed to consider "c return events" as well. + +### Added + +* `autopry` setting that calls `pry` on every stop. +* Return value information to debugger's output when `finish 0` is used. + +## [6.0.2] - 2015-08-20 + +### Fixed + +* The user should always be given back a prompt unless (s)he explicitly states the opposite. This provides a more general fix to the bug resolved in [6.0.1]. + +## [6.0.1] - 2015-08-19 + +### Fixed + +* Bug in evaluation where the user would lose the command prompt when entering an expression with a syntax error. + +## [6.0.0] - 2015-08-17 + +### Removed + +* `autoeval` setting. I haven't heard of anyone setting it to false. +* `pp`, `putl`, `eval`. People just want to evaluate Ruby code, so the less magic the better. Most of the people probably were not aware that `byebug` was overriding stuff like `pp` or `eval`. Only keeping `ps` as the single "enhanced evaluation" command. +* `verbose` setting. +* `info catch` command. Use `catch` without arguments instead. +* `R` command alias for `restart`. + +### Changed + +* `info args` is now `var args`. +* `interrupt` is now aliased to `int`, not to `i`. +* API to define custom commands and subcommands (see the Command class). + +### Fixed + +* [#140](https://github.com/deivid-rodriguez/byebug/issues/140). `help` command not showing the list of available commands and their descriptions. +* [#147](https://github.com/deivid-rodriguez/byebug/issues/147). Setting breakpoints at symlinked files. + +### Added + +* API to define custom command processors (see the CommandProcessor class). + +## [5.0.0] - 2015-05-18 + +### Fixed + +* [#136](https://github.com/deivid-rodriguez/byebug/issues/136). `frame` command not working with negative numbers ([@ark6]). + +### Added + +* IDE support and a new command/subcommand API for plugins. +* Add a "savefile" setting holding the file where "save" command saves current debugger's state. + +### Changed + +* `disable` no longer disable all breakpoints, it just shows command's help instead. To disable all breakpoints now you need to do `disable breakpoints` (or `dis b`). Similarly, you can't no longer use `dis 1 2 3` but need to do `dis b 1 2 3` to disable specific breakpoints. The same applies to the `enable` command. + +### Removed + +* `help set ` no longer works. `help set` includes that same output and it's not verbose enough so that this is a problem. Same with `help show `. + +## [4.0.5] - 2015-04-02 + +### Fixed + +* [#131](https://github.com/deivid-rodriguez/byebug/issues/131). +* Thread commands help format should be consistent with the rest of the help system now. + +## [4.0.4] - 2015-03-27 + +### Fixed + +* [#127](https://github.com/deivid-rodriguez/byebug/issues/127). + +## [4.0.3] - 2015-03-19 + +### Fixed + +* Unused variable warning in `context.c`. + +## [4.0.2] - 2015-03-16 + +### Fixed + +* [#118](https://github.com/deivid-rodriguez/byebug/issues/118). Remove `rb-readline` as a dependency and show a help message whenever requiring `readline` fails instead. + +## [4.0.1] - 2015-03-13 + +### Fixed + +* .yml files needed for printers support were missing from the release... :S +* [#118](https://github.com/deivid-rodriguez/byebug/issues/118). Add `readline` as a dependency. + +## [4.0.0] - 2015-03-13 + +### Added + +* `untracevar` command that stops tracing a global variable. +* Window CI build through AppVeyor. +* OSX CI build through Travis. +* Style enforcement through RuboCop. +* C style enforment using the `indent` command line utility. +* Some remote debugging tests ([@eric-hu]). +* Printer's support ([@astashov]). + +### Changed + +* A lot of internal refactoring. +* `tracevar` now requires the full global variable name (with "$"). +* [#92](https://github.com/deivid-rodriguez/byebug/issues/92). The `catch` command is not allowed in post_mortem mode anymore. It was not working anyways. +* [#85](https://github.com/deivid-rodriguez/byebug/issues/85). `step` is now more user friendly when used in combination with `up`. +* `var const` can now be called without an argument and will show constants in the current scope. +* `break` with a class name now creates breakpoints regardless of class not being yet defined. If that's the case, it gives a warning but the class is created anyways. + +### Fixed + +* Code reloading issues. +* `set fullpath` was not showing fullpaths. Now it is. +* [#93](https://github.com/deivid-rodriguez/byebug/issues/93): `up`, `down` and `frame` commands now work in post_mortem mode. +* rc file (`.byebugrc`) loading: invalid commands are just ignored instead of aborting, global (home) rc file is now properly loaded before project's file. +* [#93](https://github.com/deivid-rodriguez/byebug/issues/93). Backtraces not working in `post_mortem` mode. +* 'cmd1 ; cmd2 ; ...; cmdN' syntax which allows running several commands sequentially. +* [#101](https://github.com/deivid-rodriguez/byebug/issues/101). `finish` command not stopping at the correct line. +* [#106](https://github.com/deivid-rodriguez/byebug/issues/106). `break` with namespaced class, like `break A::B#c` should now work. +* Command history is now persisted before exiting byebug. +* Setting breakpoint in a method would stop not only at the beginning of the method but also at the beginning of every block inside the method. +* [#122](https://github.com/deivid-rodriguez/byebug/issues/122). Setting breakpoints on module methods ([@x-yuri]). + +### Removed + +* `autoreload` setting as it's not necessary anymore. Code should always be up to date. +* `reload` command for the same reason. +* Gem dependency on `debugger-linecache`. +* `step+`, `step-`, `next+`, `next-`, `set/show linetrace_plus` and `set/show forcestep` commands. These were all mechanisms to deal with TracePoint API event dupplication, but this duplicated events have been completely removed from the API since [r48609]( bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/48609), so they are no longer necessary. +* `info file` subcommands: `info file breakpoints`, `info file mtime`, `info file sha1`, `info file all`. Now all information is listed under `info file`. +* `testing` setting. It was just a hack to be able to test `byebug`. Nobody was supposed to actually use it! +* `var class` command, just use Ruby (`self.class.class_variables`). +* `p` command, just use `eval`, or just type your expression and `byebug` will autoevaluate it. +* `exit` alias for `quit`. + +## [3.5.1] - 2014-09-29 + +### Fixed + +* [#79](https://github.com/deivid-rodriguez/byebug/issues/79). Windows installation. +* `condition` command not properly detecting invalid breakpoint ids. + +## [3.5.0] - 2014-09-28 + +### Fixed + +* [#81](https://github.com/deivid-rodriguez/byebug/issues/81). Byebug's history messing up other programs using Readline. +* Readline's history not being properly saved and inmediately available. +* User not being notified when trying to debug a non existent script. + +### Changed + +* Complete rewrite of byebug's history. +* Complete rewrite of list command. +* Docs about stacktrace related commands (`up`, `down`, `frame`, `backtrace`). + +## [3.4.2] - 2014-09-26 + +### Fixed + +* [#67](https://github.com/deivid-rodriguez/byebug/issues/67). Debugging commands invoked by ruby executable, as in `byebug -- ruby -Itest a_test.rb -n test_something`. + +## [3.4.1] - 2014-09-25 + +### Fixed + +* [#54](https://github.com/deivid-rodriguez/byebug/issues/54). Use of threads inside `eval` command. +* `list` command not listing backwards after reaching the end of the file. + +## [3.4.0] - 2014-09-01 + +### Fixed + +* deivid-rodriguez/pry-byebug#32 in a better way. + +## [3.3.0] - 2014-08-28 + +### Fixed + +* `set verbose` command. +* `set post_mortem false` command. +* Debugger stopping in `byebug`'s internal frames in some cases. +* `backtrace` crashing when `fullpath` setting disabled and calculated stack size being smaller than the real one. + +### Changed + +* The `-t` option for `bin/byebug` now turns tracing on whereas the `-x` option tells byebug to run the initialization file (.byebugrc) on startup. This is the default behaviour though. +* `bin/byebug` libified and tests added. + +### Removed + +* `info locals` command. Use `var local` instead. +* `info instance_variables` command. Use `var instance` instead. +* `info global_variables` command. Use `var global` instead. +* `info variables` command. Use `var all` instead. +* `irb` command stepping capabilities, see [8e226d0](https://github.com/deivid-rodriguez/byebug/commit/8e226d0). +* `script` and `restart-script` options for `bin/byebug`. + +## [3.2.0] - 2014-08-02 + +### Fixed + +* [#71](https://github.com/deivid-rodriguez/byebug/issues/71). Remote debugging ([@shuky19]). +* [#69](https://github.com/deivid-rodriguez/byebug/issues/69). `source` command ([@Olgagr]). + +### Removed + +* `post_mortem` activation through `Byebug.post_mortem`. Use `set post_mortem` instead. +* `info stack` command. Use `where` instead. +* `method iv` command. Use `var instance` instead. +* [#77](https://github.com/deivid-rodriguez/byebug/issues/77). Warning. + +## [3.1.2] - 2014-04-23 + +### Fixed + +* `post_mortem` mode in `bin/byebug` (really). +* Line tracing in `bin/byebug`. + +## [3.1.1] - 2014-04-23 + +### Fixed + +* `post_mortem` mode in bin/byebug. + +## [3.1.0] - 2014-04-23 + +### Removed + +* `show commands` command. Use `history` instead. +* Byebug.start accepting options. Any program settings you want applied from the start should be set in `.byebugrc`. +* `trace` command. Use `set linetrace` for line tracing and `tracevar` for global variable tracing. +* `show version` command. Use `byebug --version` to check byebug's version. +* `set arg` setting. Use the `restart` command instead. + +### Changed + +* `linetrace_plus` setting renamed to `tracing_plus`. + +### Added + +* `history` command to check byebug's history of previous commands. + +## [3.0.0] - 2014-04-17 + +### Fixed + +* Plain `byebug` not working when `pry-byebug` installed. +* `post_mortem` mode. +* Command history not being saved after regular program termination. +* [#54](https://github.com/deivid-rodriguez/byebug/issues/54). (Again) calling `Byebug.start` with `Timeout.timeout` ([@zmoazeni]). + +### Added + +* Allow disabling `post_mortem` mode. + +### Changed + +* `show commands` command for listing history of previous commands now behaves like shell's `history` command. +* `show/set history filename` is now `show/set histfile`. +* `show/set history size` is now `show/set histsize`. +* `show/set history save` is now `show/set autosave`. +* `finish` semantics, see [61f9b4d](https://github.com/deivid-rodriguez/byebug/commit/61f9b4d). +* Use per project history file by default. + +### Removed + +* The `init` option for `Byebug.start`. Information to make the `restart` command work is always saved now. + +## [2.7.0] - 2014-02-24 + +### Fixed + +* [#52](https://github.com/deivid-rodriguez/byebug/issues/52). `IGNORED_FILES` slowing down startup. +* [#53](https://github.com/deivid-rodriguez/byebug/issues/53). Calling `Byebug.start` with `Timeout.timeout`. +* [#54](https://github.com/deivid-rodriguez/byebug/issues/54). Calling `Byebug.start` with `Timeout.timeout`. + +## [2.6.0] - 2014-02-08 + +### Fixed + +* Circular dependency affecting `pry-byebug` ([@andreychernih]). + +## [2.5.0] - 2013-12-14 + +### Added + +* Support for `sublime-debugger`. + +## [2.4.1] - 2013-12-05 + +### Fixed + +* [#40](https://github.com/deivid-rodriguez/byebug/issues/40). Installation error in Mac OSX ([@luislavena]). + +## [2.4.0] - 2013-12-02 + +### Fixed + +* `thread list` showing too many threads. +* Fix setting post mortem mode with `set post_mortem`. Now this is the only post mortem functionality available as specifying `Byebug.post_mortem` with a block has been removed in this version. + +### Added + +* (Again) `debugger` as an alias to `byebug` ([@wallace]). +* `-R` option for `bin/byebug` to specify server's hostname:port for remote debugging ([@mrkn]). + +### Changed + +* Use `require` instead of `require_relative` for loading byebug's extension library ([@nobu]). +* `trace variable $foo` should be now `trace variable $foo`. + +## [2.3.1] - 2013-10-17 + +### Fixed + +* Breakpoint removal. +* Broken test suite. + +## [2.3.0] - 2013-10-09 + +### Added + +* Compatibility with Phusion Passenger Enterprise ([@FooBarWidget]). + +### Changed + +* More minimalist help system. + +## [2.2.2] - 2013-09-25 + +### Fixed + +* Compilation issue in 64 bit systems. + +## [2.2.1] - 2013-09-24 + +### Fixed + +* [#26](https://github.com/deivid-rodriguez/byebug/issues/26). Compilation issue introduced in [2.2.0]. + +### Changed + +* `show/set stack_trace_on_error` is now `show/set stack_on_error`. + +## [2.2.0] - 2013-09-22 + +### Fixed + +* Stack size calculations. +* Setting `post_mortem` mode. + +### Added + +* `verbose` setting for TracePoint API event inspection. + +### Changed + +* Warning free byebug. +* Allow `edit ` without a line number. + +## [2.1.1] - 2013-09-10 + +### Fixed + +* Debugging code inside `-e` Ruby flag. + +## [2.1.0] - 2013-09-08 + +### Fixed + +* Remote debugging display. +* `eval` crashing when inspecting raised an exception (reported by [@iblue]). + +### Changed + +* `enable breakpoints` now enables every breakpoint. +* `disable breakpoints` now disables every breakpoint. + +## [2.0.0] - 2013-08-30 + +### Added + +* "Official" definition of a command API. +* Thread support. + +### Removed + +* `jump` command. It had never worked. + +### Changed + +* Several internal refactorings. + +## [1.8.2] - 2013-08-16 + +### Fixed + +* `save` command now saves the list of `displays`. +* Stack size calculation. + +### Changed + +* More user friendly regexps for commands. +* Better help for some commands. + +## [1.8.1] - 2013-08-12 + +### Fixed + +* Major regression introduced in [1.8.0]. + +## [1.8.0] - 2013-08-12 + +### Added + +* Remote debugging support. + +## [1.7.0] - 2013-08-03 + +### Added + +* List command automatically called after callstack navigation commands. +* C-frames specifically marked in the callstack. +* C-frames skipped when navigating the callstack. + +## [1.6.1] - 2013-07-10 + +### Fixed + +* Windows compatibiliy: compilation and terminal width issues. + +## [1.6.0] - 2013-07-10 + +### Fixed + +* `byebug` placed at the end of a block or method call not working as expected. +* `autolist` being applied when Ruby `-e` option used. + +### Changed + +* Backtrace callstyles. Use `long` for detailed frames in callstack and `short` for more concise frames. + +## [1.5.0] - 2013-06-21 + +### Fixed + +* Incomplete backtraces when the debugger was not started at program startup. + +## [1.4.2] - 2013-06-20 + +### Fixed + +* `help command subcommand` command. +* Interaction with Rails Console debugging flag. +* `post_mortem` mode when running byebug from the outset. +* `no-quit` flag when running byebug from the outset. + +## [1.4.1] - 2013-06-15 + +### Fixed + +* Crash when printing some filenames in backtraces. +* Allow byebug developers to easily use compilers different from gcc ([@GarthSnyder]). + +## [1.4.0] - 2013-06-05 + +### Fixed + +* Memory leaks causing `byebug` to randomly crash. + +### Changed + +* Use the Debug Inspector API for backtrace information. + +## [1.3.1] - 2013-06-02 + +### Fixed + +* Interaction with Rails debugging flag. +* Crash when trying to print lines of code containing the character '%'. +* `basename` and `linetrace` options not working together. + +## [1.3.0] - 2013-05-25 + +### Added + +* Support colon-delimited include paths in command-line front-end ([@ender672]). + +## [1.2.0] - 2013-05-20 + +### Fixed + +* Ctrl+C during command line editing (works like pry/irb). + +### Added + +* `pry` command. + +## [1.1.1] - 2013-05-07 + +### Added + +* `pry-byebug` compatibility. + +### Changed + +* Better help system. +* Code cleanup. + +## [1.1.0] - 2013-04-30 + +### Added + +* Post Mortem support. + +## [1.0.3] - 2013-04-23 + +### Fixed + +* Negative line numbers shown by list command at the beginning of file. +* `backtrace` command segfaulting when trying to show info on some frame args. Don't know the reason yet, but the exception is handled now and command does not segfault anymore. + +### Changed + +* `autoreload` is set by default now. +* Try some thread support (not even close to usable). + +## [1.0.2] - 2013-04-09 + +### Fixed + +* backtraces messed up when using both `next`/`step` and backtrace navigation commands. + +### Changed + +* `autolist` and `autoeval` are default settings now. + +## [1.0.1] - 2013-04-06 + +### Fixed + +* Byebug not loading properly. + +## [1.0.0] - 2013-03-29 + +### Fixed + +* Green test suite. + +## 0.0.1 - 2013-03-18 + +### Added + +* Initial release. + +[Unreleased]: https://github.com/deivid-rodriguez/byebug/compare/v11.1.3...HEAD +[11.1.3]: https://github.com/deivid-rodriguez/byebug/compare/v11.1.2...v11.1.3 +[11.1.2]: https://github.com/deivid-rodriguez/byebug/compare/v11.1.1...v11.1.2 +[11.1.1]: https://github.com/deivid-rodriguez/byebug/compare/v11.1.0...v11.1.1 +[11.1.0]: https://github.com/deivid-rodriguez/byebug/compare/v11.0.1...v11.1.0 +[11.0.1]: https://github.com/deivid-rodriguez/byebug/compare/v11.0.0...v11.0.1 +[11.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v10.0.2...v11.0.0 +[10.0.2]: https://github.com/deivid-rodriguez/byebug/compare/v10.0.1...v10.0.2 +[10.0.1]: https://github.com/deivid-rodriguez/byebug/compare/v10.0.0...v10.0.1 +[10.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v9.1.0...v10.0.0 +[9.1.0]: https://github.com/deivid-rodriguez/byebug/compare/v9.0.6...v9.1.0 +[9.0.6]: https://github.com/deivid-rodriguez/byebug/compare/v9.0.5...v9.0.6 +[9.0.5]: https://github.com/deivid-rodriguez/byebug/compare/v9.0.4...v9.0.5 +[9.0.4]: https://github.com/deivid-rodriguez/byebug/compare/v9.0.3...v9.0.4 +[9.0.3]: https://github.com/deivid-rodriguez/byebug/compare/v9.0.2...v9.0.3 +[9.0.2]: https://github.com/deivid-rodriguez/byebug/compare/v9.0.1...v9.0.2 +[9.0.1]: https://github.com/deivid-rodriguez/byebug/compare/v9.0.0...v9.0.1 +[9.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v8.2.5...v9.0.0 +[8.2.5]: https://github.com/deivid-rodriguez/byebug/compare/v8.2.4...v8.2.5 +[8.2.4]: https://github.com/deivid-rodriguez/byebug/compare/v8.2.3...v8.2.4 +[8.2.3]: https://github.com/deivid-rodriguez/byebug/compare/v8.2.2...v8.2.3 +[8.2.2]: https://github.com/deivid-rodriguez/byebug/compare/v8.2.1...v8.2.2 +[8.2.1]: https://github.com/deivid-rodriguez/byebug/compare/v8.2.0...v8.2.1 +[8.2.0]: https://github.com/deivid-rodriguez/byebug/compare/v8.1.0...v8.2.0 +[8.1.0]: https://github.com/deivid-rodriguez/byebug/compare/v8.0.1...v8.1.0 +[8.0.1]: https://github.com/deivid-rodriguez/byebug/compare/v8.0.0...v8.0.1 +[8.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v7.0.0...v8.0.0 +[7.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v6.0.2...v7.0.0 +[6.0.2]: https://github.com/deivid-rodriguez/byebug/compare/v6.0.1...v6.0.2 +[6.0.1]: https://github.com/deivid-rodriguez/byebug/compare/v6.0.0...v6.0.1 +[6.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v5.0.0...v6.0.0 +[5.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v4.0.5...v5.0.0 +[4.0.5]: https://github.com/deivid-rodriguez/byebug/compare/v4.0.4...v4.0.5 +[4.0.4]: https://github.com/deivid-rodriguez/byebug/compare/v4.0.3...v4.0.4 +[4.0.3]: https://github.com/deivid-rodriguez/byebug/compare/v4.0.2...v4.0.3 +[4.0.2]: https://github.com/deivid-rodriguez/byebug/compare/v4.0.1...v4.0.2 +[4.0.1]: https://github.com/deivid-rodriguez/byebug/compare/v4.0.0...v4.0.1 +[4.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v3.5.1...v4.0.0 +[3.5.1]: https://github.com/deivid-rodriguez/byebug/compare/v3.5.0...v3.5.1 +[3.5.0]: https://github.com/deivid-rodriguez/byebug/compare/v3.4.2...v3.5.0 +[3.4.2]: https://github.com/deivid-rodriguez/byebug/compare/v3.4.1...v3.4.2 +[3.4.1]: https://github.com/deivid-rodriguez/byebug/compare/v3.4.0...v3.4.1 +[3.4.0]: https://github.com/deivid-rodriguez/byebug/compare/v3.3.0...v3.4.0 +[3.3.0]: https://github.com/deivid-rodriguez/byebug/compare/v3.2.0...v3.3.0 +[3.2.0]: https://github.com/deivid-rodriguez/byebug/compare/v3.1.2...v3.2.0 +[3.1.2]: https://github.com/deivid-rodriguez/byebug/compare/v3.1.1...v3.1.2 +[3.1.1]: https://github.com/deivid-rodriguez/byebug/compare/v3.1.0...v3.1.1 +[3.1.0]: https://github.com/deivid-rodriguez/byebug/compare/v3.0.0...v3.1.0 +[3.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v2.7.0...v3.0.0 +[2.7.0]: https://github.com/deivid-rodriguez/byebug/compare/v2.6.0...v2.7.0 +[2.6.0]: https://github.com/deivid-rodriguez/byebug/compare/v2.5.0...v2.6.0 +[2.5.0]: https://github.com/deivid-rodriguez/byebug/compare/v2.4.1...v2.5.0 +[2.4.1]: https://github.com/deivid-rodriguez/byebug/compare/v2.4.0...v2.4.1 +[2.4.0]: https://github.com/deivid-rodriguez/byebug/compare/v2.3.1...v2.4.0 +[2.3.1]: https://github.com/deivid-rodriguez/byebug/compare/v2.3.0...v2.3.1 +[2.3.0]: https://github.com/deivid-rodriguez/byebug/compare/v2.2.2...v2.3.0 +[2.2.2]: https://github.com/deivid-rodriguez/byebug/compare/v2.2.1...v2.2.2 +[2.2.1]: https://github.com/deivid-rodriguez/byebug/compare/v2.2.0...v2.2.1 +[2.2.0]: https://github.com/deivid-rodriguez/byebug/compare/v2.1.1...v2.2.0 +[2.1.1]: https://github.com/deivid-rodriguez/byebug/compare/v2.1.0...v2.1.1 +[2.1.0]: https://github.com/deivid-rodriguez/byebug/compare/v2.0.0...v2.1.0 +[2.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v1.8.2...v2.0.0 +[1.8.2]: https://github.com/deivid-rodriguez/byebug/compare/v1.8.1...v1.8.2 +[1.8.1]: https://github.com/deivid-rodriguez/byebug/compare/v1.8.0...v1.8.1 +[1.8.0]: https://github.com/deivid-rodriguez/byebug/compare/v1.7.0...v1.8.0 +[1.7.0]: https://github.com/deivid-rodriguez/byebug/compare/v1.6.1...v1.7.0 +[1.6.1]: https://github.com/deivid-rodriguez/byebug/compare/v1.6.0...v1.6.1 +[1.6.0]: https://github.com/deivid-rodriguez/byebug/compare/v1.5.0...v1.6.0 +[1.5.0]: https://github.com/deivid-rodriguez/byebug/compare/v1.4.2...v1.5.0 +[1.4.2]: https://github.com/deivid-rodriguez/byebug/compare/v1.4.1...v1.4.2 +[1.4.1]: https://github.com/deivid-rodriguez/byebug/compare/v1.4.0...v1.4.1 +[1.4.0]: https://github.com/deivid-rodriguez/byebug/compare/v1.3.1...v1.4.0 +[1.3.1]: https://github.com/deivid-rodriguez/byebug/compare/v1.3.0...v1.3.1 +[1.3.0]: https://github.com/deivid-rodriguez/byebug/compare/v1.2.0...v1.3.0 +[1.2.0]: https://github.com/deivid-rodriguez/byebug/compare/v1.1.1...v1.2.0 +[1.1.1]: https://github.com/deivid-rodriguez/byebug/compare/v1.1.0...v1.1.1 +[1.1.0]: https://github.com/deivid-rodriguez/byebug/compare/v1.0.3...v1.1.0 +[1.0.3]: https://github.com/deivid-rodriguez/byebug/compare/v1.0.2...v1.0.3 +[1.0.2]: https://github.com/deivid-rodriguez/byebug/compare/v1.0.1...v1.0.2 +[1.0.1]: https://github.com/deivid-rodriguez/byebug/compare/v1.0.0...v1.0.1 +[1.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v0.0.1...v1.0.0 + +[@akaneko3]: https://github.com/akaneko3 +[@andreychernih]: https://github.com/andreychernih +[@ark6]: https://github.com/ark6 +[@astashov]: https://github.com/astashov +[@bquorning]: https://github.com/bquorning +[@cben]: https://github.com/cben +[@ender672]: https://github.com/ender672 +[@eric-hu]: https://github.com/eric-hu +[@FooBarWidget]: https://github.com/FooBarWidget +[@GarthSnyder]: https://github.com/GarthSnyder +[@HookyQR]: https://github.com/HookyQR +[@iblue]: https://github.com/iblue +[@izaera]: https://github.com/izaera +[@josephks]: https://github.com/josephks +[@k0kubun]: https://github.com/k0kubun +[@ko1]: https://github.com/ko1 +[@luislavena]: https://github.com/luislavena +[@mrkn]: https://github.com/mrkn +[@nobu]: https://github.com/nobu +[@Olgagr]: https://github.com/Olgagr +[@sethk]: https://github.com/sethk +[@shuky19]: https://github.com/shuky19 +[@tacnoman]: https://github.com/tacnoman +[@terceiro]: https://github.com/terceiro +[@tzmfreedom]: https://github.com/tzmfreedom +[@wallace]: https://github.com/wallace +[@windwiny]: https://github.com/windwiny +[@x-yuri]: https://github.com/x-yuri +[@yui-knk]: https://github.com/yui-knk +[@zmoazeni]: https://github.com/zmoazeni diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/CONTRIBUTING.md b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/CONTRIBUTING.md new file mode 100644 index 0000000..42f3b4a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/CONTRIBUTING.md @@ -0,0 +1,58 @@ +# CONTRIBUTING + +Please note that this project is released with a [Contributor Code of +Conduct](code_of_conduct.md). By participating in this project you agree to +abide by its terms. + +## Bug Reports + +* Try to reproduce the issue against the latest revision. There might be + unrealeased work that fixes your problem! +* Ensure that your issue has not already been reported. +* Include the steps you carried out to produce the problem. If we can't + reproduce it, we can't fix it. +* Include the behavior you observed along with the behavior you expected, + and why you expected it. + +## Development dependencies + +* `Byebug` depends on Ruby's TracePoint API provided by `ruby-core`. This is a + young API and a lot of bugs have been recently corrected, so make sure you + always have the lastest patch level release installed. +* The recommended tool to manage development dependencies is `bundler`. Run + `gem install bundler` to install it. +* Running `bundle install` inside a local clone of `byebug` will get development + dependencies installed. + +## Running the test suite + +* Make sure you compile the C-extension using `bin/rake compile`. + Otherwise you won't be able to use `byebug`. +* Run the test suite using the default rake task (`bin/rake`). This task is + composed of 3 subtasks: `bin/rake compile`, `bin/rake test` & `bin/rake lint`. +* If you want to run specific tests, use the provided test runner, like so: + * Specific test files. For example, `bin/minitest test/commands/break_test.rb` + * Specific test classes. For example, `bin/minitest BreakAtLinesTest` + * Specific tests. For example, + `bin/minitest test_catch_removes_specific_catchpoint` + * Specific fully qualified tests. For example, + `bin/minitest BreakAtLinesTest#test_setting_breakpoint_sets_correct_fields` + * You can combine any of them and you will get the union of all filters. For + example: `bin/minitest BreakAtLinesTest + test_catch_removes_specific_catchpoint` + +## Code style + +* Byebug uses several style checks to check code style consistent. You can run + those using `bin/rake lint`. + +## Byebug as a C-extension + +Byebug is a gem developed as a C-extension. The debugger internal's +functionality is implemented in C (the interaction with the TracePoint API). +The rest of the gem is implemented in Ruby. Normally you won't need to touch +the C-extension, but it will obviously depended on the bug you're trying to fix +or the feature you are willing to add. You can learn more about C-extensions +[here](https://tenderlovemaking.com/2009/12/18/writing-ruby-c-extensions-part-1.html) +or +[here](https://tenderlovemaking.com/2010/12/11/writing-ruby-c-extensions-part-2.html). diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/GUIDE.md b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/GUIDE.md new file mode 100644 index 0000000..dfcd71e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/GUIDE.md @@ -0,0 +1,1806 @@ +# GUIDE + +## Introduction + +### First Steps + +A handful of commands are enough to get started using `byebug`. The following +session illustrates these commands. Take the following sample file: + +```ruby +# +# The n'th triangle number: triangle(n) = n*(n+1)/2 = 1 + 2 + ... + n +# +def triangle(n) + tri = 0 + + 0.upto(n) { |i| tri += i } + + tri +end + +t = triangle(3) +puts t + +``` + +Let's debug it. + +```bash +$ byebug /path/to/triangle.rb + +[1, 10] in /path/to/triangle.rb + 1: # + 2: # The n'th triangle number: triangle(n) = n*(n+1)/2 = 1 + 2 + ... + n + 3: # +=> 4: def triangle(n) + 5: tri = 0 + 6: + 7: 0.upto(n) { |i| tri += i } + 8: + 9: tri + 10: end +(byebug) +``` + +We are currently stopped before the first executable line of the program: line 4 +of `triangle.rb`. If you are used to less dynamic languages and have used +debuggers for more statically compiled languages like C, C++, or Java, it may +seem odd to be stopped before a function definition but in Ruby line 4 is +executed. + +Byebug's prompt is `(byebug)`. If the program has died and you are in +post-mortem debugging, `(byebug:post-mortem)` is used instead. If the program +has terminated normally and the `--no-quit` option has been specified in the +command line, the prompt will be `(byebug:ctrl)` instead. The commands available +change depending on the program's state. + +Byebug automatically lists 10 lines of code centered around the current line +every time it is stopped. The current line is marked with `=>`. If the range +would overflow the beggining or the end of the file, byebug will move it +accordingly so that only actual real lines of code are displayed. + +Now let us step through the program. + +```bash +(byebug) step + +[5, 14] in /path/to/triangle.rb + 5: tri = 0 + 6: + 7: 0.upto(n) { |i| tri += i } + 8: + 9: tri + 10: end + 11: +=> 12: t = triangle(3) + 13: puts t +(byebug) # hit enter + +[1, 10] in /path/to/triangle.rb + 1: # + 2: # The n'th triangle number: triangle(n) = n*(n+1)/2 = 1 + 2 + ... + n + 3: # + 4: def triangle(n) +=> 5: tri = 0 + 6: + 7: 0.upto(n) { |i| tri += i } + 8: + 9: tri + 10: end +(byebug) eval tri +nil +(byebug) step + +[2, 11] in /path/to/triangle.rb + 2: # The n'th triangle number: triangle(n) = n*(n+1)/2 = 1 + 2 + ... + n + 3: # + 4: def triangle(n) + 5: tri = 0 + 6: +=> 7: 0.upto(n) { |i| tri += i } + 8: + 9: tri + 10: end + 11: +(byebug) eval tri +0 +``` + +The first `step` command runs the script one executable unit. The second command +we entered was just hitting the return key: `byebug` remembers the last command +you entered was `step` and runs it again. + +One way to print the values of variables is `eval` (there are other ways). When we +look at the value of `tri` the first time, we see it is `nil`. Again we are +stopped _before_ the assignment on line 5, and this variable hadn't been set +previously. However after issuing another `step` command we see that the value +is 0 as expected. If every time we stop we want to see the value of `tri` to see +how things are going, there is a better way by setting a display expression: + +```bash +(byebug) display tri +1: tri = 0 +``` + +Now let us run the program until right before we return from the function. We'll +want to see which lines get run, so we turn on _line tracing_. If we don't want +whole paths to be displayed when tracing, we can turn on _basename_. + +```bash +(byebug) set linetrace +linetrace is on +(byebug) set basename +basename is on +(byebug) finish 0 +Tracing: triangle.rb:7 0.upto(n) { |i| tri += i } +1: tri = 0 +Tracing: triangle.rb:7 0.upto(n) { |i| tri += i } +1: tri = 0 +Tracing: triangle.rb:7 0.upto(n) { |i| tri += i } +1: tri = 1 +Tracing: triangle.rb:7 0.upto(n) { |i| tri += i } +1: tri = 3 +Tracing: triangle.rb:9 tri +1: tri = 6 +1: tri = 6 + +[4, 13] in /home/davidr/Proyectos/byebug/triangle.rb + 4: def triangle(n) + 5: tri = 0 + 6: + 7: 0.upto(n) { |i| tri += i } + 8: + 9: tri +=> 10: end + 11: + 12: t = triangle(3) + 13: puts t +(byebug) quit +Really quit? (y/n) +y +``` + +So far, so good. As you can see from the above, to get out of `byebug`, one +can issue a `quit` command (or the abbreviation `q`). If you want to quit +without being prompted, suffix the command with an exclamation mark, e.g., `q!`. + +### Second Sample Session: Delving Deeper + +In this section we'll introduce breakpoints, the call stack and restarting. +Below we will debug a simple Ruby program to solve the classic Towers of Hanoi +puzzle. It is augmented by the bane of programming: some command-parameter +processing with error checking. + +```ruby +# +# Solves the classic Towers of Hanoi puzzle. +# +def hanoi(n, a, b, c) + hanoi(n - 1, a, c, b) if n - 1 > 0 + + puts "Move disk #{a} to #{b}" + + hanoi(n - 1, c, b, a) if n - 1 > 0 +end + +n_args = $ARGV.length + +raise("*** Need number of disks or no parameter") if n_args > 1 + +n = 3 + +if n_args > 0 + begin + n = $ARGV[0].to_i + rescue ValueError + raise("*** Expecting an integer, got: #{$ARGV[0]}") + end +end + +raise("*** Number of disks should be between 1 and 100") if n < 1 || n > 100 + +hanoi(n, :a, :b, :c) +``` + +Recall in the first section it was stated that before the `def` is run, the +method it names is undefined. Let's check that out. First let's see what +private methods we can call before running `def hanoi`. + +```bash +$ byebug path/to/hanoi.rb + + 1: # + 2: # Solves the classic Towers of Hanoi puzzle. + 3: # + 4: def hanoi(n, a, b, c) + 5: hanoi(n - 1, a, c, b) if n - 1 > 0 + 6: + 7: puts "Move disk #{a} to #{b}" + 8: + 9: hanoi(n - 1, c, b, a) if n - 1 > 0 + 10: end +(byebug) private_methods +public +private +include +using +define_method +default_src_encoding +DelegateClass +Digest +timeout +initialize_copy +initialize_dup +initialize_clone +sprintf +format +Integer +Float +String +Array +Hash +warn +raise +fail +global_variables +__method__ +__callee__ +__dir__ +eval +local_variables +iterator? +block_given? +catch +throw +loop +respond_to_missing? +trace_var +untrace_var +at_exit +syscall +open +printf +print +putc +puts +gets +readline +select +readlines +` +p +test +srand +rand +trap +load +require +require_relative +autoload +autoload? +proc +lambda +binding +caller +caller_locations +exec +fork +exit! +system +spawn +sleep +exit +abort +Rational +Complex +set_trace_func +gem_original_require +Pathname +pp +y +URI +rubygems_require +initialize +singleton_method_added +singleton_method_removed +singleton_method_undefined +method_missing +(byebug) private_methods.member?(:hanoi) +false +``` + +`private_methods` is not a byebug command but a Ruby feature. By default, when +`byebug` doesn't understand a command, it will evaluate it as if it was a Ruby +command. You can use any Ruby to inspect your program's state at the place it +is stopped. + +Now let's see what happens after stepping: + +```bash +(byebug) step + +[5, 14] in /path/to/hanoi.rb + 5: hanoi(n - 1, a, c, b) if n - 1 > 0 + 6: + 7: puts "Move disk #{a} to #{b}" + 8: + 9: hanoi(n - 1, c, b, a) if n - 1 > 0 + 10: end + 11: +=> 12: n_args = $ARGV.length + 13: + 14: raise("*** Need number of disks or no parameter") if n_args > 1 +(byebug) private_methods.member?(:hanoi) +true +(byebug) +``` + +Okay, lets go on and talk about program arguments. + +```bash +(byebug) $ARGV +[] +``` + +Oops. We forgot to specify any parameters to this program. Let's try again. We +can use the `restart` command here. + +```bash +(byebug) restart 3 +Re exec'ing: + /path/to/exe/byebug /path/to/hanoi.rb 3 + +[1, 10] in /path/to/hanoi.rb + 1: # + 2: # Solves the classic Towers of Hanoi puzzle. + 3: # +=> 4: def hanoi(n, a, b, c) + 5: hanoi(n - 1, a, c, b) if n - 1 > 0 + 6: + 7: puts "Move disk #{a} to #{b}" + 8: + 9: hanoi(n - 1, c, b, a) if n - 1 > 0 + 10: end +(byebug) break 5 +Created breakpoint 1 at /path/to/hanoi.rb:5 +(byebug) continue +Stopped by breakpoint 1 at /path/to/hanoi.rb:5 + +[1, 10] in /path/to/hanoi.rb + 1: # + 2: # Solves the classic Towers of Hanoi puzzle. + 3: # + 4: def hanoi(n, a, b, c) +=> 5: hanoi(n - 1, a, c, b) if n - 1 > 0 + 6: + 7: puts "Move disk #{a} to #{b}" + 8: + 9: hanoi(n - 1, c, b, a) if n - 1 > 0 + 10: end +(byebug) display n +1: n = 3 +(byebug) display a +2: a = :a +(byebug) display b +3: b = :b +(byebug) undisplay 3 +(byebug) continue +Stopped by breakpoint 1 at /path/to/hanoi.rb:5 +1: n = 2 +2: a = :a +[1, 10] in /path/to/hanoi.rb + 1: # + 2: # Solves the classic Towers of Hanoi puzzle. + 3: # + 4: def hanoi(n, a, b, c) +=> 5: hanoi(n - 1, a, c, b) if n - 1 > 0 + 6: + 7: puts "Move disk #{a} to #{b}" + 8: + 9: hanoi(n - 1, c, b, a) if n - 1 > 0 + 10: end + +(byebug) c +Stopped by breakpoint 1 at /path/to/hanoi.rb:5 +1: n = 1 +2: a = :a + +[1, 10] in /path/to/hanoi.rb + 1: # + 2: # Solves the classic Towers of Hanoi puzzle. + 3: # + 4: def hanoi(n, a, b, c) +=> 5: hanoi(n - 1, a, c, b) if n - 1 > 0 + 6: + 7: puts "Move disk #{a} to #{b}" + 8: + 9: hanoi(n - 1, c, b, a) if n - 1 > 0 + 10: end +(byebug) set nofullpath +fullpath is off +(byebug) where +--> #0 Object.hanoi(n#Fixnum, a#Symbol, b#Symbol, c#Symbol) at .../shortpath/to/hanoi.rb:5 + #1 Object.hanoi(n#Fixnum, a#Symbol, b#Symbol, c#Symbol) at .../shortpath/to/hanoi.rb:5 + #2 at .../Proyectos/byebug/hanoi.rb:28 +(byebug) +``` + +In the above we added new commands: `break` (see [breakpoints]()), which +indicates to stop just before that line of code is run, and `continue`, which +resumes execution. To remove a display expression `undisplay` is used. If we +give a display number, just that display expression is removed. + +We also used a new command `where`(see [backtrace]()) to show the callstack. In +the above situation, starting from the bottom line we see we called the `hanoi` +method from line 28 of the file `hanoi.rb` and the `hanoi` method called itself +two more times at line 5. + +In the callstack we show a _current frame_ mark, the frame number, the method +being called, the names of the parameters, the types those parameters +_currently_ have and the file-line position. Remember it's possible that when +the program was called the parameters had different types, since the types of +variables can change dynamically. You can alter the style of what to show in the +trace (see [callstyle]()). + +Now let's move around the callstack. + +```bash +(byebug) undisplay +Clear all expressions? (y/n) y +(byebug) n_args +NameError Exception: undefined local variable or method `n_args' for main:Object +(byebug) frame 2 + +[19, 28] in /path/to/hanoi.rb + 19: begin + 20: n = $ARGV[0].to_i + 21: rescue ValueError + 22: raise("*** Expecting an integer, got: #{$ARGV[0]}") + 23: end + 24: end + 25: + 26: raise("*** Number of disks should be between 1 and 100") if n < 1 || n > 100 + 27: +=> 28: hanoi(n, :a, :b, :c) +(byebug) n_args +1 +(byebug) eval n +3 +(byebug) down 2 + +[1, 10] in /path/to/hanoi.rb + 1: # + 2: # Solves the classic Towers of Hanoi puzzle. + 3: # + 4: def hanoi(n, a, b, c) +=> 5: hanoi(n - 1, a, c, b) if n - 1 > 0 + 6: + 7: puts "Move disk #{a} to #{b}" + 8: + 9: hanoi(n - 1, c, b, a) if n - 1 > 0 + 10: end +(byebug) eval n +2 +``` + +Notice in the above to get the value of variable `n` we had to use a print +command like `eval n`. If we entered just `n`, that would be taken to mean byebug +command `next`. In the current scope, variable `n_args` is not defined. However +I can change to the top-most frame by using the `frame 2` command. Notice that +inside frame #2, the value of `n_args` can be shown. Also note that the value of +variable `n` is different. + +### Attaching to a running program with `byebug` + +In the previous sessions we've been calling byebug right at the outset, but +there is another mode of operation you might use. If there's a lot of code that +needs to be run before the part you want to inspect, it might not be efficient +or convenient to run byebug from the outset. + +In this section we'll show how to enter the code in the middle of your program, +while delving more into byebug's operation. We will also use unit testing. Using +unit tests will greatly reduce the amount of debugging needed, while at the same +time, will increase the quality of your program. + +What we'll do is take the `triangle` code from the first session and write a +unit test for that. In a sense we did write a tiny test for the program which +was basically the last line where we printed the value of `triangle(3)`. This +test however wasn't automated: the expectation is that someone would look at the +output and verify that what was printed is what was expected. + +Before we can turn that into something that can be `required`, we probably want +to remove that output. However I like to keep in that line so that when I +look at the file, I have an example of how to run it. Therefore we will +conditionally run this line if that file is invoked directly, but skip it if it +is not. _NOTE: `byebug` resets `$0` to try to make things like this work._ + +```ruby +if __FILE__ == $PROGRAM_NAME + t = triangle(3) + puts t +end +``` + +Okay, we're now ready to write our unit test and we'll use the `minitest` +framework for that. Here's the test code, it should be placed in the same +directory as `triangle.rb`. + +```ruby +require "minitest/autorun" +require_relative "triangle.rb" + +class TestTriangle < Minitest::Test + def test_basic + solutions = [] + + 0.upto(5) { |i| solutions << triangle(i) } + + assert_equal([0, 1, 3, 6, 10, 15], solutions, "First 5 triangle numbers") + end +end +``` + +Let's say we want to stop before the first statement in our test method, we'll +add the following: + +```ruby +... +def test_basic + byebug + solutions = [] +... +``` + +Now we run the program, requiring `byebug` + +```bash +$ ruby -rbyebug test_triangle.rb +Run options: --seed 31679 + +# Running: + +[2, 11] in test_triangle.rb + 2: require_relative "triangle.rb" + 3: + 4: class TestTriangle < Minitest::Test + 5: def test_basic + 6: byebug +=> 7: solutions = [] + 8: + 9: 0.upto(5) { |i| solutions << triangle(i) } + 10: + 11: assert_equal([0, 1, 3, 6, 10, 15], solutions, "First 5 triangle numbers") +(byebug) +``` + +and we see that we are stopped at line 7 just before the initialization of the +list `solutions`. + +Now let's see where we are... + +```bash +(byebug) set nofullpath +Displaying frame's full file names is off. +(byebug) bt +--> #0 TestTriangle.test_basic at .../Proyectos/byebug/test_triangle.rb:7 + #1 block (3 levels) in Minitest::Test.run at .../lib/minitest/test.rb:108 + #2 Minitest::Test.capture_exceptions at .../lib/minitest/test.rb:206 + #3 block (2 levels) in Minitest::Test.run at .../lib/minitest/test.rb:105 + #4 Minitest::Test.time_it at .../lib/minitest/test.rb:258 + #5 block in Minitest::Test.run at .../lib/minitest/test.rb:104 + #6 #.on_signal(name#String, action#Proc) at .../minitest-5.5.0/lib/minitest.rb:321 + #7 Minitest::Test.with_info_handler(&block#Proc) at .../lib/minitest/test.rb:278 + #8 Minitest::Test.run at .../lib/minitest/test.rb:103 + #9 #.run_one_method(klass#Class, method_name#String) at .../minitest-5.5.0/lib/minitest.rb:768 + #10 #.run_one_method(klass#Class, method_name#String, reporter#Minitest::CompositeReporter) at .../minitest-5.5.0/lib/minitest.rb:295 + #11 block (2 levels) in #.run(reporter#Minitest::CompositeReporter, options#Hash) at .../minitest-5.5.0/lib/minitest.rb:289 + ͱ-- #12 Array.each at .../minitest-5.5.0/lib/minitest.rb:288 + #13 block in #.run(reporter#Minitest::CompositeReporter, options#Hash) at .../minitest-5.5.0/lib/minitest.rb:288 + #14 #.on_signal(name#String, action#Proc) at .../minitest-5.5.0/lib/minitest.rb:321 + #15 #.with_info_handler(reporter#Minitest::CompositeReporter, &block#Proc) at .../minitest-5.5.0/lib/minitest.rb:308 + #16 #.run(reporter#Minitest::CompositeReporter, options#Hash) at .../minitest-5.5.0/lib/minitest.rb:287 + #17 block in #.__run(reporter#Minitest::CompositeReporter, options#Hash) at .../minitest-5.5.0/lib/minitest.rb:150 + ͱ-- #18 Array.map at .../minitest-5.5.0/lib/minitest.rb:150 + #19 #.__run(reporter#Minitest::CompositeReporter, options#Hash) at .../minitest-5.5.0/lib/minitest.rb:150 + #20 #.run(args#Array) at .../minitest-5.5.0/lib/minitest.rb:127 + #21 block in #.autorun at .../minitest-5.5.0/lib/minitest.rb:56 +(byebug) +``` + +We get the same result as if we had run byebug from the outset. + +### Debugging Oddities: How debugging Ruby may be different from other languages + +If you are used to debugging in other languages like C, C++, Perl, Java or even +Bash (see [bashdb](http://bashdb.sourceforge.net)), there may be a number of things that +seem or feel a little bit different and may confuse you. A number of these +things aren't oddities of the debugger per se but differences in how Ruby works +compared to those other languages. Because Ruby works a little differently from +those other languages, writing a debugger has to also be a little different as +well if it is to be useful. In this respect, using Byebug may help you +understand Ruby better. + +We've already seen one such difference: the fact that we stop on method +definitions or `def`'s and that is because these are in fact executable +statements. In other compiled languages this would not happen because that's +already been done when you compile the program (or in Perl when it scans in the +program). In this section we'll consider some other things that might throw off +new users to Ruby who are familiar with other languages and debugging in them. + +#### Bouncing Around in Blocks (iterators) + +When debugging languages with coroutines like Python and Ruby, a method call may +not necessarily go to the first statement after the method header. It's possible +that the call will continue after a `yield` statement from a prior call. + +```ruby +# +# Enumerator for primes +# +class SievePrime + def initialize + @odd_primes = [] + end + + def next_prime + candidate = 2 + yield candidate + not_prime = false + candidate += 1 + + loop do + @odd_primes.each do |p| + not_prime = (0 == (candidate % p)) + break if not_prime + end + + unless not_prime + @odd_primes << candidate + yield candidate + end + + candidate += 2 + end + end +end + +SievePrime.new.next_prime do |prime| + puts prime + break if prime > 10 +end +``` + +```bash +$ byebug primes.rb +[1, 10] in /path/to/primes.rb + 1: # + 2: # Enumerator for primes + 3: # +=> 4: class SievePrime + 5: def initialize + 6: @odd_primes = [] + 7: end + 8: + 9: def self.next_prime(&block) + 10: candidate = 2 +(byebug) set linetrace +line tracing is on. +(byebug) set basename +basename in on. +(byebug) step 9 +Tracing: primes.rb:5 def initialize +Tracing: primes.rb:9 def next_prime +Tracing: primes.rb:31 SievePrime.new.next_prime do |prime| +Tracing: primes.rb:6 @odd_primes = [] +Tracing: primes.rb:10 candidate = 2 +Tracing: primes.rb:11 yield candidate +Tracing: primes.rb:32 puts prime +2 +Tracing: primes.rb:33 break if prime > 10 +Tracing: primes.rb:12 not_prime = false + +[7, 16] in /path/to/primes.rb + 7: end + 8: + 9: def next_prime + 10: candidate = 2 + 11: yield candidate +=> 12: not_prime = false + 13: candidate += 1 + 14: + 15: loop do + 16: @odd_primes.each do |p| + 17: not_prime = (0 == (candidate % p)) +(byebug) +``` + +The loop between lines 31-34 gets interleaved between those of +`SievePrime#next_prime`, lines 9-28 above. + +#### No Parameter Values in a Call Stack + +In traditional debuggers, in a call stack you can generally see the names of the +parameters and the values that were passed in. + +Ruby is a very dynamic language and it tries to be efficient within the confines +of the language definition. Values generally aren't taken out of a variable or +expression and pushed onto a stack. Instead a new scope is created and the +parameters are given initial values. Parameter passing is by _reference_ not by +_value_ as it is say Algol, C, or Perl. During the execution of a method, +parameter values can change (and often do). In fact even the _class_ of the +object can change. + +So at present, the name of the parameter is shown. The call-style setting +([callstyle]()) can be used to set whether the name is shown or the name and the +_current_ class of the object. + +#### Lines You Can Stop At + +Consider the following little Ruby program. + +```ruby +"Yes it does" =~ / +(Yes) \s+ +it \s+ +does +/ix +puts $1 +``` + +The stopping points that Ruby records are the last two lines, lines 5 and 6. + +Inside `byebug` you can get a list of stoppable lines for a file using the `info +file` command. + +### Threading support + +Byebug supports debugging Ruby programs making use of multiple threads. + +Let's consider the following sample program: + +```ruby +class Company + def initialize(task) + @tasks, @results = Queue.new, Queue.new + + @tasks.push(task) + end + + def run + manager = Thread.new { manager_routine } + employee = Thread.new { employee_routine } + + sleep 6 + + go_home(manager) + go_home(employee) + end + + # + # An employee doing his thing + # + def employee_routine + loop do + if @tasks.empty? + have_a_break(0.1) + else + work_hard(@tasks.pop) + end + end + end + + # + # A manager doing his thing + # + def manager_routine + loop do + if @results.empty? + have_a_break(1) + else + show_off(@results.pop) + end + end + end + + private + + def show_off(result) + puts result + end + + def work_hard(task) + task ** task + end + + def have_a_break(amount) + sleep amount + end + + def go_home(person) + person.kill + end +end + +Company.new(10).run +``` + +The `Company` class simulates a real company. The company has a manager and an +employee represented by 2 threads: they work concurrently to achieve the +company's targets. + +* The employee looks for tasks to complete. If there are tasks, it works hard to + complete them. Otherwise he has a quick break. + +```ruby +# +# An employee doing his thing +# +def employee_routine + loop do + if @tasks.empty? + have_a_break(0.1) + else + work_hard(@tasks.pop) + end + end +end +``` + +* The manager, on the other hand, sits there all day and sporadically checks + whether there are any results to show off. + +```ruby +# +# A manager doing his thing +# +def manager_routine + loop do + if @results.empty? + have_a_break(1) + else + show_off(@results.pop) + end + end +end +``` + +We do some abstractions easily readable in the code. Our tasks are just a +`Queue` of numbers, so are our results. What our employer does when he works is +some calculation with those numbers and what the manager does with the results +is printing them to the screen. + +We instantiate a new company with an initial task and after running that +company we expect the result to be printed in the screen, but it is not. Lets +debug our sample program: + +```bash +[1, 10] in /path/to/company.rb +=> 1: class Company + 2: def initialize(task) + 3: @tasks, @results = Queue.new, Queue.new + 4: + 5: @tasks.push(task) + 6: end + 7: + 8: def run + 9: manager = Thread.new { manager_routine } + 10: employee = Thread.new { employee_routine } +(byebug) l + +[11, 20] in /path/to/company.rb + 11: + 12: sleep 6 + 13: + 14: go_home(manager) + 15: go_home(employee) + 16: end + 17: + 18: # + 19: # An employee doing his thing + 20: # + +(byebug) c 12 +Stopped by breakpoint 1 at /path/to/company.rb:12 + +[7, 16] in /path/to/company.rb + 7: + 8: def run + 9: manager = Thread.new { manager_routine } + 10: employee = Thread.new { employee_routine } + 11: +=> 12: sleep 6 + 13: + 14: go_home(manager) + 15: go_home(employee) + 16: end +(byebug) th l ++ 1 # /path/to/company.rb:12 + 2 # + 3 # +``` + +What we have done here is just start our program and advance to the point +inmediately after our `employee` and `manager` threads have been created. We +can then check that the threads are there using the `thread list` command. Now +we want to debug both of this threads to check what's happening and look for the +bug. + +```bash +(byebug) th switch 3 + +[5, 14] in /path/to/company.rb + 5: @tasks.push(task) + 6: end + 7: + 8: def run + 9: manager = Thread.new { manager_routine } +=> 10: employee = Thread.new { employee_routine } + 11: + 12: sleep 6 + 13: + 14: go_home(manager) +(byebug) th stop 1; th stop 2 +$ 1 # /path/to/company.rb:12 +$ 2 # /path/to/company.rb:9 +(byebug) th l +$ 1 # /path/to/company.rb:12 +$ 2 # /path/to/company.rb:55 ++ 3 # /path/to/company.rb:10 +``` + +We have started by debugging the `employee` thread. To do that, we switch to +that thread using the `thread switch 3` command. The thread number is the one +specified by `thread list`, we know this is our worker thread because `thread +list` specifies where the thread is defined in the file (and its current +position if the thread is currently running). + +After that we stopped the main thread and the worker thread, using the command +`thread stop`. We do this because we want to focus on the employee thread first +and don't want the program to finish while we are debugging. Notice that stopped +threads are marked with the "$" symbol whereas the current thread is marked with +the "+" symbol. + +```bash +(byebug) s + +[17, 26] in /path/to/company.rb + 17: + 18: # + 19: # An employee doing his thing + 20: # + 21: def employee_routine +=> 22: loop do + 23: if @tasks.empty? + 24: have_a_break(0.1) + 25: else + 26: work_hard(@tasks.pop) +(byebug) s + +[18, 27] in /path/to/company.rb + 18: # + 19: # An employee doing his thing + 20: # + 21: def employee_routine + 22: loop do +=> 23: if @tasks.empty? + 24: have_a_break(0.1) + 25: else + 26: work_hard(@tasks.pop) + 27: end +(byebug) n + +[21, 30] in /path/to/company.rb + 21: def employee_routine + 22: loop do + 23: if @tasks.empty? + 24: have_a_break(0.1) + 25: else +=> 26: work_hard(@tasks.pop) + 27: end + 28: end + 29: end + 30: +(byebug) s + +[49, 58] in /path/to/company.rb + 49: def show_off(result) + 50: puts result + 51: end + 52: + 53: def work_hard(task) +=> 54: task ** task + 55: end + 56: + 57: def have_a_break(amount) + 58: sleep amount +(byebug) s + +[21, 30] in /path/to/company.rb + 21: # + 22: # An employee doing his thing + 23: # + 24: def employee_routine + 25: loop do +=> 26: if @tasks.empty? + 27: have_a_break(0.1) + 28: else + 29: work_hard(@tasks.pop) + 30: end +(byebug) n + +[22, 31] in /path/to/company.rb + 22: # An employee doing his thing + 23: # + 24: def employee_routine + 25: loop do + 26: if @tasks.empty? +=> 27: have_a_break(0.1) + 28: else + 29: work_hard(@tasks.pop) + 30: end + 31: end +(byebug) n + +[21, 30] in /path/to/company.rb + 21: # + 22: # An employee doing his thing + 23: # + 24: def employee_routine + 25: loop do +=> 26: if @tasks.empty? + 27: have_a_break(0.1) + 28: else + 29: work_hard(@tasks.pop) + 30: end + 31: end +(byebug) +``` + +Everything seems fine in this thread. The first iteration the employee will do +his job, and after that it will just check for new tasks and sleep. Let's debug +the manager task now: + +```bash +(byebug) th resume 2 + 2 # /path/to/company.rb:12 +(byebug) th switch 2 + 2 # /path/to/company.rb:12 + +[7, 16] in /path/to/company.rb + 7: + 8: # + 9: # A CEO running his company + 10: # + 11: def run +=> 12: manager = Thread.new { manager_routine } + 13: employee = Thread.new { employee_routine } + 14: + 15: sleep 6 + 16: +(byebug) +``` + +We used the command `thread resume` to restart the manager's thread and then +switch to it using `thread switch`. It's important to resume the thread's +execution before switching to it, otherwise we'll get a hang because we cannot +run a sleeping thread. + +Now we can investigate the problem in the employer's side: + +```bash +(byebug) s +[30, 39] in /path/to/company.rb + 30: + 31: # + 32: # A manager doing his thing + 33: # + 34: def manager_routine +=> 35: loop do + 36: if @results.empty? + 37: have_a_break(1) + 38: else + 39: show_off(@results.pop) +(byebug) s + +[31, 40] in /path/to/company.rb + 31: # + 32: # A manager doing his thing + 33: # + 34: def manager_routine + 35: loop do +=> 36: if @results.empty? + 37: have_a_break(1) + 38: else + 39: show_off(@results.pop) + 40: end +(byebug) n + +[32, 41] in /path/to/company.rb + 32: # A manager doing his thing + 33: # + 34: def manager_routine + 35: loop do + 36: if @results.empty? +=> 37: have_a_break(1) + 38: else + 39: show_off(@results.pop) + 40: end + 41: end +(byebug) n + +[31, 40] in /path/to/company.rb + 31: # + 32: # A manager doing his thing + 33: # + 34: def manager_routine + 35: loop do +=> 36: if @results.empty? + 37: have_a_break(1) + 38: else + 39: show_off(@results.pop) + 40: end +(byebug) +``` + +Now we can see the problem, the `@results` variable is always empty! The +employee forgot to leave the results in his manager's deck. We fix it by +changing the line + +```ruby +work_hard(@tasks.pop) +``` + +in the `employee_routine` method with the line + +```ruby +@results << work_hard(@tasks.pop) +``` + +To be continued... + +* More complex examples with objects, pretty printing and irb. +* Line tracing and non-interactive tracing. +* Post-mortem debugging. + +## Getting in & out + +### Starting byebug + +There is a wrapper script called `byebug` which basically `require`'s the gem +then loads `byebug` before its argument (the program to be debugged) is started. +If you don't need to pass dash options to your program, which might be confused +with byebug options, then you don't need to add the `--`. To get a brief list of +options and descriptions, use the `--help` option. + +```bash +$ byebug --help + + byebug 3.5.1 + + Usage: byebug [options] -- + + -d, --debug Set $DEBUG=true + -I, --include list Add to paths to $LOAD_PATH + -m, --[no-]post-mortem Use post-mortem mode + -q, --[no-]quit Quit when script finishes + -x, --[no-]rc Run byebug initialization file + -s, --[no-]stop Stop when script is loaded + -r, --require file Require library before script + -R, --remote [host:]port Remote debug [host:]port + -t, --[no-]trace Turn on line tracing + -v, --version Print program version + -h, --help Display this message + +``` + +Many options appear as a long option name, such as `--help` and a short one +letter option name, such as `-h`. The list of options is detailed below: + +#### -h | --help + +It causes `byebug` to print some basic help and exit. + +#### -v | --version + +It causes `byebug` to print its version number and exit. + +#### -d | --debug + +Sets `$DEBUG` to `true`. Compatible with Ruby's flag. + +#### -I | --include path + +Adds `path` to load path. `path` can be a single path or a colon separated path +list. + +#### -m | --post-mortem + +If your program raises an exception that isn't caught you can enter byebug for +inspection of what went wrong. You may also want to use this option in +conjunction with `--no-stop`. See also [Post-Mortem Debugging](). + +#### --no-quit + +Keep inside `byebug` after your program terminates normally. + +#### --no-stop + +Normally `byebug` stops before executing the first statement. If instead you +want it to start running initially and perhaps break it later in the execution, +use this option. + +#### -r | --require lib + +Requires the library before executing the script. This option is compatible +with Ruby's. + +#### -t | --trace + +Turns on line tracing. Running `byebug --trace .rb` is pretty much +like running `ruby -rtracer .rb`. If all you want to do however is +get a line trace, `tracer` is most likely faster than `byebug`. + +```bash +$ time byebug --trace --no-stop hanoi.rb > /dev/null + +real 0m0.743s +user 0m0.668s +sys 0m0.068s +$ time ruby -rtracer hanoi.rb > /dev/null + +real 0m0.077s +user 0m0.072s +sys 0m0.004s +``` + +### Byebug default options + +Byebug has many command-line options,; it seems that some people want to set +them differently from the defaults. For example, some people may want +`--no-quit` to be the default behavior. One could write a wrapper script or set +a shell alias to handle this. + +### Command Files + +A command file is a file of lines that are `byebug` commands. Comments (lines +starting with `#`) may also be included. An empty line in a command file does +nothing; it does not mean to repeat the last command, as it would from the +terminal. + +When you start `byebug`, it automatically executes commands from its +_init file_, called `.byebugrc`. During startup, `byebug` does the following: + +* __Processes command line options and operands.__ Reads the init file in your + current directory, if any, and then checks your home directory. The home + directory is the directory named in the `$HOME` or `$HOMEPATH` environment + variable. Thus, you can have more than one init file, one generic in your home + directory, and another, specific to the program you are debugging, in the + directory where you invoke `byebug`. + +You can also request the execution of a command file with the `source` command +(see [Source]()). + +### Quitting byebug + +To exit `byebug`, use the `quit` command (abbreviated to `q`). Normally, if you +are in an interactive session, this command will prompt to ask if you really +want to quit. If you want to quit without being prompted, enter `quit +unconditionally` (abbreviated to `q!`). + +Another way to terminate byebug is to use the `kill` command. This does the +more forceful `kill -9`. It can be used in cases where `quit` doesn't work (I +haven't seen those yet). + +### Calling byebug from inside your program + +Running a program from byebug adds a bit of overhead and slows it down a little. +Furthermore, by necessity, debuggers change the operation of the program they +are debugging. And this can lead to unexpected and unwanted differences. It has +happened so often that the term +[Heisenbugs](https://en.wikipedia.org/wiki/Heisenbug) was coined to describe the +situation where using a debugger (among other possibilities) changes the +behavior of the program so that the bug doesn't manifest itself anymore. + +There is another way to get into byebug which adds no overhead or slowdown until +you reach the point at which you want to start debugging. However here you must +change the script and make an explicit call to byebug. Because byebug isn't +involved before the first call, there is no overhead and the script will run +at the same speed as if there were no byebug. + +To enter byebug this way, just drop `byebug` in whichever line you want to start +debugging at. You also have to require byebug somehow. If using bundler, it will +take care of that for you, otherwise you can use the ruby `-r` flag or add +`require "byebug"` in the line previous to the `byebug` call. + +If speed is crucial, you may want to start and stop this around certain sections +of code, using `Byebug.start` and `Byebug.stop`. Alternatively, instead of +issuing an explicit `Byebug.stop` you can add a block to the `Byebug.start` and +debugging is turned on for that block. If the block of code raises an uncaught +exception that would cause the block to terminate, the `stop` will occur. See +[Byebug.start with a block](). + +When `byebug`is run, `.byebugrc` is read. + +You may want to enter byebug at several points in the program where there is a +problem you want to investigate. And since `byebug` is just a method call it's +possible to enclose it in a conditional expression, for example + +```ruby +byebug if "bar" == foo and 20 == iter_count +``` + +### Restarting Byebug + +You can restart the program using `restart [program args]`. This is a re-exec - +all byebug state is lost. If command arguments are passed, those are used. +Otherwise program arguments from the last invocation are used. + +You won't be able to restart your program in all cases. First, the program +should have been invoked at the outset rather than having been called from +inside your program or invoked as a result of post-mortem handling. + +Also, since this relies on the OS `exec` call, this command is available only if +your OS supports `exec`. + +## Debugging remote programs + +It is possible to set up debugging so that you can issue byebug commands from +outside the process running the Ruby code. In fact, you might even be on a +different computer than the one running the Ruby program. + +To setup remote debugging, drop the following somewhere before the point in the +program that you want to debug (In Rails, the +`config/environments/development.rb` could be a good candidate). + +```ruby + require "byebug/core" + Byebug.wait_connection = true + Byebug.start_server("localhost", ) +``` + +Once this piece gets executed, you can connect to the remote debugger from your +local machine, by running: `byebug -R localhost:`. + +Next, at a place of program execution which gets run just before the code you +want to debug, add a call to `byebug` as was done without remote execution: + +```ruby + # work, work, work... + byebug + some ruby code # byebug will stop before this line is run +``` + +## Byebug Command Reference + +### Command Syntax + +Usually a command is put on a single line. There is no limit on how long it can +be. It starts with a command name, which is followed by arguments whose meaning +depends on the command name. For example, the command `step` accepts an +argument which is the number of times to step, as in `step 5`. You can also use +the `step` command with no arguments. Some commands do not allow any arguments. + +Multiple commands can be put on a line by separating each with a semicolon `;`. +You can disable the meaning of a semicolon to separate commands by escaping it +with a backslash. + +For example, you might want to enter the following code to compute the 5th +Fibonacci number. + +```bash +(byebug) fib1=0; fib2=1; 5.times {|temp| temp=fib1; fib1=fib2; fib2 += temp } +0 +1 +SyntaxError Exception: /home/davidr/Proyectos/sample_app/trace.rb:1: syntax +error, unexpected end-of-input, expecting '}' + 5.times { |temp| temp=fib1 + ^ +nil +1 +SyntaxError Exception: /home/davidr/Proyectos/sample_app/trace.rb:1: syntax +error, unexpected tSTRING_DEND, expecting end-of-input + fib2 += temp } + ^ +nil +(byebug) fib1=0\; fib2=1\; 5.times {|temp| temp=fib1\; fib1=fib2\; fib2 += temp } +5 +(byebug) fib2 +8 +``` + +You might also consider using the [irb]() or [pry]() commands and then you +won't have to escape semicolons. + +A blank line as input (typing just ``) means to repeat the previous +command. + +Byebug uses readline, which handles line editing and retrieval of previous +commands. Up arrow, for example, moves to the previous byebug command; down +arrow moves to the next more recent command (provided you are not already at +the last command). Command history is saved in file `.byebug_history`. A limit +is put on the history size. You can see this with the `show history size` +command. See [history]() for history parameters. + +### Command Output + +In the command-line interface, when `byebug` is waiting for input it presents a +prompt of the form `(byebug)`. If the program has terminated normally the prompt +will be `(byebug:ctrl)` and in post-mortem debugging it will be +`(byebug:post-mortem)`. + +Whenever `byebug` gives an error message such as for an invalid command or an +invalid location position, it will generally preface the message with `***`. + +### Command Help + +Once inside `byebug` you can always ask it for information on its commands using +the `help` command. You can use `help` (abbreviated `h`) with no arguments to +display a short list of named classes of commands + +```bash +(byebug) help + + break -- Sets breakpoints in the source code + catch -- Handles exception catchpoints + condition -- Sets conditions on breakpoints + continue -- Runs until program ends, hits a breakpoint or reaches a line + delete -- Deletes breakpoints + disable -- Disables breakpoints or displays + display -- Evaluates expressions every time the debugger stops + down -- Moves to a lower frame in the stack trace + edit -- Edits source files + enable -- Enables breakpoints or displays + finish -- Runs the program until frame returns + frame -- Moves to a frame in the call stack + help -- Helps you using byebug + history -- Shows byebug's history of commands + info -- Shows several informations about the program being debugged + interrupt -- Interrupts the program + irb -- Starts an IRB session + kill -- Sends a signal to the current process + list -- Lists lines of source code + method -- Shows methods of an object, class or module + next -- Runs one or more lines of code + pry -- Starts a Pry session + quit -- Exits byebug + restart -- Restarts the debugged program + save -- Saves current byebug session to a file + set -- Modifies byebug settings + show -- Shows byebug settings + skip -- Runs until the next breakpoint as long as it is different from the current one + source -- Restores a previously saved byebug session + step -- Steps into blocks or methods one or more times + thread -- Commands to manipulate threads + tracevar -- Enables tracing of a global variable + undisplay -- Stops displaying all or some expressions when program stops + untracevar -- Stops tracing a global variable + up -- Moves to a higher frame in the stack trace + var -- Shows variables and its values + where -- Displays the backtrace + +``` + +With a command name, `help` displays information on how to use the command. + +```bash +(byebug) help list + + l[ist][[-=]][ nn-mm] + + Lists lines of source code + + Lists lines forward from current line or from the place where code was + last listed. If "list-" is specified, lists backwards instead. If + "list=" is specified, lists from current line regardless of where code + was last listed. A line range can also be specified to list specific + sections of code. +(byebug) +``` + +A number of commands, namely `info`, `set`, `show`, `enable` and `disable`, have +many sub-parameters or _subcommands_. When you ask for help for one of these +commands, you will get help for all of the subcommands that command offers. +Sometimes you may want help only on a subcommand and to do this just follow the +command with its subcommand name. For example, `help info breakpoints`will just +give help about the `info breakpoints` command. Furthermore it will give longer +help than the summary information that appears when you ask for help. You don't +need to list the full subcommand name, just enough of the letters to make that +subcommand distinct from others will do. For example, `help info b` is the same +as `help info breakpoints`. + +Some examples follow. + +```bash +(byebug) help info +info[ subcommand] + +Generic command for showing things about the program being debugged. + +-- +List of "info" subcommands: +-- +info args -- Argument variables of current stack frame +info breakpoints -- Status of user-settable breakpoints +info catch -- Exceptions that can be caught in the current stack frame +info display -- Expressions to display when program stops +info file -- Info about a particular file read in +info files -- File names and timestamps of files read in +info line -- Line number and filename of current position in source file +info program -- Execution status of the program +``` + +```bash +(byebug) help info breakpoints +Status of user-settable breakpoints. +Without argument, list info about all breakpoints. +With an integer argument, list info on that breakpoint. +``` + +```bash +(byebug) help info b +Status of user-settable breakpoints. +Without argument, list info about all breakpoints. +With an integer argument, list info on that breakpoint. +``` + +### Control Commands: quit, restart, source + +#### Quit + +To exit `byebug`, type `quit` (abbreviated to `q`). Normally, if you are in an +interactive session, this command will prompt you to confirm you really want to +quit. If you want to quit without being prompted, enter `quit unconditionally` +(abbreviated to `q!`). + +#### Restart + +To restart the program, use the `restart|r` command. This is a re-exec - all +`byebug` state is lost. If command arguments are passed, those are used. +Otherwise program arguments from the last invocation are used. + +You won't be able to restart your program in all cases. First, the program +should have been invoked at the outset rather than having been called from +inside your program or invoked as a result of post-mortem handling. + +#### Source + +You can run `byebug` commands inside a file, using the command `source `. +The lines in a command file are executed sequentially. They are not printed as +they are executed. If there is an error, execution proceeds to the next command +in the file. For information about command files that get run automatically on +startup see [Command Files](). + +### Display Commands: display, undisplay + +#### Display + +If you find that you want to print the value of an expression frequently (to see +how it changes), you might want to add it to the *automatic display list** so +that `byebug` evaluates it each time your program stops or after a line is +printed if line tracing is enabled. Each expression added to the list is given a +number to identify it; to remove an expression from the list, you specify that +number. The automatic display looks like this: + +```bash +(byebug) display n +1: n = 3 +``` + +This display shows item numbers, expressions and their current values. If the +expression is undefined or illegal the expression will be printed but no value +will appear. + +```bash +(byebug) display undefined_variable +2: undefined_variable = +(byebug) display 1/0 +3: 1/0 = +``` + +If you use `display` with no argument, `byebug` will display the current values +of the expressions in the list, just as it is done when your program stops. +Using `info display` has the same effect. + +#### Undisplay + +To remove an item from the list, use `undisplay` followed by the number +identifying the expression you want to remove. `undisplay` does not repeat if +you press ``after using it (otherwise you would just get the error _No +display number n_) + +You can also temporarily disable or enable display expressions, so that the will +not be printed but they won't be forgotten either, so you can toggle them again +later. To do that, use `disable display` or `enable display` followed by the +expression number. + +### Evaluation of expressions: irb, pry + +To examine and change data in your script you can just evaluate any Ruby code +from `byebug`'s prompt. Any input that is not recognized as a command will be +evaluated, so `byebug` essentially works as a REPL. If you want to evaluate +something that conflicts with a `byebug` command, just use Ruby's `eval`. For +example, if you want to print a variable called `n`, type `eval n` because +typing just `n` will execute `byebug`'s command `next`. + +Finally, if you need more advanced functionality from REPL's, you can enter +`irb` or `pry` using `irb` or `pry` commands. The binding's environment will be +set to the current state in the program. When you leave the repl and go back to +`byebug`'s command prompt we show the file, line and text position of the +program. If you issue a `list` without location information, the default +location used is the current line rather than the current position that may have +got updated via a prior `list` command. + +``` +$ byebug triangle.rb +[1, 10] in /path/to/triangle.rb + 1: # Compute the n'th triangle number, the hard way: triangle(n) == (n*(n+1))/2 +=> 2: def triangle(n) + 3: tri = 0 + 4: 0.upto(n) do |i| + 5: tri += i + 6: end + 7: tri + 8: end + 9: + 10: if __FILE__ == $0 +(byebug) irb +irb(main):001:0> (0..6).inject { |sum, i| sum += i } + => 21 +irb(main):002:0> exit +(byebug) +``` + +### Printing variables: var + +Byebug can print many different information about variables. Such as + +* `var const `. Show the constants of ``. This is basically + listing variables and their values in `.constant`. +* `var instance `. Show the instance variables of ``. This is + basically listing `.instance_variables`. +* `var instance`. Show instance_variables of `self`. +* `var local`. Show local variables. +* `var global`. Show global variables. +* `var all`. Show local, global and instance and class variables of `self`. +* `method instance `. Show methods of ``. Basically this is the + same as running `.instance_methods(false)`. +* `method `. Show methods of the class or module + ``. Basically this is the same as running + `.methods`. + +### Examining Program Source Files: list + +`byebug` can print parts of your script's source. When your script stops, +`byebug` spontaneously lists the source code around the line where it stopped +that line. It does that when you change the current stack frame as well. +Implicitly there is a default line location. Each time a list command is run +that implicit location is updated, so that running several list commands in +succession shows a contiguous block of program text. + +If you don't need code context displayed every time, you can issue the `set +noautolist` command. Now whenever you want code listed, you can explicitly issue +the `list` command or its abbreviation `l`. Notice that when a second listing is +displayed, we continue listing from the place we last left off. When the +beginning or end of the file is reached, the line range to be shown is adjusted +so "it doesn't overflow". You can set the `noautolist` option by default by +dropping `set noautolist` in byebug's startup file `.byebugrc`. + +If you want to set how many lines to be printed by default rather than use the +initial number of lines, 10, use the `set listsize` command ([listsize()). To +see the entire program in one shot, give an explicit starting and ending line +number. You can print other portions of source files by giving explicit position +as a parameter to the list command. + +There are several ways to specify what part of the file you want to print. `list +nnn` prints lines centered around line number `nnn` in the current source file. +`l` prints more lines, following the last lines printed. `list -` prints lines +just before the lines last printed. `list nnn-mmm` prints lines between `nnn` +and `mmm` inclusive. `list =` prints lines centered around where the script is +stopped. Repeating a `list` command with `RET` discards the argument, so it is +equivalent to typing just `list`. This is more useful than listing the same +lines again. An exception is made for an argument of `-`: that argument is +preserved in repetition so that each repetition moves up in the source file. + +### Editing Source files: edit + +To edit a source file, use the `edit` command. The editor of your choice is invoked +with the current line set to the active line in the program. Alternatively, you can +give a line specification to specify what part of the file you want to edit. + +You can customize `byebug` to use any editor you want by using the `EDITOR` +environment variable. The only restriction is that your editor (say `ex`) recognizes +the following command-line syntax: + +``` +ex +nnn file +``` + +The optional numeric value `+nnn` specifies the line number in the file where +you want to start editing. For example, to configure `byebug` to use the `vi` editor, +you could use these commands with the `sh` shell: + +```bash +EDITOR=/usr/bin/vi +export EDITOR +byebug ... +``` + +or in the `csh` shell, + +```bash +setenv EDITOR /usr/bin/vi +byebug ... +``` + +### The stack trace + +When your script has stopped, one thing you'll probably want to know is where +it stopped and some idea of how it got there. + +Each time your script calls a method or enters a block, information about this +action is saved. This information is what we call a _stack frame_ or just a +_frame_. The set of all frames at a certain point in the program's execution is +called the _stack trace_ or just the _stack_. Each frame contains a line number +and the source-file name that the line refers to. If the frame is the beginning +of a method it also contains the method name. + +When your script is started, the stack has only one frame, that of the `main` +method. This is called the _initial frame_ or the _outermost frame_. Each time +a method is called, a new frame is added to the stack trace. Each time a method +returns, the frame for that method invocation is removed. If a method is +recursive, there can be many frames for the same method. The frame for the +method in which execution is actually occurring is called the _innermost +frame_. This is the most recently created of all the stack frames that still +exist. + +Every time the debugger stops, one entry in the stack is selected as the +current frame. Many byebug commands refer implicitly to the selected block. In +particular, whenever you ask Byebug to list lines without giving a line number +or location the value is found in the selected frame. There are special +commands to select whichever frame you're interested in, such as `up`, `down` +and `frame`. + +After switching frames, when you issue a `list` command without any position +information, the position used is the location in the frame that you just +switched between, rather than a location that got updated via a prior `list` +command. + +Byebug assigns numbers to all existing stack frames, starting with zero for the +_innermost frame_, one for the frame that called it, and so on upward. These +numbers do not really exist in your script, they are assigned by Byebug to give +you a way of designating stack frames in commands. + +### Printing the Stack: `where` command + +The command `where`, aliased to `bt` or `backtrace` prints the call stack., It +shows one line per frame, for many frames, starting with the place that you are +stopped at (frame zero), followed by its caller (frame one), and on up the +stack. Each frame is numbered and can be referred to in the `frame` command. +The position of the current frame is marked with `-->`. + +The are some special frames generated for methods that are implemented in C. +One such method is `each`. They are marked differently in the call stack to +indicate that we cannot switch to those frames. This is because they have no +source code in Ruby, so we can not debug them using Byebug. + +```bash +(byebug) where +--> #0 Object.gcd(a#Fixnum, b#Fixnum) at line gcd.rb:6 + #1 at line gcd.rb:19 +``` + +### Selecting a frame: `up`, `down` and `frame` commands + +* `up `: Move `n` frames up the stack, towards the outermost frame (higher + frame numbers, frames that have existed longer). `n` defaults to one. + +* `down `: Move `n` frames down the stack, towards the _innermost frame_ + (lower frame numbers, frames that were created more recently). `n` defaults to + one. + +* `frame `: Allows you to move to an arbitrary frame. `n` is the stack frame + number or 0 if no frame number is given. `frame 0` will show the current and + most recent stack frame. If a negative number is given, counting is from the + other end of the stack frame, so `frame -1` shows the least-recent, outermost + stack frame. Without an argument, `frame` prints the current stack frame. diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/LICENSE b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/LICENSE new file mode 100644 index 0000000..94e72ac --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2018 David Rodríguez +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/README.md b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/README.md new file mode 100644 index 0000000..a8e2046 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/README.md @@ -0,0 +1,199 @@ +# Byebug + +[![Version][gem]][gem_url] +[![Tidelift][tid]][tid_url] +[![Gitter][irc]][irc_url] + +[gem]: https://img.shields.io/gem/v/byebug.svg +[tid]: https://tidelift.com/badges/package/rubygems/byebug +[irc]: https://img.shields.io/badge/IRC%20(gitter)-devs%20%26%20users-brightgreen.svg + +[gem_url]: https://rubygems.org/gems/byebug +[tid_url]: https://tidelift.com/subscription/pkg/rubygems-byebug?utm_source=rubygems-byebug&utm_medium=readme_badge +[irc_url]: https://gitter.im/deivid-rodriguez/byebug + +Byebug is a simple to use and feature rich debugger for Ruby. It uses the +TracePoint API for execution control and the Debug Inspector API for call stack +navigation. Therefore, Byebug doesn't depend on internal core sources. Byebug is also +fast because it is developed as a C extension and reliable because it is supported +by a full test suite. + +The debugger permits the ability to understand what is going on _inside_ a Ruby program +while it executes and offers many of the traditional debugging features such as: + +* Stepping: Running your program one line at a time. +* Breaking: Pausing the program at some event or specified instruction, to + examine the current state. +* Evaluating: Basic REPL functionality, although [pry] does a better job at + that. +* Tracking: Keeping track of the different values of your variables or the + different lines executed by your program. + +## For enterprise + +Byebug for enterprise is available via the Tidelift Subscription. [Learn +more][Tidelift for enterprise]. + +## Build Status + +![ubuntu](https://github.com/deivid-rodriguez/byebug/workflows/ubuntu/badge.svg) +![windows](https://github.com/deivid-rodriguez/byebug/workflows/windows/badge.svg) + +## Requirements + +* _Required_: MRI 2.4.0 or higher. +* _Recommended_: MRI 2.6.4 or higher (MRI 2.6.0 to 2.6.3 contain a regression + causing unbalanced call/return events in some cases, breaking the `next` command). + +## Install + +```shell +gem install byebug +``` + +Alternatively, if you use `bundler`: + +```shell +bundle add byebug --group "development, test" +``` + +## Usage + +### From within the Ruby code + +Simply include `byebug` wherever you want to start debugging and the execution will +stop there. For example, if you were debugging Rails, you would add `byebug` to +your code: + +```ruby +def index + byebug + @articles = Article.find_recent +end +``` + +And then start a Rails server: + +```shell +bin/rails s +``` + +Once the execution gets to your `byebug` command, you will receive a debugging prompt. + +### From the command line + +If you want to debug a Ruby script without editing it, you can invoke byebug from the command line. + +```shell +byebug myscript.rb +``` + +## Byebug's commands + +Command | Aliases | Subcommands +------- | ------- | ----------- +`backtrace` | `bt` `w` `where`| +`break` | `b` | +`catch` | `cat` | +`condition` | `cond` | +`continue` | `c` `cont` | +`continue!` | `c!` `cont!` | +`debug` | | +`delete` | `del` | +`disable` | `dis` | `breakpoints` `display` +`display` | `disp` | +`down` | | +`edit` | `ed` | +`enable` | `en` | `breakpoints` `display` +`finish` | `fin` | +`frame` | `f` | +`help` | `h` | +`history` | `hist` | +`info` | `i` | `args` `breakpoints` `catch` `display` `file` `line` `program` +`interrupt` | `int` | +`irb` | | +`kill` | | +`list` | `l` | +`method` | `m` | `instance` +`next` | `n` | +`pry` | | +`quit` | `q` | +`quit!` | `q!` | +`restart` | | +`save` | `sa` | +`set` | | `autoirb` `autolist` `autopry` `autosave` `basename` `callstyle` `fullpath` `histfile` `histsize` `linetrace` `listsize` `post_mortem` `savefile` `stack_on_error` `width` +`show` | | `autoirb` `autolist` `autopry` `autosave` `basename` `callstyle` `fullpath` `histfile` `histsize` `linetrace` `listsize` `post_mortem` `savefile` `stack_on_error` `width` +`skip` | `sk` | +`source` | `so` | +`step` | `s` | +`thread` | `th` | `current` `list` `resume` `stop` `switch` +`tracevar` | `tr` | +`undisplay` | `undisp` | +`untracevar`| `untr` | +`up` | | +`var` | `v` | `all` `constant` `global` `instance` `local` + +## Semantic Versioning + +Byebug attempts to follow [semantic versioning](https://semver.org) and +bump major version only when backwards incompatible changes are released. +Backwards compatibility is targeted to [pry-byebug] and any other plugins +relying on `byebug`. + +## Getting Started + +Read [byebug's markdown +guide](https://github.com/deivid-rodriguez/byebug/blob/master/GUIDE.md) to get +started. Proper documentation will be eventually written. + +## Related projects + +* [pry-byebug] adds `next`, `step`, `finish`, `continue` and `break` commands + to `pry` using `byebug`. +* [ruby-debug-passenger] adds a rake task that restarts Passenger with Byebug + connected. +* [minitest-byebug] starts a byebug session on minitest failures. +* [sublime_debugger] provides a plugin for ruby debugging on Sublime Text. +* [atom-byebug] provides integration with the Atom editor [EXPERIMENTAL]. + +## Contribute + +See [Getting Started with Development](CONTRIBUTING.md). + +## Funding + +Subscribe to [Tidelift][Tidelift support] to ensure byebug stays actively +maintained, and at the same time get licensing assurances and timely security +notifications for your open source dependencies. + +You can also help `byebug` by leaving a small (or big) tip through [Liberapay]. + +## Security contact information + +Please use the Tidelift security contact to [report a security vulnerability]. +Tidelift will coordinate the fix and disclosure. + +## Credits + +Everybody who has ever contributed to this forked and reforked piece of +software, especially: + +* @ko1, author of the awesome TracePoint API for Ruby. +* @cldwalker, [debugger]'s maintainer. +* @denofevil, author of [debase], the starting point of this. +* @kevjames3 for testing, bug reports and the interest in the project. +* @FooBarWidget for working and helping with remote debugging. + +[debugger]: https://github.com/cldwalker/debugger +[pry]: https://github.com/pry/pry +[debase]: https://github.com/denofevil/debase +[pry-byebug]: https://github.com/deivid-rodriguez/pry-byebug +[ruby-debug-passenger]: https://github.com/davejamesmiller/ruby-debug-passenger +[minitest-byebug]: https://github.com/kaspth/minitest-byebug +[sublime_debugger]: https://github.com/shuky19/sublime_debugger +[atom-byebug]: https://github.com/izaera/atom-byebug +[Liberapay]: https://liberapay.com/byebug/donate +[Tidelift]: https://tidelift.com/subscription/pkg/rubygems-byebug?utm_source=rubygems-byebug&utm_medium=readme_text +[Tidelift for enterprise]: https://tidelift.com/subscription/pkg/rubygems-byebug?utm_source=rubygems-byebug&utm_medium=referral&utm_campaign=github&utm_content=enterprise +[Tidelift support]: https://tidelift.com/subscription/pkg/rubygems-byebug?utm_source=rubygems-byebug&utm_medium=referral&utm_campaign=github&utm_content=support +[report a security vulnerability]: https://tidelift.com/security diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/exe/byebug b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/exe/byebug new file mode 100755 index 0000000..d71be55 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/exe/byebug @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "byebug/runner" + +Byebug::Runner.new.run diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/.sitearchdir.-.byebug.time b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/.sitearchdir.-.byebug.time new file mode 100644 index 0000000..e69de29 diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/Makefile b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/Makefile new file mode 100644 index 0000000..643d8bc --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/Makefile @@ -0,0 +1,267 @@ + +SHELL = /bin/sh + +# V=0 quiet, V=1 verbose. other values don't work. +V = 0 +Q1 = $(V:1=) +Q = $(Q1:0=@) +ECHO1 = $(V:1=@ :) +ECHO = $(ECHO1:0=@ echo) +NULLCMD = : + +#### Start of system configuration section. #### + +srcdir = . +topdir = /opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0 +hdrdir = $(topdir) +arch_hdrdir = /opt/hostedtoolcache/Ruby/2.7.7/x64/include/ruby-2.7.0/x86_64-linux +PATH_SEPARATOR = : +VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby +prefix = $(DESTDIR)/opt/hostedtoolcache/Ruby/2.7.7/x64 +rubysitearchprefix = $(rubylibprefix)/$(sitearch) +rubyarchprefix = $(rubylibprefix)/$(arch) +rubylibprefix = $(libdir)/$(RUBY_BASE_NAME) +exec_prefix = $(prefix) +vendorarchhdrdir = $(vendorhdrdir)/$(sitearch) +sitearchhdrdir = $(sitehdrdir)/$(sitearch) +rubyarchhdrdir = $(rubyhdrdir)/$(arch) +vendorhdrdir = $(rubyhdrdir)/vendor_ruby +sitehdrdir = $(rubyhdrdir)/site_ruby +rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME) +vendorarchdir = $(vendorlibdir)/$(sitearch) +vendorlibdir = $(vendordir)/$(ruby_version) +vendordir = $(rubylibprefix)/vendor_ruby +sitearchdir = $(DESTDIR)./.gem.20221126-1610-1v6fje +sitelibdir = $(DESTDIR)./.gem.20221126-1610-1v6fje +sitedir = $(rubylibprefix)/site_ruby +rubyarchdir = $(rubylibdir)/$(arch) +rubylibdir = $(rubylibprefix)/$(ruby_version) +sitearchincludedir = $(includedir)/$(sitearch) +archincludedir = $(includedir)/$(arch) +sitearchlibdir = $(libdir)/$(sitearch) +archlibdir = $(libdir)/$(arch) +ridir = $(datarootdir)/$(RI_BASE_NAME) +mandir = $(datarootdir)/man +localedir = $(datarootdir)/locale +libdir = $(exec_prefix)/lib +psdir = $(docdir) +pdfdir = $(docdir) +dvidir = $(docdir) +htmldir = $(docdir) +infodir = $(datarootdir)/info +docdir = $(datarootdir)/doc/$(PACKAGE) +oldincludedir = $(DESTDIR)/usr/include +includedir = $(prefix)/include +runstatedir = $(localstatedir)/run +localstatedir = $(prefix)/var +sharedstatedir = $(prefix)/com +sysconfdir = $(prefix)/etc +datadir = $(datarootdir) +datarootdir = $(prefix)/share +libexecdir = $(exec_prefix)/libexec +sbindir = $(exec_prefix)/sbin +bindir = $(exec_prefix)/bin +archdir = $(rubyarchdir) + + +CC_WRAPPER = +CC = gcc +CXX = g++ +LIBRUBY = $(LIBRUBY_SO) +LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a +LIBRUBYARG_SHARED = -Wl,-rpath,$(libdir) -L$(libdir) -l$(RUBY_SO_NAME) +LIBRUBYARG_STATIC = -Wl,-rpath,$(libdir) -L$(libdir) -l$(RUBY_SO_NAME)-static $(MAINLIBS) +empty = +OUTFLAG = -o $(empty) +COUTFLAG = -o $(empty) +CSRCFLAG = $(empty) + +RUBY_EXTCONF_H = +cflags = $(optflags) $(debugflags) $(warnflags) +cxxflags = +optflags = -O3 +debugflags = -ggdb3 +warnflags = -Wall -Wextra -Wdeprecated-declarations -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wmisleading-indentation -Wpointer-arith -Wwrite-strings -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable +cppflags = +CCDLFLAGS = -fPIC +CFLAGS = $(CCDLFLAGS) -g -O2 -fPIC $(ARCH_FLAG) +INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir) +DEFS = +CPPFLAGS = -I/opt/hostedtoolcache/Ruby/2.7.7/x64/include -DENABLE_PATH_CHECK=0 $(DEFS) $(cppflags) +CXXFLAGS = $(CCDLFLAGS) -g -O2 $(ARCH_FLAG) +ldflags = -L. -L/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -fstack-protector-strong -rdynamic -Wl,-export-dynamic +dldflags = -L/opt/hostedtoolcache/Ruby/2.7.7/x64/lib -Wl,--compress-debug-sections=zlib +ARCH_FLAG = +DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG) +LDSHARED = $(CC) -shared +LDSHAREDXX = $(CXX) -shared +AR = ar +EXEEXT = + +RUBY_INSTALL_NAME = $(RUBY_BASE_NAME) +RUBY_SO_NAME = ruby +RUBYW_INSTALL_NAME = +RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version) +RUBYW_BASE_NAME = rubyw +RUBY_BASE_NAME = ruby + +arch = x86_64-linux +sitearch = $(arch) +ruby_version = 2.7.0 +ruby = $(bindir)/$(RUBY_BASE_NAME) +RUBY = $(ruby) +BUILTRUBY = $(bindir)/$(RUBY_BASE_NAME) +ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/backward.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h + +RM = rm -f +RM_RF = $(RUBY) -run -e rm -- -rf +RMDIRS = rmdir --ignore-fail-on-non-empty -p +MAKEDIRS = /usr/bin/mkdir -p +INSTALL = /usr/bin/install -c +INSTALL_PROG = $(INSTALL) -m 0755 +INSTALL_DATA = $(INSTALL) -m 644 +COPY = cp +TOUCH = exit > + +#### End of system configuration section. #### + +preload = +libpath = . $(libdir) +LIBPATH = -L. -L$(libdir) -Wl,-rpath,$(libdir) +DEFFILE = + +CLEANFILES = mkmf.log +DISTCLEANFILES = +DISTCLEANDIRS = + +extout = +extout_prefix = +target_prefix = /byebug +LOCAL_LIBS = +LIBS = $(LIBRUBYARG_SHARED) -lm -lc +ORIG_SRCS = breakpoint.c byebug.c context.c locker.c threads.c +SRCS = $(ORIG_SRCS) +OBJS = breakpoint.o byebug.o context.o locker.o threads.o +HDRS = $(srcdir)/byebug.h +LOCAL_HDRS = +TARGET = byebug +TARGET_NAME = byebug +TARGET_ENTRY = Init_$(TARGET_NAME) +DLLIB = $(TARGET).so +EXTSTATIC = +STATIC_LIB = + +TIMESTAMP_DIR = . +BINDIR = $(bindir) +RUBYCOMMONDIR = $(sitedir)$(target_prefix) +RUBYLIBDIR = $(sitelibdir)$(target_prefix) +RUBYARCHDIR = $(sitearchdir)$(target_prefix) +HDRDIR = $(rubyhdrdir)/ruby$(target_prefix) +ARCHHDRDIR = $(rubyhdrdir)/$(arch)/ruby$(target_prefix) +TARGET_SO_DIR = +TARGET_SO = $(TARGET_SO_DIR)$(DLLIB) +CLEANLIBS = $(TARGET_SO) +CLEANOBJS = *.o *.bak + +all: $(DLLIB) +static: $(STATIC_LIB) +.PHONY: all install static install-so install-rb +.PHONY: clean clean-so clean-static clean-rb + +clean-static:: +clean-rb-default:: +clean-rb:: +clean-so:: +clean: clean-so clean-static clean-rb-default clean-rb + -$(Q)$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time + +distclean-rb-default:: +distclean-rb:: +distclean-so:: +distclean-static:: +distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb + -$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log + -$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES) + -$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true + +realclean: distclean +install: install-so install-rb + +install-so: $(DLLIB) $(TIMESTAMP_DIR)/.sitearchdir.-.byebug.time + $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR) +clean-static:: + -$(Q)$(RM) $(STATIC_LIB) +install-rb: pre-install-rb do-install-rb install-rb-default +install-rb-default: pre-install-rb-default do-install-rb-default +pre-install-rb: Makefile +pre-install-rb-default: Makefile +do-install-rb: +do-install-rb-default: +pre-install-rb-default: + @$(NULLCMD) +$(TIMESTAMP_DIR)/.sitearchdir.-.byebug.time: + $(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR) + $(Q) $(TOUCH) $@ + +site-install: site-install-so site-install-rb +site-install-so: install-so +site-install-rb: install-rb + +.SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S + +.cc.o: + $(ECHO) compiling $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.cc.S: + $(ECHO) translating $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.mm.o: + $(ECHO) compiling $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.mm.S: + $(ECHO) translating $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.cxx.o: + $(ECHO) compiling $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.cxx.S: + $(ECHO) translating $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.cpp.o: + $(ECHO) compiling $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.cpp.S: + $(ECHO) translating $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.c.o: + $(ECHO) compiling $(<) + $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.c.S: + $(ECHO) translating $(<) + $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.m.o: + $(ECHO) compiling $(<) + $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.m.S: + $(ECHO) translating $(<) + $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +$(TARGET_SO): $(OBJS) Makefile + $(ECHO) linking shared-object byebug/$(DLLIB) + -$(Q)$(RM) $(@) + $(Q) $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS) + + + +$(OBJS): $(HDRS) $(ruby_headers) diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/breakpoint.c b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/breakpoint.c new file mode 100644 index 0000000..0047206 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/breakpoint.c @@ -0,0 +1,517 @@ +#include "byebug.h" + +#ifdef _WIN32 +#include +#endif + +#if defined DOSISH +#define isdirsep(x) ((x) == '/' || (x) == '\\') +#else +#define isdirsep(x) ((x) == '/') +#endif + +static VALUE cBreakpoint; +static int breakpoint_max; + +static ID idEval; + +static VALUE +eval_expression(VALUE args) +{ + return rb_funcall2(rb_mKernel, idEval, 2, RARRAY_PTR(args)); +} + +/* + * call-seq: + * breakpoint.enabled? -> bool + * + * Returns +true+ if breakpoint is enabled, false otherwise. + */ +static VALUE +brkpt_enabled(VALUE self) +{ + breakpoint_t *breakpoint; + + Data_Get_Struct(self, breakpoint_t, breakpoint); + return breakpoint->enabled; +} + +/* + * call-seq: + * breakpoint.enabled = true | false + * + * Enables or disables breakpoint. + */ +static VALUE +brkpt_set_enabled(VALUE self, VALUE enabled) +{ + breakpoint_t *breakpoint; + + Data_Get_Struct(self, breakpoint_t, breakpoint); + return breakpoint->enabled = enabled; +} + +/* + * call-seq: + * breakpoint.expr -> string + * + * Returns a conditional expression which indicates when this breakpoint should + * be activated. + */ +static VALUE +brkpt_expr(VALUE self) +{ + breakpoint_t *breakpoint; + + Data_Get_Struct(self, breakpoint_t, breakpoint); + return breakpoint->expr; +} + +/* + * call-seq: + * breakpoint.expr = string | nil + * + * Sets or unsets the conditional expression which indicates when this + * breakpoint should be activated. + */ +static VALUE +brkpt_set_expr(VALUE self, VALUE expr) +{ + breakpoint_t *breakpoint; + + Data_Get_Struct(self, breakpoint_t, breakpoint); + breakpoint->expr = NIL_P(expr) ? expr : StringValue(expr); + return expr; +} + +/* + * call-seq: + * breakpoint.hit_condition -> symbol + * + * Returns the hit condition of the breakpoint: +nil+ if it is an + * unconditional breakpoint, or :greater_or_equal, :equal or :modulo otherwise + */ +static VALUE +brkpt_hit_condition(VALUE self) +{ + breakpoint_t *breakpoint; + + Data_Get_Struct(self, breakpoint_t, breakpoint); + switch (breakpoint->hit_condition) + { + case HIT_COND_GE: + return ID2SYM(rb_intern("greater_or_equal")); + case HIT_COND_EQ: + return ID2SYM(rb_intern("equal")); + case HIT_COND_MOD: + return ID2SYM(rb_intern("modulo")); + case HIT_COND_NONE: + default: + return Qnil; + } +} + +/* + * call-seq: + * breakpoint.hit_condition = symbol + * + * Sets the hit condition of the breakpoint which must be one of the following + * values: + * + * +nil+ if it is an unconditional breakpoint, or + * :greater_or_equal(:ge), :equal(:eq), :modulo(:mod) + */ +static VALUE +brkpt_set_hit_condition(VALUE self, VALUE value) +{ + breakpoint_t *breakpoint; + ID id_value; + + Data_Get_Struct(self, breakpoint_t, breakpoint); + id_value = rb_to_id(value); + + if (rb_intern("greater_or_equal") == id_value || rb_intern("ge") == id_value) + breakpoint->hit_condition = HIT_COND_GE; + else if (rb_intern("equal") == id_value || rb_intern("eq") == id_value) + breakpoint->hit_condition = HIT_COND_EQ; + else if (rb_intern("modulo") == id_value || rb_intern("mod") == id_value) + breakpoint->hit_condition = HIT_COND_MOD; + else + rb_raise(rb_eArgError, "Invalid condition parameter"); + return value; +} + +/* + * call-seq: + * breakpoint.hit_count -> int + * + * Returns the number of times this breakpoint has been hit. + */ +static VALUE +brkpt_hit_count(VALUE self) +{ + breakpoint_t *breakpoint; + + Data_Get_Struct(self, breakpoint_t, breakpoint); + return INT2FIX(breakpoint->hit_count); +} + +/* + * call-seq: + * breakpoint.hit_value -> int + * + * Returns the hit value of the breakpoint, namely, a value to build a + * condition on the number of hits of the breakpoint. + */ +static VALUE +brkpt_hit_value(VALUE self) +{ + breakpoint_t *breakpoint; + + Data_Get_Struct(self, breakpoint_t, breakpoint); + return INT2FIX(breakpoint->hit_value); +} + +/* + * call-seq: + * breakpoint.hit_value = int + * + * Sets the hit value of the breakpoint. This allows the user to set conditions + * on the number of hits to enable/disable the breakpoint. + */ +static VALUE +brkpt_set_hit_value(VALUE self, VALUE value) +{ + breakpoint_t *breakpoint; + + Data_Get_Struct(self, breakpoint_t, breakpoint); + breakpoint->hit_value = FIX2INT(value); + return value; +} + +/* + * call-seq: + * breakpoint.id -> int + * + * Returns the id of the breakpoint. + */ +static VALUE +brkpt_id(VALUE self) +{ + breakpoint_t *breakpoint; + + Data_Get_Struct(self, breakpoint_t, breakpoint); + return INT2FIX(breakpoint->id); +} + +/* + * call-seq: + * breakpoint.pos -> string or int + * + * Returns the position of this breakpoint, either a method name or a line + * number. + */ +static VALUE +brkpt_pos(VALUE self) +{ + breakpoint_t *breakpoint; + + Data_Get_Struct(self, breakpoint_t, breakpoint); + if (breakpoint->type == BP_METHOD_TYPE) + return rb_str_new2(rb_id2name(breakpoint->pos.mid)); + else + return INT2FIX(breakpoint->pos.line); +} + +/* + * call-seq: + * breakpoint.source -> string + * + * Returns the source file of the breakpoint. + */ +static VALUE +brkpt_source(VALUE self) +{ + breakpoint_t *breakpoint; + + Data_Get_Struct(self, breakpoint_t, breakpoint); + return breakpoint->source; +} + +static void +mark_breakpoint(breakpoint_t *breakpoint) +{ + rb_gc_mark(breakpoint->source); + rb_gc_mark(breakpoint->expr); +} + +static VALUE +brkpt_create(VALUE klass) +{ + breakpoint_t *breakpoint = ALLOC(breakpoint_t); + + return Data_Wrap_Struct(klass, mark_breakpoint, xfree, breakpoint); +} + +static VALUE +brkpt_initialize(VALUE self, VALUE source, VALUE pos, VALUE expr) +{ + breakpoint_t *breakpoint; + + Data_Get_Struct(self, breakpoint_t, breakpoint); + + breakpoint->type = FIXNUM_P(pos) ? BP_POS_TYPE : BP_METHOD_TYPE; + if (breakpoint->type == BP_POS_TYPE) + breakpoint->pos.line = FIX2INT(pos); + else + breakpoint->pos.mid = SYM2ID(pos); + + breakpoint->id = ++breakpoint_max; + breakpoint->source = StringValue(source); + breakpoint->enabled = Qtrue; + breakpoint->expr = NIL_P(expr) ? expr : StringValue(expr); + breakpoint->hit_count = 0; + breakpoint->hit_value = 0; + breakpoint->hit_condition = HIT_COND_NONE; + + return Qnil; +} + +static int +filename_cmp_impl(VALUE source, char *file) +{ + char *source_ptr, *file_ptr; + long s_len, f_len, min_len; + long s, f; + int dirsep_flag = 0; + + s_len = RSTRING_LEN(source); + f_len = strlen(file); + min_len = s_len < f_len ? s_len : f_len; + + source_ptr = RSTRING_PTR(source); + file_ptr = file; + + for (s = s_len - 1, f = f_len - 1; + s >= s_len - min_len && f >= f_len - min_len; s--, f--) + { + if ((source_ptr[s] == '.' || file_ptr[f] == '.') && dirsep_flag) + return 1; + if (isdirsep(source_ptr[s]) && isdirsep(file_ptr[f])) + dirsep_flag = 1; +#ifdef DOSISH_DRIVE_LETTER + else if (s == 0) + return (toupper(source_ptr[s]) == toupper(file_ptr[f])); +#endif + else if (source_ptr[s] != file_ptr[f]) + return 0; + } + return 1; +} + +static int +filename_cmp(VALUE source, char *file) +{ +#ifdef _WIN32 + return filename_cmp_impl(source, file); +#else +#ifdef PATH_MAX + char path[PATH_MAX + 1]; + + path[PATH_MAX] = 0; + return filename_cmp_impl(source, realpath(file, path) != NULL ? path : file); +#else + char *path; + int result; + + path = realpath(file, NULL); + result = filename_cmp_impl(source, path == NULL ? file : path); + free(path); + return result; +#endif +#endif +} + +static int +classname_cmp(VALUE name, VALUE klass) +{ + VALUE mod_name; + VALUE class_name = NIL_P(name) ? rb_str_new2("main") : name; + + if (NIL_P(klass)) + return 0; + + mod_name = rb_mod_name(klass); + return (!NIL_P(mod_name) && rb_str_cmp(class_name, mod_name) == 0); +} + +static int +check_breakpoint_by_hit_condition(VALUE rb_breakpoint) +{ + breakpoint_t *breakpoint; + + if (NIL_P(rb_breakpoint)) + return 0; + + Data_Get_Struct(rb_breakpoint, breakpoint_t, breakpoint); + breakpoint->hit_count++; + + if (Qtrue != breakpoint->enabled) + return 0; + + switch (breakpoint->hit_condition) + { + case HIT_COND_NONE: + return 1; + case HIT_COND_GE: + { + if (breakpoint->hit_count >= breakpoint->hit_value) + return 1; + break; + } + case HIT_COND_EQ: + { + if (breakpoint->hit_count == breakpoint->hit_value) + return 1; + break; + } + case HIT_COND_MOD: + { + if (breakpoint->hit_count % breakpoint->hit_value == 0) + return 1; + break; + } + } + return 0; +} + +static int +check_breakpoint_by_pos(VALUE rb_breakpoint, char *file, int line) +{ + breakpoint_t *breakpoint; + + if (NIL_P(rb_breakpoint)) + return 0; + + Data_Get_Struct(rb_breakpoint, breakpoint_t, breakpoint); + + if (Qfalse == breakpoint->enabled || breakpoint->type != BP_POS_TYPE + || breakpoint->pos.line != line) + return 0; + + return filename_cmp(breakpoint->source, file); +} + +static int +check_breakpoint_by_method(VALUE rb_breakpoint, VALUE klass, ID mid, VALUE self) +{ + breakpoint_t *breakpoint; + + if (NIL_P(rb_breakpoint)) + return 0; + + Data_Get_Struct(rb_breakpoint, breakpoint_t, breakpoint); + + if (Qfalse == breakpoint->enabled || breakpoint->type != BP_METHOD_TYPE + || breakpoint->pos.mid != mid) + return 0; + + if (classname_cmp(breakpoint->source, klass) + || ((rb_type(self) == T_CLASS || rb_type(self) == T_MODULE) + && classname_cmp(breakpoint->source, self))) + return 1; + + return 0; +} + +static int +check_breakpoint_by_expr(VALUE rb_breakpoint, VALUE bind) +{ + breakpoint_t *breakpoint; + VALUE args, expr_result; + + if (NIL_P(rb_breakpoint)) + return 0; + + Data_Get_Struct(rb_breakpoint, breakpoint_t, breakpoint); + + if (Qfalse == breakpoint->enabled) + return 0; + + if (NIL_P(breakpoint->expr)) + return 1; + + args = rb_ary_new3(2, breakpoint->expr, bind); + expr_result = rb_protect(eval_expression, args, 0); + + return RTEST(expr_result); +} + +extern VALUE +find_breakpoint_by_pos(VALUE breakpoints, VALUE source, VALUE pos, VALUE bind) +{ + VALUE breakpoint; + char *file; + int line; + int i; + + file = RSTRING_PTR(source); + line = FIX2INT(pos); + for (i = 0; i < RARRAY_LENINT(breakpoints); i++) + { + breakpoint = rb_ary_entry(breakpoints, i); + if (check_breakpoint_by_pos(breakpoint, file, line) + && check_breakpoint_by_expr(breakpoint, bind) + && check_breakpoint_by_hit_condition(breakpoint)) + { + return breakpoint; + } + } + return Qnil; +} + +extern VALUE +find_breakpoint_by_method(VALUE breakpoints, VALUE klass, ID mid, VALUE bind, + VALUE self) +{ + VALUE breakpoint; + int i; + + for (i = 0; i < RARRAY_LENINT(breakpoints); i++) + { + breakpoint = rb_ary_entry(breakpoints, i); + if (check_breakpoint_by_method(breakpoint, klass, mid, self) + && check_breakpoint_by_expr(breakpoint, bind) + && check_breakpoint_by_hit_condition(breakpoint)) + { + return breakpoint; + } + } + return Qnil; +} + +void +Init_byebug_breakpoint(VALUE mByebug) +{ + breakpoint_max = 0; + + cBreakpoint = rb_define_class_under(mByebug, "Breakpoint", rb_cObject); + + rb_define_alloc_func(cBreakpoint, brkpt_create); + rb_define_method(cBreakpoint, "initialize", brkpt_initialize, 3); + + rb_define_method(cBreakpoint, "enabled?", brkpt_enabled, 0); + rb_define_method(cBreakpoint, "enabled=", brkpt_set_enabled, 1); + rb_define_method(cBreakpoint, "expr", brkpt_expr, 0); + rb_define_method(cBreakpoint, "expr=", brkpt_set_expr, 1); + rb_define_method(cBreakpoint, "hit_count", brkpt_hit_count, 0); + rb_define_method(cBreakpoint, "hit_condition", brkpt_hit_condition, 0); + rb_define_method(cBreakpoint, "hit_condition=", brkpt_set_hit_condition, 1); + rb_define_method(cBreakpoint, "hit_value", brkpt_hit_value, 0); + rb_define_method(cBreakpoint, "hit_value=", brkpt_set_hit_value, 1); + rb_define_method(cBreakpoint, "id", brkpt_id, 0); + rb_define_method(cBreakpoint, "pos", brkpt_pos, 0); + rb_define_method(cBreakpoint, "source", brkpt_source, 0); + + idEval = rb_intern("eval"); +} diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/breakpoint.o b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/breakpoint.o new file mode 100644 index 0000000..7b46f9a Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/breakpoint.o differ diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/byebug.c b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/byebug.c new file mode 100644 index 0000000..51630aa --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/byebug.c @@ -0,0 +1,900 @@ +#include "byebug.h" + +static VALUE mByebug; /* Ruby Byebug Module object */ + +static VALUE tracing = Qfalse; +static VALUE post_mortem = Qfalse; +static VALUE verbose = Qfalse; + +static VALUE catchpoints = Qnil; +static VALUE breakpoints = Qnil; +static VALUE tracepoints = Qnil; + +static VALUE raised_exception = Qnil; + +static ID idPuts; +static ID idEmpty; + +/* Hash table with active threads and their associated contexts */ +VALUE threads = Qnil; + +/* + * call-seq: + * Byebug.breakpoints -> array + * + * Returns an array of breakpoints. + */ +static VALUE +Breakpoints(VALUE self) +{ + UNUSED(self); + + if (NIL_P(breakpoints)) + breakpoints = rb_ary_new(); + + return breakpoints; +} + +/* + * call-seq: + * Byebug.catchpoints -> hash + * + * Returns the catchpoints hash. + */ +static VALUE +Catchpoints(VALUE self) +{ + UNUSED(self); + + return catchpoints; +} + +/* + * call-seq: + * Byebug.raised_exception -> exception + * + * Returns raised exception when in post_mortem mode. + */ +static VALUE +Raised_exception(VALUE self) +{ + UNUSED(self); + + return raised_exception; +} + +#define IS_STARTED (!NIL_P(catchpoints)) + +static void +check_started() +{ + if (!IS_STARTED) + { + rb_raise(rb_eRuntimeError, "Byebug is not started yet."); + } +} + +static void +trace_print(rb_trace_arg_t *trace_arg, debug_context_t *dc, + const char *file_filter, const char *debug_msg) +{ + char *fullpath = NULL; + const char *basename; + int filtered = 0; + const char *event = rb_id2name(SYM2ID(rb_tracearg_event(trace_arg))); + + VALUE rb_path = rb_tracearg_path(trace_arg); + const char *path = NIL_P(rb_path) ? "" : RSTRING_PTR(rb_path); + + int line = NUM2INT(rb_tracearg_lineno(trace_arg)); + + VALUE rb_mid = rb_tracearg_method_id(trace_arg); + const char *mid = NIL_P(rb_mid) ? "(top level)" : rb_id2name(SYM2ID(rb_mid)); + + VALUE rb_cl = rb_tracearg_defined_class(trace_arg); + VALUE rb_cl_name = NIL_P(rb_cl) ? rb_cl : rb_mod_name(rb_cl); + const char *defined_class = NIL_P(rb_cl_name) ? "" : RSTRING_PTR(rb_cl_name); + + if (!trace_arg) + return; + + if (file_filter) + { +#ifndef _WIN32 + fullpath = realpath(path, NULL); +#endif + basename = fullpath ? strrchr(fullpath, '/') : path; + + if (!basename || strncmp(basename + 1, file_filter, strlen(file_filter))) + filtered = 1; + +#ifndef _WIN32 + free(fullpath); +#endif + } + + if (!filtered) + { + if (debug_msg) + rb_funcall(mByebug, idPuts, 1, + rb_sprintf("[#%d] %s\n", dc->thnum, debug_msg)); + else + rb_funcall(mByebug, idPuts, 1, + rb_sprintf("%*s [#%d] %s@%s:%d %s#%s\n", dc->calced_stack_size, + "", dc->thnum, event, path, line, defined_class, + mid)); + } +} + +static void +cleanup(debug_context_t *dc) +{ + dc->stop_reason = CTX_STOP_NONE; + + release_lock(); +} + +#define EVENT_TEARDOWN cleanup(dc); + +#define EVENT_SETUP \ + debug_context_t *dc; \ + VALUE context; \ + rb_trace_arg_t *trace_arg; \ + \ + UNUSED(data); \ + \ + if (!is_living_thread(rb_thread_current())) \ + return; \ + \ + thread_context_lookup(rb_thread_current(), &context); \ + Data_Get_Struct(context, debug_context_t, dc); \ + \ + trace_arg = rb_tracearg_from_tracepoint(trace_point); \ + if (verbose == Qtrue) \ + trace_print(trace_arg, dc, 0, 0); \ + \ + if (CTX_FL_TEST(dc, CTX_FL_IGNORE)) \ + return; \ + \ + acquire_lock(dc); + + +#define CALL_EVENT_SETUP \ + dc->calced_stack_size++; \ + dc->steps_out = dc->steps_out < 0 ? -1 : dc->steps_out + 1; + +#define RETURN_EVENT_SETUP \ + dc->calced_stack_size--; \ + \ + if (dc->steps_out == 1) \ + dc->steps = 1; + +#define RETURN_EVENT_TEARDOWN \ + dc->steps_out = dc->steps_out <= 0 ? -1 : dc->steps_out - 1; + + +/* Functions that return control to byebug after the different events */ + +static VALUE +call_at(VALUE ctx, debug_context_t *dc, ID mid, int argc, VALUE arg) +{ + struct call_with_inspection_data cwi; + VALUE argv[1]; + + argv[0] = arg; + + cwi.dc = dc; + cwi.ctx = ctx; + cwi.id = mid; + cwi.argc = argc; + cwi.argv = &argv[0]; + + return call_with_debug_inspector(&cwi); +} + +static VALUE +call_at_line(VALUE ctx, debug_context_t *dc) +{ + return call_at(ctx, dc, rb_intern("at_line"), 0, Qnil); +} + +static VALUE +call_at_tracing(VALUE ctx, debug_context_t *dc) +{ + return call_at(ctx, dc, rb_intern("at_tracing"), 0, Qnil); +} + +static VALUE +call_at_breakpoint(VALUE ctx, debug_context_t *dc, VALUE breakpoint) +{ + dc->stop_reason = CTX_STOP_BREAKPOINT; + + return call_at(ctx, dc, rb_intern("at_breakpoint"), 1, breakpoint); +} + +static VALUE +call_at_catchpoint(VALUE ctx, debug_context_t *dc, VALUE exp) +{ + dc->stop_reason = CTX_STOP_CATCHPOINT; + + return call_at(ctx, dc, rb_intern("at_catchpoint"), 1, exp); +} + +static VALUE +call_at_return(VALUE ctx, debug_context_t *dc, VALUE return_value) +{ + dc->stop_reason = CTX_STOP_BREAKPOINT; + + return call_at(ctx, dc, rb_intern("at_return"), 1, return_value); +} + +static VALUE +call_at_end(VALUE ctx, debug_context_t *dc) +{ + dc->stop_reason = CTX_STOP_BREAKPOINT; + + return call_at(ctx, dc, rb_intern("at_end"), 0, Qnil); +} + +static void +call_at_line_check(VALUE ctx, debug_context_t *dc, VALUE breakpoint) +{ + dc->stop_reason = CTX_STOP_STEP; + + if (!NIL_P(breakpoint)) + call_at_breakpoint(ctx, dc, breakpoint); + + byebug_reset_stepping_stop_points(dc); + + call_at_line(ctx, dc); +} + + +/* TracePoint API event handlers */ + +static void +line_event(VALUE trace_point, void *data) +{ + VALUE brkpnt, file, line, binding; + + EVENT_SETUP; + + file = rb_tracearg_path(trace_arg); + line = rb_tracearg_lineno(trace_arg); + binding = rb_tracearg_binding(trace_arg); + + if (RTEST(tracing)) + call_at_tracing(context, dc); + + if (!CTX_FL_TEST(dc, CTX_FL_IGNORE_STEPS)) + dc->steps = dc->steps <= 0 ? -1 : dc->steps - 1; + + if (dc->calced_stack_size <= dc->dest_frame) + { + dc->dest_frame = dc->calced_stack_size; + CTX_FL_UNSET(dc, CTX_FL_IGNORE_STEPS); + + dc->lines = dc->lines <= 0 ? -1 : dc->lines - 1; + } + + if (dc->steps == 0 || dc->lines == 0) + call_at_line_check(context, dc, Qnil); + else + { + brkpnt = Qnil; + + if (!NIL_P(breakpoints)) + brkpnt = find_breakpoint_by_pos(breakpoints, file, line, binding); + + if (!NIL_P(brkpnt)) + call_at_line_check(context, dc, brkpnt); + } + + EVENT_TEARDOWN; +} + +static void +call_event(VALUE trace_point, void *data) +{ + VALUE brkpnt, klass, msym, mid, binding, self; + + EVENT_SETUP; + + if (dc->calced_stack_size <= dc->dest_frame) + CTX_FL_UNSET(dc, CTX_FL_IGNORE_STEPS); + + CALL_EVENT_SETUP; + + msym = rb_tracearg_method_id(trace_arg); + + mid = SYM2ID(msym); + klass = rb_tracearg_defined_class(trace_arg); + binding = rb_tracearg_binding(trace_arg); + self = rb_tracearg_self(trace_arg); + + brkpnt = Qnil; + + if (!NIL_P(breakpoints)) + brkpnt = find_breakpoint_by_method(breakpoints, klass, mid, binding, self); + + if (!NIL_P(brkpnt)) + { + call_at_breakpoint(context, dc, brkpnt); + call_at_line(context, dc); + } + + EVENT_TEARDOWN; +} + +static void +return_event(VALUE trace_point, void *data) +{ + VALUE brkpnt, file, line, binding; + + EVENT_SETUP; + + RETURN_EVENT_SETUP; + + if ((dc->steps_out == 0) && (CTX_FL_TEST(dc, CTX_FL_STOP_ON_RET))) + { + byebug_reset_stepping_stop_points(dc); + + call_at_return(context, dc, rb_tracearg_return_value(trace_arg)); + } + else if (!NIL_P(breakpoints)) + { + file = rb_tracearg_path(trace_arg); + /* + * @todo Sometimes the TracePoint API gives some return events without + * file:line information, so we need to guard for nil until we know what's + * going on. This happens, for example, with active_support core extensions: + * + * [#7] call@.../core_ext/numeric/conversions.rb:124 Fixnum#to_s + * [#7] b_call@.../core_ext/numeric/conversions.rb:124 BigDecimal#to_s + * [#7] line@.../core_ext/numeric/conversions.rb:125 BigDecimal#to_s + * [#7] c_call@.../core_ext/numeric/conversions.rb:125 Kernel#is_a? + * [#7] c_return@.../core_ext/numeric/conversions.rb:125 Kernel#is_a? + * [#7] line@.../core_ext/numeric/conversions.rb:131 BigDecimal#to_s + * [#7] c_call@.../core_ext/numeric/conversions.rb:131 Fixnum#to_default_s + * [#7] c_return@.../core_ext/numeric/conversions.rb:131 Fixnum#to_default_s + * [#7] b_return@/hort/core_ext/numeric/conversions.rb:133 BigDecimal#to_s + * [#7] return@:0 Fixnum#to_s # => This guy... + */ + if (NIL_P(file)) + rb_warn("The TracePoint API emitted a return event without file information. It might be a bug, please report this."); + else + { + line = rb_tracearg_lineno(trace_arg); + binding = rb_tracearg_binding(trace_arg); + + brkpnt = find_breakpoint_by_pos(breakpoints, file, line, binding); + + if (!NIL_P(brkpnt)) + call_at_return(context, dc, rb_tracearg_return_value(trace_arg)); + } + } + + RETURN_EVENT_TEARDOWN; + + EVENT_TEARDOWN; +} + +static void +end_event(VALUE trace_point, void *data) +{ + EVENT_SETUP; + + RETURN_EVENT_SETUP; + + if ((dc->steps_out == 0) && (CTX_FL_TEST(dc, CTX_FL_STOP_ON_RET))) + { + byebug_reset_stepping_stop_points(dc); + + call_at_end(context, dc); + } + + RETURN_EVENT_TEARDOWN; + + EVENT_TEARDOWN; +} + +static void +raw_call_event(VALUE trace_point, void *data) +{ + EVENT_SETUP; + + CALL_EVENT_SETUP; + + EVENT_TEARDOWN; +} + +static void +raw_return_event(VALUE trace_point, void *data) +{ + EVENT_SETUP; + + RETURN_EVENT_SETUP; + + RETURN_EVENT_TEARDOWN; + + EVENT_TEARDOWN; +} + +static void +raise_event(VALUE trace_point, void *data) +{ + VALUE expn_class, ancestors, pm_context; + int i; + debug_context_t *new_dc; + + EVENT_SETUP; + + raised_exception = rb_tracearg_raised_exception(trace_arg); + + if (post_mortem == Qtrue && !rb_ivar_defined(raised_exception, rb_intern("@__bb_context"))) + { + pm_context = context_dup(dc); + rb_ivar_set(raised_exception, rb_intern("@__bb_context"), pm_context); + + Data_Get_Struct(pm_context, debug_context_t, new_dc); + rb_debug_inspector_open(context_backtrace_set, (void *)new_dc); + } + + if (NIL_P(catchpoints) || dc->calced_stack_size == 0 + || RHASH_TBL(catchpoints)->num_entries == 0) + { + EVENT_TEARDOWN; + return; + } + + expn_class = rb_obj_class(raised_exception); + ancestors = rb_mod_ancestors(expn_class); + for (i = 0; i < RARRAY_LENINT(ancestors); i++) + { + VALUE ancestor_class, module_name, hit_count; + + ancestor_class = rb_ary_entry(ancestors, i); + module_name = rb_mod_name(ancestor_class); + hit_count = rb_hash_aref(catchpoints, module_name); + + /* increment exception */ + if (!NIL_P(hit_count)) + { + rb_hash_aset(catchpoints, module_name, INT2FIX(FIX2INT(hit_count) + 1)); + + call_at_catchpoint(context, dc, raised_exception); + call_at_line(context, dc); + + break; + } + } + + EVENT_TEARDOWN; +} + + +/* Setup TracePoint functionality */ + +static void +register_tracepoints(VALUE self) +{ + int i; + VALUE traces = tracepoints; + + UNUSED(self); + + if (NIL_P(traces)) + { + int line_msk = RUBY_EVENT_LINE; + int call_msk = RUBY_EVENT_CALL; + int ret_msk = RUBY_EVENT_RETURN | RUBY_EVENT_B_RETURN; + int end_msk = RUBY_EVENT_END; + int raw_call_msk = RUBY_EVENT_C_CALL | RUBY_EVENT_B_CALL | RUBY_EVENT_CLASS; + int raw_ret_msk = RUBY_EVENT_C_RETURN; + int raise_msk = RUBY_EVENT_RAISE; + + VALUE tpLine = rb_tracepoint_new(Qnil, line_msk, line_event, 0); + VALUE tpCall = rb_tracepoint_new(Qnil, call_msk, call_event, 0); + VALUE tpReturn = rb_tracepoint_new(Qnil, ret_msk, return_event, 0); + VALUE tpEnd = rb_tracepoint_new(Qnil, end_msk, end_event, 0); + VALUE tpCCall = rb_tracepoint_new(Qnil, raw_call_msk, raw_call_event, 0); + VALUE tpCReturn = rb_tracepoint_new(Qnil, raw_ret_msk, raw_return_event, 0); + VALUE tpRaise = rb_tracepoint_new(Qnil, raise_msk, raise_event, 0); + + traces = rb_ary_new(); + rb_ary_push(traces, tpLine); + rb_ary_push(traces, tpCall); + rb_ary_push(traces, tpReturn); + rb_ary_push(traces, tpEnd); + rb_ary_push(traces, tpCCall); + rb_ary_push(traces, tpCReturn); + rb_ary_push(traces, tpRaise); + + tracepoints = traces; + } + + for (i = 0; i < RARRAY_LENINT(traces); i++) + rb_tracepoint_enable(rb_ary_entry(traces, i)); +} + +static void +clear_tracepoints(VALUE self) +{ + int i; + + UNUSED(self); + + for (i = RARRAY_LENINT(tracepoints) - 1; i >= 0; i--) + rb_tracepoint_disable(rb_ary_entry(tracepoints, i)); +} + + +/* Byebug's Public API */ + +/* + * call-seq: + * Byebug.contexts -> array + * + * Returns an array of all contexts. + */ +static VALUE +Contexts(VALUE self) +{ + volatile VALUE list; + volatile VALUE new_list; + VALUE context; + threads_table_t *t_tbl; + debug_context_t *dc; + int i; + + UNUSED(self); + + check_started(); + + new_list = rb_ary_new(); + list = rb_funcall(rb_cThread, rb_intern("list"), 0); + + for (i = 0; i < RARRAY_LENINT(list); i++) + { + VALUE thread = rb_ary_entry(list, i); + + thread_context_lookup(thread, &context); + rb_ary_push(new_list, context); + } + + Data_Get_Struct(threads, threads_table_t, t_tbl); + st_clear(t_tbl->tbl); + + for (i = 0; i < RARRAY_LENINT(new_list); i++) + { + context = rb_ary_entry(new_list, i); + Data_Get_Struct(context, debug_context_t, dc); + st_insert(t_tbl->tbl, dc->thread, context); + } + + return new_list; +} + +/* + * call-seq: + * Byebug.thread_context(thread) -> context + * + * Returns context of the thread passed as an argument. + */ +static VALUE +Thread_context(VALUE self, VALUE thread) +{ + VALUE context; + + UNUSED(self); + + check_started(); + + thread_context_lookup(thread, &context); + + return context; +} + +/* + * call-seq: + * Byebug.current_context -> context + * + * Returns the current context. + * Note: Byebug.current_context.thread == Thread.current + */ +static VALUE +Current_context(VALUE self) +{ + VALUE context; + + UNUSED(self); + + thread_context_lookup(rb_thread_current(), &context); + + return context; +} + +/* + * call-seq: + * Byebug.started? -> bool + * + * Returns +true+ byebug is started. + */ +static VALUE +Started(VALUE self) +{ + UNUSED(self); + + return IS_STARTED ? Qtrue : Qfalse; +} + +/* + * call-seq: + * Byebug.stop -> bool + * + * This method disables byebug. It returns +true+ if byebug was already + * disabled, otherwise it returns +false+. + */ +static VALUE +Stop(VALUE self) +{ + UNUSED(self); + + if (IS_STARTED) + { + clear_tracepoints(self); + + breakpoints = Qnil; + catchpoints = Qnil; + + return Qfalse; + } + + return Qtrue; +} + +static VALUE +Stoppable(VALUE self) +{ + VALUE context; + debug_context_t *dc; + + if (!IS_STARTED) + return Qfalse; + + if (!NIL_P(breakpoints) && rb_funcall(breakpoints, idEmpty, 0) == Qfalse) + return Qfalse; + + if (!NIL_P(catchpoints) && rb_funcall(catchpoints, idEmpty, 0) == Qfalse) + return Qfalse; + + if (post_mortem == Qtrue) + return Qfalse; + + if (RTEST(tracing)) + return Qfalse; + + context = Current_context(self); + if (!NIL_P(context)) + { + Data_Get_Struct(context, debug_context_t, dc); + + if (dc->steps > 0) + return Qfalse; + } + + return Qtrue; +} + +/* + * call-seq: + * Byebug.start -> bool + * + * The return value is the value of !Byebug.started? before issuing the + * +start+; That is, +true+ is returned, unless byebug was previously started. + */ +static VALUE +Start(VALUE self) +{ + if (IS_STARTED) + return Qfalse; + + catchpoints = rb_hash_new(); + + threads = create_threads_table(); + + register_tracepoints(self); + + return Qtrue; +} + +/* + * call-seq: + * Byebug.debug_load(file, stop = false) -> nil + * + * Same as Kernel#load but resets current context's frames. + * +stop+ parameter forces byebug to stop at the first line of code in +file+ + */ +static VALUE +Debug_load(int argc, VALUE *argv, VALUE self) +{ + VALUE file, stop, context; + debug_context_t *dc; + VALUE status = Qnil; + int state = 0; + + UNUSED(self); + + if (rb_scan_args(argc, argv, "11", &file, &stop) == 1) + stop = Qfalse; + + Start(self); + + context = Current_context(self); + Data_Get_Struct(context, debug_context_t, dc); + + dc->calced_stack_size = 1; + + if (RTEST(stop)) + dc->steps = 1; + + rb_load_protect(file, 0, &state); + if (0 != state) + { + status = rb_errinfo(); + byebug_reset_stepping_stop_points(dc); + } + + return status; +} + +/* + * call-seq: + * Byebug.tracing? -> bool + * + * Returns +true+ if global tracing is enabled. + */ +static VALUE +Tracing(VALUE self) +{ + UNUSED(self); + + return tracing; +} + +/* + * call-seq: + * Byebug.tracing = bool + * + * Sets the global tracing flag. + */ +static VALUE +Set_tracing(VALUE self, VALUE value) +{ + UNUSED(self); + + tracing = RTEST(value) ? Qtrue : Qfalse; + return value; +} + +/* + * call-seq: + * Byebug.verbose? -> bool + * + * Returns +true+ if global verbose flag for TracePoint API events is enabled. + */ +static VALUE +Verbose(VALUE self) +{ + UNUSED(self); + + return verbose; +} + +/* + * call-seq: + * Byebug.verbose = bool + * + * Sets the global verbose flag for TracePoint API events is enabled. + */ +static VALUE +Set_verbose(VALUE self, VALUE value) +{ + UNUSED(self); + + verbose = RTEST(value) ? Qtrue : Qfalse; + return value; +} + +/* + * call-seq: + * Byebug.post_mortem? -> bool + * + * Returns +true+ if post-mortem debugging is enabled. + */ +static VALUE +Post_mortem(VALUE self) +{ + UNUSED(self); + + return post_mortem; +} + +/* + * call-seq: + * Byebug.post_mortem = bool + * + * Sets post-moterm flag. + */ +static VALUE +Set_post_mortem(VALUE self, VALUE value) +{ + UNUSED(self); + + post_mortem = RTEST(value) ? Qtrue : Qfalse; + return value; +} + +/* + * call-seq: + * Byebug.add_catchpoint(exception) -> exception + * + * Adds a new exception to the catchpoints hash. + */ +static VALUE +Add_catchpoint(VALUE self, VALUE value) +{ + UNUSED(self); + + if (TYPE(value) != T_STRING) + rb_raise(rb_eTypeError, "value of a catchpoint must be String"); + + rb_hash_aset(catchpoints, rb_str_dup(value), INT2FIX(0)); + return value; +} + +/* + * Document-class: Byebug + * + * == Summary + * + * This is a singleton class allows controlling byebug. Use it to start/stop + * byebug, set/remove breakpoints, etc. + */ +void +Init_byebug() +{ + mByebug = rb_define_module("Byebug"); + + rb_define_module_function(mByebug, "add_catchpoint", Add_catchpoint, 1); + rb_define_module_function(mByebug, "breakpoints", Breakpoints, 0); + rb_define_module_function(mByebug, "catchpoints", Catchpoints, 0); + rb_define_module_function(mByebug, "contexts", Contexts, 0); + rb_define_module_function(mByebug, "current_context", Current_context, 0); + rb_define_module_function(mByebug, "debug_load", Debug_load, -1); + rb_define_module_function(mByebug, "post_mortem?", Post_mortem, 0); + rb_define_module_function(mByebug, "post_mortem=", Set_post_mortem, 1); + rb_define_module_function(mByebug, "raised_exception", Raised_exception, 0); + rb_define_module_function(mByebug, "start", Start, 0); + rb_define_module_function(mByebug, "started?", Started, 0); + rb_define_module_function(mByebug, "stop", Stop, 0); + rb_define_module_function(mByebug, "stoppable?", Stoppable, 0); + rb_define_module_function(mByebug, "thread_context", Thread_context, 1); + rb_define_module_function(mByebug, "tracing?", Tracing, 0); + rb_define_module_function(mByebug, "tracing=", Set_tracing, 1); + rb_define_module_function(mByebug, "verbose?", Verbose, 0); + rb_define_module_function(mByebug, "verbose=", Set_verbose, 1); + + Init_threads_table(mByebug); + Init_byebug_context(mByebug); + Init_byebug_breakpoint(mByebug); + + rb_global_variable(&breakpoints); + rb_global_variable(&catchpoints); + rb_global_variable(&tracepoints); + rb_global_variable(&raised_exception); + rb_global_variable(&threads); + + idPuts = rb_intern("puts"); + idEmpty = rb_intern("empty?"); +} diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/byebug.h b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/byebug.h new file mode 100644 index 0000000..a6897a1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/byebug.h @@ -0,0 +1,145 @@ +#ifndef BYEBUG +#define BYEBUG + +#include +#include + +/* To prevent unused parameter warnings */ +#define UNUSED(x) (void)(x) + +/* flags */ +#define CTX_FL_DEAD (1 << 1) /* this context belonged to a dead thread */ +#define CTX_FL_IGNORE (1 << 2) /* this context belongs to ignored thread */ +#define CTX_FL_SUSPEND (1 << 3) /* thread currently suspended */ +#define CTX_FL_TRACING (1 << 4) /* call at_tracing method */ +#define CTX_FL_WAS_RUNNING (1 << 5) /* thread was previously running */ +#define CTX_FL_STOP_ON_RET (1 << 6) /* can stop on method 'end' */ +#define CTX_FL_IGNORE_STEPS (1 << 7) /* doesn't countdown steps to break */ + +/* macro functions */ +#define CTX_FL_TEST(c, f) ((c)->flags & (f)) +#define CTX_FL_SET(c, f) \ + do \ + { \ + (c)->flags |= (f); \ + } while (0) +#define CTX_FL_UNSET(c, f) \ + do \ + { \ + (c)->flags &= ~(f); \ + } while (0) + +/* types */ +typedef enum +{ + CTX_STOP_NONE, + CTX_STOP_STEP, + CTX_STOP_BREAKPOINT, + CTX_STOP_CATCHPOINT +} ctx_stop_reason; + +typedef struct +{ + int calced_stack_size; + int flags; + ctx_stop_reason stop_reason; + + VALUE thread; + int thnum; + + int dest_frame; /* next stop's frame if stopped by next */ + int lines; /* # of lines in dest_frame before stopping */ + int steps; /* # of steps before stopping */ + int steps_out; /* # of returns before stopping */ + + VALUE backtrace; /* [[loc, self, klass, binding], ...] */ +} debug_context_t; + +typedef enum +{ + LOCATION, + SELF, + CLASS, + BINDING +} frame_part; + +struct call_with_inspection_data +{ + debug_context_t *dc; + VALUE ctx; + ID id; + int argc; + VALUE *argv; +}; + +typedef struct +{ + st_table *tbl; +} threads_table_t; + +enum bp_type +{ + BP_POS_TYPE, + BP_METHOD_TYPE +}; + +enum hit_condition +{ + HIT_COND_NONE, + HIT_COND_GE, + HIT_COND_EQ, + HIT_COND_MOD +}; + +typedef struct +{ + int id; + enum bp_type type; + VALUE source; + union + { + int line; + ID mid; + } pos; + VALUE expr; + VALUE enabled; + int hit_count; + int hit_value; + enum hit_condition hit_condition; +} breakpoint_t; + +/* functions from locker.c */ +extern void byebug_add_to_locked(VALUE thread); +extern VALUE byebug_pop_from_locked(); +extern void byebug_remove_from_locked(VALUE thread); + +/* functions from threads.c */ +extern void Init_threads_table(VALUE mByebug); +extern VALUE create_threads_table(void); +extern void thread_context_lookup(VALUE thread, VALUE *context); +extern int is_living_thread(VALUE thread); +extern void acquire_lock(debug_context_t *dc); +extern void release_lock(void); + +/* global variables */ +extern VALUE threads; +extern VALUE next_thread; + +/* functions from context.c */ +extern void Init_byebug_context(VALUE mByebug); +extern VALUE byebug_context_create(VALUE thread); +extern VALUE context_dup(debug_context_t *context); +extern void byebug_reset_stepping_stop_points(debug_context_t *context); +extern VALUE call_with_debug_inspector(struct call_with_inspection_data *data); +extern VALUE context_backtrace_set(const rb_debug_inspector_t *inspector, + void *data); + +/* functions from breakpoint.c */ +extern void Init_byebug_breakpoint(VALUE mByebug); +extern VALUE find_breakpoint_by_pos(VALUE breakpoints, VALUE source, VALUE pos, + VALUE bind); + +extern VALUE find_breakpoint_by_method(VALUE breakpoints, VALUE klass, + VALUE mid, VALUE bind, VALUE self); + +#endif diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/byebug.o b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/byebug.o new file mode 100644 index 0000000..52d6e67 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/byebug.o differ diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/byebug.so b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/byebug.so new file mode 100755 index 0000000..86e4bde Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/byebug.so differ diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/context.c b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/context.c new file mode 100644 index 0000000..f5403cc --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/context.c @@ -0,0 +1,686 @@ +#include "byebug.h" + +static VALUE cContext; +static VALUE cDebugThread; +static int thnum_max = 0; + +/* "Step", "Next" and "Finish" do their work by saving information about where + * to stop next. byebug_reset_stepping_stop_points removes/resets this information. */ +extern void +byebug_reset_stepping_stop_points(debug_context_t *context) +{ + context->dest_frame = -1; + context->lines = -1; + context->steps = -1; + context->steps_out = -1; +} + +/* + * call-seq: + * context.dead? -> bool + * + * Returns +true+ if context doesn't represent a live context and is created + * during post-mortem exception handling. + */ +static inline VALUE +Context_dead(VALUE self) +{ + debug_context_t *context; + + Data_Get_Struct(self, debug_context_t, context); + return CTX_FL_TEST(context, CTX_FL_DEAD) ? Qtrue : Qfalse; +} + +static void +context_mark(void *data) +{ + debug_context_t *context = (debug_context_t *)data; + + rb_gc_mark(context->backtrace); +} + +static VALUE +dc_backtrace(const debug_context_t *context) +{ + return context->backtrace; +} + +static int +dc_stack_size(debug_context_t *context) +{ + + if (NIL_P(dc_backtrace(context))) + return 0; + + return RARRAY_LENINT(dc_backtrace(context)); +} + +extern VALUE +byebug_context_create(VALUE thread) +{ + debug_context_t *context = ALLOC(debug_context_t); + + context->flags = 0; + context->thnum = ++thnum_max; + context->thread = thread; + byebug_reset_stepping_stop_points(context); + context->stop_reason = CTX_STOP_NONE; + + rb_debug_inspector_open(context_backtrace_set, (void *)context); + context->calced_stack_size = dc_stack_size(context) + 1; + + if (rb_obj_class(thread) == cDebugThread) + CTX_FL_SET(context, CTX_FL_IGNORE); + + return Data_Wrap_Struct(cContext, context_mark, 0, context); +} + +extern VALUE +context_dup(debug_context_t *context) +{ + debug_context_t *new_context = ALLOC(debug_context_t); + + memcpy(new_context, context, sizeof(debug_context_t)); + byebug_reset_stepping_stop_points(new_context); + new_context->backtrace = context->backtrace; + CTX_FL_SET(new_context, CTX_FL_DEAD); + + return Data_Wrap_Struct(cContext, context_mark, 0, new_context); +} + + +static VALUE +dc_frame_get(const debug_context_t *context, int frame_index, frame_part type) +{ + VALUE frame; + + if (NIL_P(dc_backtrace(context))) + rb_raise(rb_eRuntimeError, "Backtrace information is not available"); + + if (frame_index >= RARRAY_LENINT(dc_backtrace(context))) + rb_raise(rb_eRuntimeError, "That frame doesn't exist!"); + + frame = rb_ary_entry(dc_backtrace(context), frame_index); + return rb_ary_entry(frame, type); +} + +static VALUE +dc_frame_location(const debug_context_t *context, int frame_index) +{ + return dc_frame_get(context, frame_index, LOCATION); +} + +static VALUE +dc_frame_self(const debug_context_t *context, int frame_index) +{ + return dc_frame_get(context, frame_index, SELF); +} + +static VALUE +dc_frame_class(const debug_context_t *context, int frame_index) +{ + return dc_frame_get(context, frame_index, CLASS); +} + +static VALUE +dc_frame_binding(const debug_context_t *context, int frame_index) +{ + return dc_frame_get(context, frame_index, BINDING); +} + +static VALUE +load_backtrace(const rb_debug_inspector_t *inspector) +{ + VALUE backtrace = rb_ary_new(); + VALUE locs = rb_debug_inspector_backtrace_locations(inspector); + int i; + + for (i = 0; i < RARRAY_LENINT(locs); i++) + { + VALUE frame = rb_ary_new(); + + rb_ary_push(frame, rb_ary_entry(locs, i)); + rb_ary_push(frame, rb_debug_inspector_frame_self_get(inspector, i)); + rb_ary_push(frame, rb_debug_inspector_frame_class_get(inspector, i)); + rb_ary_push(frame, rb_debug_inspector_frame_binding_get(inspector, i)); + + rb_ary_push(backtrace, frame); + } + + return backtrace; +} + +extern VALUE +context_backtrace_set(const rb_debug_inspector_t *inspector, void *data) +{ + debug_context_t *dc = (debug_context_t *)data; + + dc->backtrace = load_backtrace(inspector); + + return Qnil; +} + +static VALUE +open_debug_inspector_i(const rb_debug_inspector_t *inspector, void *data) +{ + struct call_with_inspection_data *cwi = + (struct call_with_inspection_data *)data; + + cwi->dc->backtrace = load_backtrace(inspector); + + return rb_funcall2(cwi->ctx, cwi->id, cwi->argc, cwi->argv); +} + +static VALUE +open_debug_inspector(struct call_with_inspection_data *cwi) +{ + return rb_debug_inspector_open(open_debug_inspector_i, cwi); +} + +static VALUE +open_debug_inspector_ensure(VALUE v) +{ + return open_debug_inspector((struct call_with_inspection_data *)v); +} + + +static VALUE +close_debug_inspector(struct call_with_inspection_data *cwi) +{ + cwi->dc->backtrace = Qnil; + return Qnil; +} + +static VALUE +close_debug_inspector_ensure(VALUE v) +{ + return close_debug_inspector((struct call_with_inspection_data *)v); +} + +extern VALUE +call_with_debug_inspector(struct call_with_inspection_data *data) +{ + return rb_ensure(open_debug_inspector_ensure, (VALUE)data, close_debug_inspector_ensure, + (VALUE)data); +} + +#define FRAME_SETUP \ + debug_context_t *context; \ + VALUE frame_no; \ + int frame_n; \ + Data_Get_Struct(self, debug_context_t, context); \ + if (!rb_scan_args(argc, argv, "01", &frame_no)) \ + frame_n = 0; \ + else \ + frame_n = FIX2INT(frame_no); + +/* + * call-seq: + * context.frame_binding(frame_position = 0) -> binding + * + * Returns frame's binding. + */ +static VALUE +Context_frame_binding(int argc, VALUE *argv, VALUE self) +{ + FRAME_SETUP; + + return dc_frame_binding(context, frame_n); +} + +/* + * call-seq: + * context.frame_class(frame_position = 0) -> class + * + * Returns frame's defined class. + */ +static VALUE +Context_frame_class(int argc, VALUE *argv, VALUE self) +{ + FRAME_SETUP; + + return dc_frame_class(context, frame_n); +} + +/* + * call-seq: + * context.frame_file(frame_position = 0) -> string + * + * Returns the name of the file in the frame. + */ +static VALUE +Context_frame_file(int argc, VALUE *argv, VALUE self) +{ + VALUE loc, absolute_path; + + FRAME_SETUP; + + loc = dc_frame_location(context, frame_n); + + absolute_path = rb_funcall(loc, rb_intern("absolute_path"), 0); + + if (!NIL_P(absolute_path)) + return absolute_path; + + return rb_funcall(loc, rb_intern("path"), 0); +} + +/* + * call-seq: + * context.frame_line(frame_position = 0) -> int + * + * Returns the line number in the file in the frame. + */ +static VALUE +Context_frame_line(int argc, VALUE *argv, VALUE self) +{ + VALUE loc; + + FRAME_SETUP; + + loc = dc_frame_location(context, frame_n); + + return rb_funcall(loc, rb_intern("lineno"), 0); +} + +/* + * call-seq: + * context.frame_method(frame_position = 0) -> sym + * + * Returns the sym of the method in the frame. + */ +static VALUE +Context_frame_method(int argc, VALUE *argv, VALUE self) +{ + VALUE loc; + + FRAME_SETUP; + + loc = dc_frame_location(context, frame_n); + + return rb_str_intern(rb_funcall(loc, rb_intern("label"), 0)); +} + +/* + * call-seq: + * context.frame_self(frame_postion = 0) -> obj + * + * Returns self object of the frame. + */ +static VALUE +Context_frame_self(int argc, VALUE *argv, VALUE self) +{ + FRAME_SETUP; + + return dc_frame_self(context, frame_n); +} + +/* + * call-seq: + * context.ignored? -> bool + * + * Returns the ignore flag for the context, which marks whether the associated + * thread is ignored while debugging. + */ +static inline VALUE +Context_ignored(VALUE self) +{ + debug_context_t *context; + + Data_Get_Struct(self, debug_context_t, context); + return CTX_FL_TEST(context, CTX_FL_IGNORE) ? Qtrue : Qfalse; +} + +/* + * call-seq: + * context.resume -> nil + * + * Resumes thread from the suspended mode. + */ +static VALUE +Context_resume(VALUE self) +{ + debug_context_t *context; + + Data_Get_Struct(self, debug_context_t, context); + + if (!CTX_FL_TEST(context, CTX_FL_SUSPEND)) + return Qnil; + + CTX_FL_UNSET(context, CTX_FL_SUSPEND); + + if (CTX_FL_TEST(context, CTX_FL_WAS_RUNNING)) + rb_thread_wakeup(context->thread); + + return Qnil; +} + +/* + * call-seq: + * context.backtrace-> Array + * + * Returns the frame stack of a context. + */ +static inline VALUE +Context_backtrace(VALUE self) +{ + debug_context_t *context; + + Data_Get_Struct(self, debug_context_t, context); + + return dc_backtrace(context); +} + +static VALUE +Context_stop_reason(VALUE self) +{ + debug_context_t *context; + const char *symbol; + + Data_Get_Struct(self, debug_context_t, context); + + if (CTX_FL_TEST(context, CTX_FL_DEAD)) + symbol = "post-mortem"; + else + switch (context->stop_reason) + { + case CTX_STOP_STEP: + symbol = "step"; + break; + case CTX_STOP_BREAKPOINT: + symbol = "breakpoint"; + break; + case CTX_STOP_CATCHPOINT: + symbol = "catchpoint"; + break; + case CTX_STOP_NONE: + default: + symbol = "none"; + } + return ID2SYM(rb_intern(symbol)); +} + +/* + * call-seq: + * context.step_into(steps, frame = 0) + * + * Stops the current context after a number of +steps+ are made from frame + * +frame+ (by default the newest one). + */ +static VALUE +Context_step_into(int argc, VALUE *argv, VALUE self) +{ + VALUE steps, v_frame; + int n_args, from_frame; + debug_context_t *context; + + Data_Get_Struct(self, debug_context_t, context); + + if (context->calced_stack_size == 0) + rb_raise(rb_eRuntimeError, "No frames collected."); + + n_args = rb_scan_args(argc, argv, "11", &steps, &v_frame); + + if (FIX2INT(steps) <= 0) + rb_raise(rb_eRuntimeError, "Steps argument must be positive."); + + from_frame = n_args == 1 ? 0 : FIX2INT(v_frame); + + if (from_frame < 0 || from_frame >= context->calced_stack_size) + rb_raise(rb_eRuntimeError, "Destination frame (%d) is out of range (%d)", + from_frame, context->calced_stack_size); + else if (from_frame > 0) + CTX_FL_SET(context, CTX_FL_IGNORE_STEPS); + + context->steps = FIX2INT(steps); + context->dest_frame = context->calced_stack_size - from_frame; + + return steps; +} + +/* + * call-seq: + * context.step_out(n_frames = 1, force = false) + * + * Stops after +n_frames+ frames are finished. +force+ parameter (if true) + * ensures that the execution will stop in the specified frame even when there + * are no more instructions to run. In that case, it will stop when the return + * event for that frame is triggered. + */ +static VALUE +Context_step_out(int argc, VALUE *argv, VALUE self) +{ + int n_args, n_frames; + VALUE v_frames, force; + debug_context_t *context; + + n_args = rb_scan_args(argc, argv, "02", &v_frames, &force); + n_frames = n_args == 0 ? 1 : FIX2INT(v_frames); + + Data_Get_Struct(self, debug_context_t, context); + + if (n_frames < 0 || n_frames > context->calced_stack_size) + rb_raise(rb_eRuntimeError, + "You want to finish %d frames, but stack size is only %d", + n_frames, context->calced_stack_size); + + context->steps_out = n_frames; + if (n_args == 2 && RTEST(force)) + CTX_FL_SET(context, CTX_FL_STOP_ON_RET); + else + CTX_FL_UNSET(context, CTX_FL_STOP_ON_RET); + + return Qnil; +} + +/* + * call-seq: + * context.step_over(lines, frame = 0) + * + * Steps over +lines+ lines in frame +frame+ (by default the newest one) or + * higher (if frame +frame+ finishes). + */ +static VALUE +Context_step_over(int argc, VALUE *argv, VALUE self) +{ + int n_args, frame; + VALUE lines, v_frame; + debug_context_t *context; + + Data_Get_Struct(self, debug_context_t, context); + + if (context->calced_stack_size == 0) + rb_raise(rb_eRuntimeError, "No frames collected."); + + n_args = rb_scan_args(argc, argv, "11", &lines, &v_frame); + frame = n_args == 1 ? 0 : FIX2INT(v_frame); + + if (frame < 0 || frame >= context->calced_stack_size) + rb_raise(rb_eRuntimeError, "Destination frame (%d) is out of range (%d)", + frame, context->calced_stack_size); + + context->lines = FIX2INT(lines); + context->dest_frame = context->calced_stack_size - frame; + + return Qnil; +} + +/* + * call-seq: + * context.suspend -> nil + * + * Suspends the thread when it is running. + */ +static VALUE +Context_suspend(VALUE self) +{ + VALUE status; + debug_context_t *context; + + Data_Get_Struct(self, debug_context_t, context); + + status = rb_funcall(context->thread, rb_intern("status"), 0); + + if (rb_str_cmp(status, rb_str_new2("run")) == 0) + CTX_FL_SET(context, CTX_FL_WAS_RUNNING); + else if (rb_str_cmp(status, rb_str_new2("sleep")) == 0) + CTX_FL_UNSET(context, CTX_FL_WAS_RUNNING); + else + return Qnil; + + CTX_FL_SET(context, CTX_FL_SUSPEND); + + return Qnil; +} + +/* + * call-seq: + * context.switch -> nil + * + * Switches execution to this context. + */ +static VALUE +Context_switch(VALUE self) +{ + debug_context_t *context; + + Data_Get_Struct(self, debug_context_t, context); + + next_thread = context->thread; + + context->steps = 1; + context->steps_out = 0; + CTX_FL_SET(context, CTX_FL_STOP_ON_RET); + + return Qnil; +} + +/* + * call-seq: + * context.suspended? -> bool + * + * Returns +true+ if the thread is suspended by debugger. + */ +static VALUE +Context_is_suspended(VALUE self) +{ + debug_context_t *context; + + Data_Get_Struct(self, debug_context_t, context); + + return CTX_FL_TEST(context, CTX_FL_SUSPEND) ? Qtrue : Qfalse; +} + +/* + * call-seq: + * context.thnum -> int + * + * Returns the context's number. + */ +static inline VALUE +Context_thnum(VALUE self) +{ + debug_context_t *context; + + Data_Get_Struct(self, debug_context_t, context); + return INT2FIX(context->thnum); +} + +/* + * call-seq: + * context.thread -> thread + * + * Returns the thread this context is associated with. + */ +static inline VALUE +Context_thread(VALUE self) +{ + debug_context_t *context; + + Data_Get_Struct(self, debug_context_t, context); + return context->thread; +} + +/* + * call-seq: + * context.tracing -> bool + * + * Returns the tracing flag for the current context. + */ +static VALUE +Context_tracing(VALUE self) +{ + debug_context_t *context; + + Data_Get_Struct(self, debug_context_t, context); + return CTX_FL_TEST(context, CTX_FL_TRACING) ? Qtrue : Qfalse; +} + +/* + * call-seq: + * context.tracing = bool + * + * Controls the tracing for this context. + */ +static VALUE +Context_set_tracing(VALUE self, VALUE value) +{ + debug_context_t *context; + + Data_Get_Struct(self, debug_context_t, context); + + if (RTEST(value)) + CTX_FL_SET(context, CTX_FL_TRACING); + else + CTX_FL_UNSET(context, CTX_FL_TRACING); + return value; +} + +/* :nodoc: */ +static VALUE +dt_inherited(VALUE klass) +{ + UNUSED(klass); + + rb_raise(rb_eRuntimeError, "Can't inherit Byebug::DebugThread class"); + + return Qnil; +} + +/* + * Document-class: Context + * + * == Summary + * + * Byebug keeps a single instance of this class per thread. + */ +void +Init_byebug_context(VALUE mByebug) +{ + cContext = rb_define_class_under(mByebug, "Context", rb_cObject); + + rb_define_method(cContext, "backtrace", Context_backtrace, 0); + rb_define_method(cContext, "dead?", Context_dead, 0); + rb_define_method(cContext, "frame_binding", Context_frame_binding, -1); + rb_define_method(cContext, "frame_class", Context_frame_class, -1); + rb_define_method(cContext, "frame_file", Context_frame_file, -1); + rb_define_method(cContext, "frame_line", Context_frame_line, -1); + rb_define_method(cContext, "frame_method", Context_frame_method, -1); + rb_define_method(cContext, "frame_self", Context_frame_self, -1); + rb_define_method(cContext, "ignored?", Context_ignored, 0); + rb_define_method(cContext, "resume", Context_resume, 0); + rb_define_method(cContext, "step_into", Context_step_into, -1); + rb_define_method(cContext, "step_out", Context_step_out, -1); + rb_define_method(cContext, "step_over", Context_step_over, -1); + rb_define_method(cContext, "stop_reason", Context_stop_reason, 0); + rb_define_method(cContext, "suspend", Context_suspend, 0); + rb_define_method(cContext, "suspended?", Context_is_suspended, 0); + rb_define_method(cContext, "switch", Context_switch, 0); + rb_define_method(cContext, "thnum", Context_thnum, 0); + rb_define_method(cContext, "thread", Context_thread, 0); + rb_define_method(cContext, "tracing", Context_tracing, 0); + rb_define_method(cContext, "tracing=", Context_set_tracing, 1); + + cDebugThread = rb_define_class_under(mByebug, "DebugThread", rb_cThread); + rb_define_singleton_method(cDebugThread, "inherited", dt_inherited, 0); +} diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/context.o b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/context.o new file mode 100644 index 0000000..5459806 Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/context.o differ diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/extconf.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/extconf.rb new file mode 100644 index 0000000..057552e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/extconf.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require "mkmf" + +makefile_config = RbConfig::MAKEFILE_CONFIG + +makefile_config["CC"] = ENV["CC"] if ENV["CC"] + +makefile_config["CFLAGS"] << " -gdwarf-2 -g3 -O0" if ENV["debug"] + +dir_config("ruby") +with_cflags(makefile_config["CFLAGS"]) { create_makefile("byebug/byebug") } diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/locker.c b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/locker.c new file mode 100644 index 0000000..f74a111 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/locker.c @@ -0,0 +1,96 @@ +#include "byebug.h" + +/** + * A simple linked list containing locked threads, FIFO style. + */ + +typedef struct locked_thread_t +{ + VALUE thread; + struct locked_thread_t *next; +} locked_thread_t; + +static locked_thread_t *locked_head = NULL; +static locked_thread_t *locked_tail = NULL; + +static int +is_in_locked(VALUE thread) +{ + locked_thread_t *node; + + if (!locked_head) + return 0; + + for (node = locked_head; node != locked_tail; node = node->next) + if (node->thread == thread) + return 1; + + return 0; +} + +extern void +byebug_add_to_locked(VALUE thread) +{ + locked_thread_t *node; + + if (is_in_locked(thread)) + return; + + node = ALLOC(locked_thread_t); + node->thread = thread; + node->next = NULL; + + if (locked_tail) + locked_tail->next = node; + + locked_tail = node; + + if (!locked_head) + locked_head = node; +} + +extern VALUE +byebug_pop_from_locked() +{ + VALUE thread; + locked_thread_t *node; + + if (!locked_head) + return Qnil; + + node = locked_head; + locked_head = locked_head->next; + + if (locked_tail == node) + locked_tail = NULL; + + thread = node->thread; + xfree(node); + + return thread; +} + +extern void +byebug_remove_from_locked(VALUE thread) +{ + locked_thread_t *node; + locked_thread_t *next_node; + + if (NIL_P(thread) || !locked_head || !is_in_locked(thread)) + return; + + if (locked_head->thread == thread) + { + byebug_pop_from_locked(); + return; + } + + for (node = locked_head; node != locked_tail; node = node->next) + if (node->next && node->next->thread == thread) + { + next_node = node->next; + node->next = next_node->next; + xfree(next_node); + return; + } +} diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/locker.o b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/locker.o new file mode 100644 index 0000000..a1ddedc Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/locker.o differ diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/threads.c b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/threads.c new file mode 100644 index 0000000..832ffb1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/threads.c @@ -0,0 +1,230 @@ +#include "byebug.h" + +/* Threads table class */ +static VALUE cThreadsTable; + +/* If not Qnil, holds the next thread that must be run */ +VALUE next_thread = Qnil; + +/* To allow thread syncronization, we must stop threads when debugging */ +static VALUE locker = Qnil; + +static int +t_tbl_mark_keyvalue(st_data_t key, st_data_t value, st_data_t tbl) +{ + UNUSED(tbl); + + rb_gc_mark((VALUE)key); + + if (!value) + return ST_CONTINUE; + + rb_gc_mark((VALUE)value); + + return ST_CONTINUE; +} + +static void +t_tbl_mark(void *data) +{ + threads_table_t *t_tbl = (threads_table_t *)data; + st_table *tbl = t_tbl->tbl; + + st_foreach(tbl, t_tbl_mark_keyvalue, (st_data_t)tbl); +} + +static void +t_tbl_free(void *data) +{ + threads_table_t *t_tbl = (threads_table_t *)data; + + st_free_table(t_tbl->tbl); + xfree(t_tbl); +} + +/* + * Creates a numeric hash whose keys are the currently active threads and + * whose values are their associated contexts. + */ +VALUE +create_threads_table(void) +{ + threads_table_t *t_tbl; + + t_tbl = ALLOC(threads_table_t); + t_tbl->tbl = st_init_numtable(); + return Data_Wrap_Struct(cThreadsTable, t_tbl_mark, t_tbl_free, t_tbl); +} + +/* + * Checks a single entry in the threads table. + * + * If it has no associated context or the key doesn't correspond to a living + * thread, the entry is removed from the thread's list. + */ +static int +check_thread_i(st_data_t key, st_data_t value, st_data_t data) +{ + UNUSED(data); + + if (!value) + return ST_DELETE; + + if (!is_living_thread((VALUE)key)) + return ST_DELETE; + + return ST_CONTINUE; +} + +/* + * Checks whether a thread is either in the running or sleeping state. + */ +int +is_living_thread(VALUE thread) +{ + VALUE status = rb_funcall(thread, rb_intern("status"), 0); + + if (NIL_P(status) || status == Qfalse) + return 0; + + if (rb_str_cmp(status, rb_str_new2("run")) == 0 + || rb_str_cmp(status, rb_str_new2("sleep")) == 0) + return 1; + + return 0; +} + +/* + * Checks threads table for dead/finished threads. + */ +static void +cleanup_dead_threads(void) +{ + threads_table_t *t_tbl; + + Data_Get_Struct(threads, threads_table_t, t_tbl); + st_foreach(t_tbl->tbl, check_thread_i, 0); +} + +/* + * Looks up a context in the threads table. If not present, it creates it. + */ +void +thread_context_lookup(VALUE thread, VALUE *context) +{ + threads_table_t *t_tbl; + + Data_Get_Struct(threads, threads_table_t, t_tbl); + + if (!st_lookup(t_tbl->tbl, thread, context) || !*context) + { + *context = byebug_context_create(thread); + st_insert(t_tbl->tbl, thread, *context); + } +} + +/* + * Holds thread execution while another thread is active. + * + * Thanks to this, all threads are "frozen" while the user is typing commands. + */ +void +acquire_lock(debug_context_t *dc) +{ + while ((!NIL_P(locker) && locker != rb_thread_current()) + || CTX_FL_TEST(dc, CTX_FL_SUSPEND)) + { + byebug_add_to_locked(rb_thread_current()); + rb_thread_stop(); + + if (CTX_FL_TEST(dc, CTX_FL_SUSPEND)) + CTX_FL_SET(dc, CTX_FL_WAS_RUNNING); + } + + locker = rb_thread_current(); +} + +/* + * Releases our global lock and passes execution on to another thread, either + * the thread specified by +next_thread+ or any other thread if +next_thread+ + * is nil. + */ +void +release_lock(void) +{ + VALUE thread; + + cleanup_dead_threads(); + + locker = Qnil; + + if (NIL_P(next_thread)) + thread = byebug_pop_from_locked(); + else + { + byebug_remove_from_locked(next_thread); + thread = next_thread; + next_thread = Qnil; + } + + if (!NIL_P(thread) && is_living_thread(thread)) + rb_thread_run(thread); +} + +/* + * call-seq: + * Byebug.unlock -> nil + * + * Unlocks global switch so other threads can run. + */ +static VALUE +Unlock(VALUE self) +{ + UNUSED(self); + + release_lock(); + + return locker; +} + +/* + * call-seq: + * Byebug.lock -> Thread.current + * + * Locks global switch to reserve execution to current thread exclusively. + */ +static VALUE +Lock(VALUE self) +{ + debug_context_t *dc; + VALUE context; + + UNUSED(self); + + if (!is_living_thread(rb_thread_current())) + rb_raise(rb_eRuntimeError, "Current thread is dead!"); + + thread_context_lookup(rb_thread_current(), &context); + Data_Get_Struct(context, debug_context_t, dc); + + acquire_lock(dc); + + return locker; +} + +/* + * + * Document-class: ThreadsTable + * + * == Sumary + * + * Hash table holding currently active threads and their associated contexts + */ +void +Init_threads_table(VALUE mByebug) +{ + cThreadsTable = rb_define_class_under(mByebug, "ThreadsTable", rb_cObject); + + rb_define_module_function(mByebug, "unlock", Unlock, 0); + rb_define_module_function(mByebug, "lock", Lock, 0); +} diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/threads.o b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/threads.o new file mode 100644 index 0000000..b11b16d Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/ext/byebug/threads.o differ diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug.rb new file mode 100644 index 0000000..f7d2307 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require_relative "byebug/attacher" diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/attacher.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/attacher.rb new file mode 100644 index 0000000..0a4d596 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/attacher.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +# +# Main Container for all of Byebug's code +# +module Byebug + # + # Starts byebug, and stops at the first line of user's code. + # + def self.attach + unless started? + self.mode = :attached + + start + run_init_script + end + + current_context.step_out(3, true) + end + + def self.spawn(host = "localhost", port = nil) + require_relative "core" + + self.wait_connection = true + start_server(host, port || PORT) + end +end + +# +# Adds a `byebug` method to the Kernel module. +# +# Dropping a `byebug` call anywhere in your code, you get a debug prompt. +# +module Kernel + def byebug + require_relative "core" + + Byebug.attach unless Byebug.mode == :off + end + + def remote_byebug(host = "localhost", port = nil) + Byebug.spawn(host, port) + + Byebug.attach + end + + alias debugger byebug +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/breakpoint.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/breakpoint.rb new file mode 100644 index 0000000..b7dd30e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/breakpoint.rb @@ -0,0 +1,111 @@ +# frozen_string_literal: true + +module Byebug + # + # Implements breakpoints + # + class Breakpoint + # + # First breakpoint, in order of creation + # + def self.first + Byebug.breakpoints.first + end + + # + # Last breakpoint, in order of creation + # + def self.last + Byebug.breakpoints.last + end + + # + # Adds a new breakpoint + # + # @param [String] file + # @param [Fixnum] line + # @param [String] expr + # + def self.add(file, line, expr = nil) + breakpoint = Breakpoint.new(file, line, expr) + Byebug.breakpoints << breakpoint + breakpoint + end + + # + # Removes a breakpoint + # + # @param id [integer] breakpoint number + # + def self.remove(id) + Byebug.breakpoints.reject! { |b| b.id == id } + end + + # + # Returns an array of line numbers in file named +filename+ where + # breakpoints could be set. The list will contain an entry for each + # distinct line event call so it is possible (and possibly useful) for a + # line number appear more than once. + # + # @param filename [String] File name to inspect for possible breakpoints + # + def self.potential_lines(filename) + name = "#{Time.new.to_i}_#{rand(2**31)}" + iseq = RubyVM::InstructionSequence.compile(File.read(filename), name) + + if iseq.respond_to?(:each_child) + potential_lines_with_trace_points(iseq, {}) + else + potential_lines_without_trace_points(iseq, {}) + end + end + + def self.potential_lines_with_trace_points(iseq, lines) + iseq.trace_points.each { |(line, _)| lines[line] = true } + iseq.each_child do |child| + potential_lines_with_trace_points(child, lines) + end + + lines.keys.sort + end + + private_class_method :potential_lines_with_trace_points + + def self.potential_lines_without_trace_points(iseq, lines) + iseq.disasm.each_line do |line| + res = /^\d+ (?\w+)\s+.+\(\s*(?\d+)\)$/.match(line) + next unless res && res[:insn] == "trace" + + lines[res[:lineno].to_i] = true + end + + lines.keys + end + + private_class_method :potential_lines_without_trace_points + + # + # Returns true if a breakpoint could be set in line number +lineno+ in file + # name +filename. + # + def self.potential_line?(filename, lineno) + potential_lines(filename).member?(lineno) + end + + # + # True if there's no breakpoints + # + def self.none? + Byebug.breakpoints.empty? + end + + # + # Prints all information associated to the breakpoint + # + def inspect + meths = %w[id pos source expr hit_condition hit_count hit_value enabled?] + values = meths.map { |field| "#{field}: #{send(field)}" }.join(", ") + "#" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/byebug.so b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/byebug.so new file mode 100755 index 0000000..86e4bde Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/byebug.so differ diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/command.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/command.rb new file mode 100644 index 0000000..34b558f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/command.rb @@ -0,0 +1,111 @@ +# frozen_string_literal: true + +require "forwardable" +require_relative "helpers/string" + +module Byebug + # + # Parent class of all byebug commands. + # + # Subclass it and name the subclass ending with the word Command to implement + # your own custom command. + # + # @example Define a custom command + # + # class MyCustomCommand < Command + # def self.regexp + # /custom_regexp/ + # end + # + # def self.description + # "Custom long desc" + # end + # + # def.short_description + # "Custom short desc" + # end + # + # def execute + # # My command's implementation + # end + # end + # + class Command + extend Forwardable + + attr_reader :processor + + def initialize(processor, input = self.class.to_s) + @processor = processor + @match = match(input) + end + + def context + @context ||= processor.context + end + + def frame + @frame ||= context.frame + end + + def arguments + @match[0].split(" ").drop(1).join(" ") + end + + def_delegators "self.class", :help, :match + + def_delegator "processor.printer", :print, :pr + def_delegator "processor.printer", :print_collection, :prc + def_delegator "processor.printer", :print_variables, :prv + + def_delegators "processor.interface", :errmsg, :puts, :print, :confirm + + class << self + include Helpers::StringHelper + + # + # Special methods to allow command filtering in processors + # + attr_accessor :allow_in_control, :allow_in_post_mortem + + attr_writer :always_run + + def always_run + @always_run ||= 0 + end + + # + # Name of the command, as executed by the user. + # + def to_s + name + .split("::") + .map { |n| n.gsub(/Command$/, "").downcase if /Command$/.match?(n) } + .compact + .join(" ") + end + + def columnize(width) + format( + " %-#{width}s -- %s\n", + name: to_s, + description: short_description + ) + end + + # + # Default help text for a command. + # + def help + prettify(description) + end + + # + # Command's regexp match against an input + # + def match(input) + regexp.match(input) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/command_list.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/command_list.rb new file mode 100644 index 0000000..4a94db2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/command_list.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require_relative "errors" + +module Byebug + # + # Holds an array of subcommands for a command + # + class CommandList + include Enumerable + + def initialize(commands) + @commands = commands.sort_by(&:to_s) + end + + def match(input) + find { |cmd| cmd.match(input) } + end + + def each + @commands.each { |cmd| yield(cmd) } + end + + def to_s + "\n" + map { |cmd| cmd.columnize(width) }.join + "\n" + end + + private + + def width + @width ||= map(&:to_s).max_by(&:size).size + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands.rb new file mode 100644 index 0000000..e73a9e4 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require_relative "commands/break" +require_relative "commands/catch" +require_relative "commands/condition" +require_relative "commands/continue" +require_relative "commands/debug" +require_relative "commands/delete" +require_relative "commands/disable" +require_relative "commands/display" +require_relative "commands/down" +require_relative "commands/edit" +require_relative "commands/enable" +require_relative "commands/finish" +require_relative "commands/frame" +require_relative "commands/help" +require_relative "commands/history" +require_relative "commands/info" +require_relative "commands/interrupt" +require_relative "commands/irb" +require_relative "commands/kill" +require_relative "commands/list" +require_relative "commands/method" +require_relative "commands/next" +require_relative "commands/pry" +require_relative "commands/quit" +require_relative "commands/restart" +require_relative "commands/save" +require_relative "commands/set" +require_relative "commands/show" +require_relative "commands/skip" +require_relative "commands/source" +require_relative "commands/step" +require_relative "commands/thread" +require_relative "commands/tracevar" +require_relative "commands/undisplay" +require_relative "commands/untracevar" +require_relative "commands/up" +require_relative "commands/var" +require_relative "commands/where" diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/break.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/break.rb new file mode 100644 index 0000000..55f4f4c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/break.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +require_relative "../command" +require_relative "../helpers/eval" +require_relative "../helpers/file" +require_relative "../helpers/parse" +require_relative "../source_file_formatter" + +module Byebug + # + # Implements breakpoint functionality + # + class BreakCommand < Command + include Helpers::EvalHelper + include Helpers::FileHelper + include Helpers::ParseHelper + + self.allow_in_control = true + + def self.regexp + /^\s* b(?:reak)? (?:\s+ (.+?))? (?:\s+ if \s+(.+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + b[reak] [:] [if ] + b[reak] [::...](.|#) [if ] + + They can be specified by line or method and an expression can be added + for conditionally enabled breakpoints. + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Sets breakpoints in the source code" + end + + def execute + return puts(help) unless @match[1] + + b = line_breakpoint(@match[1]) || method_breakpoint(@match[1]) + return errmsg(pr("break.errors.location")) unless b + + return puts(pr("break.created", id: b.id, file: b.source, line: b.pos)) if syntax_valid?(@match[2]) + + errmsg(pr("break.errors.expression", expr: @match[2])) + b.enabled = false + end + + private + + def line_breakpoint(location) + line_match = location.match(/^(\d+)$/) + file_line_match = location.match(/^(.+):(\d+)$/) + return unless line_match || file_line_match + + file = line_match ? frame.file : file_line_match[1] + line = line_match ? line_match[1].to_i : file_line_match[2].to_i + + add_line_breakpoint(file, line) + end + + def method_breakpoint(location) + location.match(/([^.#]+)[.#](.+)/) do |match| + klass = target_object(match[1]) + method = match[2].intern + + Breakpoint.add(klass, method, @match[2]) + end + end + + def target_object(str) + k = error_eval(str) + + k&.is_a?(Module) ? k.name : str + rescue StandardError + errmsg("Warning: breakpoint source is not yet defined") + str + end + + def add_line_breakpoint(file, line) + raise(pr("break.errors.source", file: file)) unless File.exist?(file) + + fullpath = File.realpath(file) + + raise(pr("break.errors.far_line", lines: n_lines(file), file: fullpath)) if line > n_lines(file) + + unless Breakpoint.potential_line?(fullpath, line) + msg = pr( + "break.errors.line", + file: fullpath, + line: line, + valid_breakpoints: valid_breakpoints_for(fullpath, line) + ) + + raise(msg) + end + + Breakpoint.add(fullpath, line, @match[2]) + end + + def valid_breakpoints_for(path, line) + potential_lines = Breakpoint.potential_lines(path) + annotator = ->(n) { potential_lines.include?(n) ? "[B]" : " " } + source_file_formatter = SourceFileFormatter.new(path, annotator) + + source_file_formatter.lines_around(line).join.chomp + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/catch.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/catch.rb new file mode 100644 index 0000000..5d1a1b8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/catch.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +require_relative "../command" +require_relative "../helpers/eval" + +module Byebug + # + # Implements exception catching. + # + # Enables the user to catch unhandled assertion when they happen. + # + class CatchCommand < Command + include Helpers::EvalHelper + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* cat(?:ch)? (?:\s+(\S+))? (?:\s+(off))? \s*$/x + end + + def self.description + <<-DESCRIPTION + cat[ch][ (off|[ off])] + + #{short_description} + + catch -- lists catchpoints + catch off -- deletes all catchpoints + catch -- enables handling + catch off -- disables handling + DESCRIPTION + end + + def self.short_description + "Handles exception catchpoints" + end + + def execute + return info unless @match[1] + + return @match[1] == "off" ? clear : add(@match[1]) unless @match[2] + + return errmsg pr("catch.errors.off", off: cmd) unless @match[2] == "off" + + remove(@match[1]) + end + + private + + def remove(exception) + return errmsg pr("catch.errors.not_found", exception: exception) unless Byebug.catchpoints.member?(exception) + + puts pr("catch.removed", exception: exception) + Byebug.catchpoints.delete(exception) + end + + def add(exception) + errmsg pr("catch.errors.not_class", class: exception) if warning_eval(exception.is_a?(Class).to_s) + + puts pr("catch.added", exception: exception) + Byebug.add_catchpoint(exception) + end + + def clear + Byebug.catchpoints.clear if confirm(pr("catch.confirmations.delete_all")) + end + + def info + if Byebug.catchpoints && !Byebug.catchpoints.empty? + Byebug.catchpoints.each_key do |exception| + puts("#{exception}: #{exception.is_a?(Class)}") + end + else + puts "No exceptions set to be caught." + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/condition.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/condition.rb new file mode 100644 index 0000000..c66a351 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/condition.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require_relative "../command" +require_relative "../helpers/parse" + +module Byebug + # + # Implements conditions on breakpoints. + # + # Adds the ability to stop on breakpoints only under certain conditions. + # + class ConditionCommand < Command + include Helpers::ParseHelper + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* cond(?:ition)? (?:\s+(\d+)(?:\s+(.*))?)? \s*$/x + end + + def self.description + <<-DESCRIPTION + cond[ition] [ expr] + + #{short_description} + + Specify breakpoint number to break only if is true. is + an integer and is an expression to be evaluated whenever + breakpoint is reached. If no expression is specified, the condition + is removed. + DESCRIPTION + end + + def self.short_description + "Sets conditions on breakpoints" + end + + def execute + return puts(help) unless @match[1] + + breakpoints = Byebug.breakpoints.sort_by(&:id) + return errmsg(pr("condition.errors.no_breakpoints")) if breakpoints.empty? + + pos, err = get_int(@match[1], "Condition", 1) + return errmsg(err) if err + + breakpoint = breakpoints.find { |b| b.id == pos } + return errmsg(pr("break.errors.no_breakpoint")) unless breakpoint + + return errmsg(pr("break.errors.not_changed", expr: @match[2])) unless syntax_valid?(@match[2]) + + breakpoint.expr = @match[2] + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/continue.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/continue.rb new file mode 100644 index 0000000..21b7242 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/continue.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +require_relative "../command" +require_relative "../helpers/parse" + +module Byebug + # + # Implements the continue command. + # + # Allows the user to continue execution until the next stopping point, a + # specific line number or until program termination. + # + class ContinueCommand < Command + include Helpers::ParseHelper + + def self.regexp + /^\s* c(?:ont(?:inue)?)? (?:(!|\s+unconditionally|\s+\S+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + c[ont[inue]][ ] + + #{short_description} + + Normally the program stops at the next breakpoint. However, if the + parameter "unconditionally" is given or the command is suffixed with + "!", the program will run until the end regardless of any enabled + breakpoints. + DESCRIPTION + end + + def self.short_description + "Runs until program ends, hits a breakpoint or reaches a line" + end + + def execute + if until_line? + num, err = get_int(modifier, "Continue", 0, nil) + return errmsg(err) unless num + + filename = File.expand_path(frame.file) + return errmsg(pr("continue.errors.unstopped_line", line: num)) unless Breakpoint.potential_line?(filename, num) + + Breakpoint.add(filename, num) + end + + processor.proceed! + + Byebug.mode = :off if unconditionally? + Byebug.stop if unconditionally? || Byebug.stoppable? + end + + private + + def until_line? + @match[1] && !["!", "unconditionally"].include?(modifier) + end + + def unconditionally? + @match[1] && ["!", "unconditionally"].include?(modifier) + end + + def modifier + @match[1].lstrip + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/debug.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/debug.rb new file mode 100644 index 0000000..1cb636f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/debug.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require_relative "../command" +require_relative "../helpers/eval" + +module Byebug + # + # Spawns a subdebugger and evaluates the given expression + # + class DebugCommand < Command + include Helpers::EvalHelper + + def self.regexp + /^\s* debug (?:\s+(\S+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + debug + + #{short_description} + + Allows, for example, setting breakpoints on expressions evaluated from + the debugger's prompt. + DESCRIPTION + end + + def self.short_description + "Spawns a subdebugger" + end + + def execute + return puts(help) unless @match[1] + + puts safe_inspect(separate_thread_eval(@match[1])) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/delete.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/delete.rb new file mode 100644 index 0000000..ee3c69c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/delete.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require_relative "../command" +require_relative "../helpers/parse" + +module Byebug + # + # Implements breakpoint deletion. + # + class DeleteCommand < Command + include Helpers::ParseHelper + + self.allow_in_control = true + self.allow_in_post_mortem = true + + def self.regexp + /^\s* del(?:ete)? (?:\s+(.*))?$/x + end + + def self.description + <<-DESCRIPTION + del[ete][ nnn...] + + #{short_description} + + Without and argument, deletes all breakpoints. With integer arguments, + it deletes specific breakpoints. + DESCRIPTION + end + + def self.short_description + "Deletes breakpoints" + end + + def execute + unless @match[1] + Byebug.breakpoints.clear if confirm(pr("break.confirmations.delete_all")) + + return + end + + @match[1].split(/ +/).each do |number| + pos, err = get_int(number, "Delete", 1) + + return errmsg(err) unless pos + + if Breakpoint.remove(pos) + puts(pr("break.messages.breakpoint_deleted", pos: pos)) + else + errmsg(pr("break.errors.no_breakpoint_delete", pos: pos)) + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/disable.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/disable.rb new file mode 100644 index 0000000..ce92c5a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/disable.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require_relative "../subcommands" + +require_relative "../commands/disable/breakpoints" +require_relative "../commands/disable/display" + +module Byebug + # + # Disabling custom display expressions or breakpoints. + # + class DisableCommand < Command + include Subcommands + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* dis(?:able)? (?:\s+ (.+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + dis[able][[ breakpoints| display)][ n1[ n2[ ...[ nn]]]]] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Disables breakpoints or displays" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/disable/breakpoints.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/disable/breakpoints.rb new file mode 100644 index 0000000..8427088 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/disable/breakpoints.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require_relative "../../helpers/toggle" + +module Byebug + # + # Reopens the +disable+ command to define the +breakpoints+ subcommand + # + class DisableCommand < Command + # + # Disables all or specific breakpoints + # + class BreakpointsCommand < Command + include Helpers::ToggleHelper + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* b(?:reakpoints)? (?:\s+ (.+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + dis[able] b[reakpoints][ .. ] + + #{short_description} + + Give breakpoint numbers (separated by spaces) as arguments or no + argument at all if you want to disable every breakpoint. + DESCRIPTION + end + + def self.short_description + "Disable all or specific breakpoints." + end + + def execute + enable_disable_breakpoints("disable", @match[1]) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/disable/display.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/disable/display.rb new file mode 100644 index 0000000..102bf83 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/disable/display.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require_relative "../../helpers/toggle" + +module Byebug + # + # Reopens the +disable+ command to define the +display+ subcommand + # + class DisableCommand < Command + # + # Enables all or specific displays + # + class DisplayCommand < Command + include Helpers::ToggleHelper + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* d(?:isplay)? (?:\s+ (.+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + dis[able] d[isplay][ .. ] + + #{short_description} + + Arguments are the code numbers of the expressions to disable. Do "info + display" to see the current list of code numbers. If no arguments are + specified, all displays are disabled. + DESCRIPTION + end + + def self.short_description + "Disables expressions to be displayed when program stops." + end + + def execute + enable_disable_display("disable", @match[1]) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/display.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/display.rb new file mode 100644 index 0000000..6947b01 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/display.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require_relative "../command" +require_relative "../helpers/eval" + +module Byebug + # + # Custom expressions to be displayed every time the debugger stops. + # + class DisplayCommand < Command + include Helpers::EvalHelper + + self.allow_in_post_mortem = false + self.always_run = 2 + + def self.regexp + /^\s* disp(?:lay)? (?:\s+ (.+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + disp[lay][ ] + + #{short_description} + + If specified, adds into display expression + list. Otherwise, it lists all expressions. + DESCRIPTION + end + + def self.short_description + "Evaluates expressions every time the debugger stops" + end + + def execute + return print_display_expressions unless @match && @match[1] + + Byebug.displays.push [true, @match[1]] + display_expression(@match[1]) + end + + private + + def display_expression(exp) + print pr("display.result", n: Byebug.displays.size, + exp: exp, + result: eval_expr(exp)) + end + + def print_display_expressions + result = prc("display.result", Byebug.displays) do |item, index| + active, exp = item + + { n: index + 1, exp: exp, result: eval_expr(exp) } if active + end + + print result + end + + def eval_expr(expression) + error_eval(expression).inspect + rescue StandardError + "(undefined)" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/down.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/down.rb new file mode 100644 index 0000000..5f85406 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/down.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require "pathname" +require_relative "../command" +require_relative "../helpers/frame" +require_relative "../helpers/parse" + +module Byebug + # + # Move the current frame down in the backtrace. + # + class DownCommand < Command + include Helpers::FrameHelper + include Helpers::ParseHelper + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* down (?:\s+(\S+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + down[ count] + + #{short_description} + + Use the "bt" command to find out where you want to go. + DESCRIPTION + end + + def self.short_description + "Moves to a lower frame in the stack trace" + end + + def execute + pos, err = parse_steps(@match[1], "Down") + return errmsg(err) unless pos + + jump_frames(-pos) + + ListCommand.new(processor).execute if Setting[:autolist] + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/edit.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/edit.rb new file mode 100644 index 0000000..53ecbe8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/edit.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require_relative "../command" + +module Byebug + # + # Edit a file from byebug's prompt. + # + class EditCommand < Command + self.allow_in_control = true + self.allow_in_post_mortem = true + + def self.regexp + /^\s* ed(?:it)? (?:\s+(\S+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + edit[ file:lineno] + + #{short_description} + + With no argumnt, edits file containing most re line listed. Editing + targets can also be specified to start editing at a specific line in a + specific file + DESCRIPTION + end + + def self.short_description + "Edits source files" + end + + def execute + file, line = location(@match[1]) + return edit_error("not_exist", file) unless File.exist?(file) + return edit_error("not_readable", file) unless File.readable?(file) + + cmd = line ? "#{editor} +#{line} #{file}" : "#{editor} #{file}" + + Kernel.system(cmd) + end + + private + + def location(matched) + if matched.nil? + file = frame.file + return errmsg(pr("edit.errors.state")) unless file + + line = frame.line + elsif (@pos_match = /([^:]+)[:]([0-9]+)/.match(matched)) + file, line = @pos_match.captures + else + file = matched + line = nil + end + + [File.expand_path(file), line] + end + + def editor + ENV["EDITOR"] || "vim" + end + + def edit_error(type, file) + errmsg(pr("edit.errors.#{type}", file: file)) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/enable.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/enable.rb new file mode 100644 index 0000000..e470e87 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/enable.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require_relative "../subcommands" + +require_relative "../commands/enable/breakpoints" +require_relative "../commands/enable/display" + +module Byebug + # + # Enabling custom display expressions or breakpoints. + # + class EnableCommand < Command + include Subcommands + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* en(?:able)? (?:\s+ (.+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + en[able][[ b[reakpoints]| d[isplay])][ n1[ n2[ ...[ nn]]]]] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Enables breakpoints or displays" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/enable/breakpoints.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/enable/breakpoints.rb new file mode 100644 index 0000000..ab9ba6f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/enable/breakpoints.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require_relative "../../helpers/toggle" + +module Byebug + # + # Reopens the +enable+ command to define the +breakpoints+ subcommand + # + class EnableCommand < Command + # + # Enables all or specific breakpoints + # + class BreakpointsCommand < Command + include Helpers::ToggleHelper + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* b(?:reakpoints)? (?:\s+ (.+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + en[able] b[reakpoints][ ] + + #{short_description} + + Give breakpoint numbers (separated by spaces) as arguments or no + argument at all if you want to enable every breakpoint. + DESCRIPTION + end + + def self.short_description + "Enable all or specific breakpoints" + end + + def execute + enable_disable_breakpoints("enable", @match[1]) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/enable/display.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/enable/display.rb new file mode 100644 index 0000000..a75da37 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/enable/display.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require_relative "../../helpers/toggle" + +module Byebug + # + # Reopens the +enable+ command to define the +display+ subcommand + # + class EnableCommand < Command + # + # Enables all or specific displays + # + class DisplayCommand < Command + include Helpers::ToggleHelper + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* d(?:isplay)? (?:\s+ (.+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + en[able] d[isplay][ .. ] + + #{short_description} + + Arguments are the code numbers of the expressions to enable. Do "info + display" to see the current list of code numbers. If no arguments are + specified, all displays are enabled. + DESCRIPTION + end + + def self.short_description + "Enables expressions to be displayed when program stops." + end + + def execute + enable_disable_display("enable", @match[1]) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/finish.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/finish.rb new file mode 100644 index 0000000..2a51820 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/finish.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require_relative "../command" +require_relative "../helpers/parse" + +module Byebug + # + # Implements the finish functionality. + # + # Allows the user to continue execution until certain frames are finished. + # + class FinishCommand < Command + include Helpers::ParseHelper + + self.allow_in_post_mortem = false + + def self.regexp + /^\s* fin(?:ish)? (?:\s+(\S+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + fin[ish][ n_frames] + + #{short_description} + + If no number is given, we run until the current frame returns. If a + number of frames `n_frames` is given, then we run until `n_frames` + return from the current position. + DESCRIPTION + end + + def self.short_description + "Runs the program until frame returns" + end + + def execute + if @match[1] + n_frames, err = get_int(@match[1], "finish", 0, max_frames - 1) + return errmsg(err) unless n_frames + else + n_frames = 1 + end + + force = n_frames.zero? ? true : false + context.step_out(context.frame.pos + n_frames, force) + context.frame = 0 + processor.proceed! + end + + private + + def max_frames + context.stack_size - context.frame.pos + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/frame.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/frame.rb new file mode 100644 index 0000000..2cabfb8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/frame.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require "pathname" +require_relative "../command" +require_relative "../helpers/frame" +require_relative "../helpers/parse" + +module Byebug + # + # Move to specific frames in the backtrace. + # + class FrameCommand < Command + include Helpers::FrameHelper + include Helpers::ParseHelper + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* f(?:rame)? (?:\s+(\S+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + f[rame][ frame-number] + + #{short_description} + + If a frame number has been specified, to moves to that frame. Otherwise + it moves to the newest frame. + + A negative number indicates position from the other end, so "frame -1" + moves to the oldest frame, and "frame 0" moves to the newest frame. + + Without an argument, the command prints the current stack frame. Since + the current position is redisplayed, it may trigger a resyncronization + if there is a front end also watching over things. + + Use the "bt" command to find out where you want to go. + DESCRIPTION + end + + def self.short_description + "Moves to a frame in the call stack" + end + + def execute + return print(pr("frame.line", context.frame.to_hash)) unless @match[1] + + pos, err = get_int(@match[1], "Frame") + return errmsg(err) unless pos + + switch_to_frame(pos) + + ListCommand.new(processor).execute if Setting[:autolist] + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/help.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/help.rb new file mode 100644 index 0000000..fa93c5d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/help.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require_relative "../command" +require_relative "../errors" + +module Byebug + # + # Ask for help from byebug's prompt. + # + class HelpCommand < Command + self.allow_in_control = true + self.allow_in_post_mortem = true + + def self.regexp + /^\s* h(?:elp)? (?:\s+(\S+))? (?:\s+(\S+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + h[elp][ [ ]] + + #{short_description} + + help -- prints a summary of all commands + help -- prints help on command + help -- prints help on 's subcommand + DESCRIPTION + end + + def self.short_description + "Helps you using byebug" + end + + def execute + return help_for_all unless @match[1] + + return help_for(@match[1], command) unless @match[2] + + help_for(@match[2], subcommand) + end + + private + + def help_for_all + puts(processor.command_list.to_s) + end + + def help_for(input, cmd) + raise CommandNotFound.new(input, command) unless cmd + + puts(cmd.help) + end + + def command + @command ||= processor.command_list.match(@match[1]) + end + + def subcommand + return unless command + + @subcommand ||= command.subcommand_list.match(@match[2]) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/history.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/history.rb new file mode 100644 index 0000000..66ac76d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/history.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require_relative "../command" +require_relative "../helpers/parse" + +module Byebug + # + # Show history of byebug commands. + # + class HistoryCommand < Command + include Helpers::ParseHelper + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* hist(?:ory)? (?:\s+(?.+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + hist[ory][ num_cmds] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Shows byebug's history of commands" + end + + def execute + history = processor.interface.history + + size, = get_int(@match[:num_cmds], "history", 1) if @match[:num_cmds] + + puts history.to_s(size) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/info.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/info.rb new file mode 100644 index 0000000..2b0d4f3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/info.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require_relative "../subcommands" + +require_relative "../commands/info/breakpoints" +require_relative "../commands/info/display" +require_relative "../commands/info/file" +require_relative "../commands/info/line" +require_relative "../commands/info/program" + +module Byebug + # + # Shows info about different aspects of the debugger. + # + class InfoCommand < Command + include Subcommands + + self.allow_in_control = true + self.allow_in_post_mortem = true + + def self.regexp + /^\s* i(?:nfo)? (?:\s+ (.+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + info[ subcommand] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Shows several informations about the program being debugged" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/info/breakpoints.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/info/breakpoints.rb new file mode 100644 index 0000000..dad21f4 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/info/breakpoints.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +module Byebug + # + # Reopens the +info+ command to define the +breakpoints+ subcommand + # + class InfoCommand < Command + # + # Information about current breakpoints + # + class BreakpointsCommand < Command + self.allow_in_post_mortem = true + + def self.regexp + /^\s* b(?:reakpoints)? (?:\s+ (.+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + inf[o] b[reakpoints] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Status of user settable breakpoints" + end + + def execute + return puts("No breakpoints.") if Byebug.breakpoints.empty? + + breakpoints = Byebug.breakpoints.sort_by(&:id) + + if @match[1] + indices = @match[1].split(/ +/).map(&:to_i) + breakpoints = breakpoints.select { |b| indices.member?(b.id) } + return errmsg("No breakpoints found among list given") if breakpoints.empty? + end + + puts "Num Enb What" + breakpoints.each { |b| info_breakpoint(b) } + end + + private + + def info_breakpoint(brkpt) + interp = format( + "%-3d %-3s at %s:%s%s", + id: brkpt.id, + status: brkpt.enabled? ? "y" : "n", + file: brkpt.source, + line: brkpt.pos, + expression: brkpt.expr.nil? ? "" : " if #{brkpt.expr}" + ) + puts interp + hits = brkpt.hit_count + return unless hits.positive? + + s = hits > 1 ? "s" : "" + puts " breakpoint already hit #{hits} time#{s}" + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/info/display.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/info/display.rb new file mode 100644 index 0000000..8fbb03b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/info/display.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module Byebug + # + # Reopens the +info+ command to define the +display+ subcommand + # + class InfoCommand < Command + # + # Information about display expressions + # + class DisplayCommand < Command + self.allow_in_post_mortem = true + + def self.regexp + /^\s* d(?:isplay)? \s*$/x + end + + def self.description + <<-DESCRIPTION + inf[o] d[display] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "List of expressions to display when program stops" + end + + def execute + return puts("There are no auto-display expressions now.") unless Byebug.displays.find { |d| d[0] } + + puts "Auto-display expressions now in effect:" + puts "Num Enb Expression" + + Byebug.displays.each_with_index do |d, i| + interp = format( + "%3d: %s %s", + number: i + 1, + status: d[0] ? "y" : "n", + expression: d[1] + ) + + puts(interp) + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/info/file.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/info/file.rb new file mode 100644 index 0000000..8f1e675 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/info/file.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +require_relative "../../helpers/file" + +module Byebug + # + # Reopens the +info+ command to define the +file+ subcommand + # + class InfoCommand < Command + # + # Information about a particular source file + # + class FileCommand < Command + include Helpers::FileHelper + include Helpers::StringHelper + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* f(?:ile)? (?:\s+ (.+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + inf[o] f[ile] + + #{short_description} + + It informs about file name, number of lines, possible breakpoints in + the file, last modification time and sha1 digest. + DESCRIPTION + end + + def self.short_description + "Information about a particular source file." + end + + def execute + file = @match[1] || frame.file + return errmsg(pr("info.errors.undefined_file", file: file)) unless File.exist?(file) + + puts prettify <<-RUBY + File #{info_file_basic(file)} + + Breakpoint line numbers: #{info_file_breakpoints(file)} + + Modification time: #{info_file_mtime(file)} + + Sha1 Signature: #{info_file_sha1(file)} + RUBY + end + + private + + def info_file_basic(file) + path = File.expand_path(file) + return unless File.exist?(path) + + s = n_lines(path) == 1 ? "" : "s" + "#{path} (#{n_lines(path)} line#{s})" + end + + def info_file_breakpoints(file) + breakpoints = Breakpoint.potential_lines(file) + return unless breakpoints + + breakpoints.to_a.sort.join(" ") + end + + def info_file_mtime(file) + File.stat(file).mtime + end + + def info_file_sha1(file) + require "digest/sha1" + Digest::SHA1.hexdigest(file) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/info/line.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/info/line.rb new file mode 100644 index 0000000..f32bc15 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/info/line.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Byebug + # + # Reopens the +info+ command to define the +line+ subcommand + # + class InfoCommand < Command + # + # Information about current location + # + class LineCommand < Command + self.allow_in_post_mortem = true + + def self.regexp + /^\s* l(?:ine)? \s*$/x + end + + def self.description + <<-DESCRIPTION + inf[o] l[ine] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Line number and file name of current position in source file." + end + + def execute + puts "Line #{frame.line} of \"#{frame.file}\"" + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/info/program.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/info/program.rb new file mode 100644 index 0000000..1b65486 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/info/program.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module Byebug + # + # Reopens the +info+ command to define the +args+ subcommand + # + class InfoCommand < Command + # + # Information about arguments of the current method/block + # + class ProgramCommand < Command + self.allow_in_post_mortem = true + + def self.regexp + /^\s* p(?:rogram)? \s*$/x + end + + def self.description + <<-DESCRIPTION + inf[o] p[rogram] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Information about the current status of the debugged program." + end + + def execute + puts "Program stopped. " + format_stop_reason context.stop_reason + end + + private + + def format_stop_reason(stop_reason) + case stop_reason + when :step + puts "It stopped after stepping, next'ing or initial start." + when :breakpoint + puts "It stopped at a breakpoint." + when :catchpoint + puts "It stopped at a catchpoint." + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/interrupt.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/interrupt.rb new file mode 100644 index 0000000..a6a7925 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/interrupt.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require_relative "../command" + +module Byebug + # + # Interrupting execution of current thread. + # + class InterruptCommand < Command + self.allow_in_control = true + + def self.regexp + /^\s*int(?:errupt)?\s*$/ + end + + def self.description + <<-DESCRIPTION + int[errupt] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Interrupts the program" + end + + def execute + Byebug.start + + Byebug.thread_context(Thread.main).interrupt + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/irb.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/irb.rb new file mode 100644 index 0000000..08de1c3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/irb.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require_relative "../command" +require "irb" +require "English" + +module Byebug + # + # Enter IRB from byebug's prompt + # + class IrbCommand < Command + self.allow_in_post_mortem = true + + def self.regexp + /^\s* irb \s*$/x + end + + def self.description + <<-DESCRIPTION + irb + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Starts an IRB session" + end + + def execute + return errmsg(pr("base.errors.only_local")) unless processor.interface.instance_of?(LocalInterface) + + # @todo IRB tries to parse $ARGV so we must clear it (see #197). Add a + # test case for it so we can remove this comment. + with_clean_argv { IRB.start } + end + + private + + def with_clean_argv + saved_argv = $ARGV.dup + $ARGV.clear + begin + yield + ensure + $ARGV.concat(saved_argv) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/kill.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/kill.rb new file mode 100644 index 0000000..95411b4 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/kill.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require_relative "../command" + +module Byebug + # + # Send custom signals to the debugged program. + # + class KillCommand < Command + self.allow_in_control = true + + def self.regexp + /^\s* kill \s* (?:\s+(\S+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + kill[ signal] + + #{short_description} + + Equivalent of Process.kill(Process.pid) + DESCRIPTION + end + + def self.short_description + "Sends a signal to the current process" + end + + def execute + if @match[1] + signame = @match[1] + + return errmsg("signal name #{signame} is not a signal I know about\n") unless Signal.list.member?(signame) + else + return unless confirm("Really kill? (y/n) ") + + signame = "KILL" + end + + processor.interface.close if signame == "KILL" + Process.kill(signame, Process.pid) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/list.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/list.rb new file mode 100644 index 0000000..dd03c3d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/list.rb @@ -0,0 +1,159 @@ +# frozen_string_literal: true + +require_relative "../command" +require_relative "../source_file_formatter" +require_relative "../helpers/file" +require_relative "../helpers/parse" + +module Byebug + # + # List parts of the source code. + # + class ListCommand < Command + include Helpers::FileHelper + include Helpers::ParseHelper + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* l(?:ist)? (?:\s*([-=])|\s+(\S+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + l[ist][[-=]][ nn-mm] + + #{short_description} + + Lists lines forward from current line or from the place where code was + last listed. If "list-" is specified, lists backwards instead. If + "list=" is specified, lists from current line regardless of where code + was last listed. A line range can also be specified to list specific + sections of code. + DESCRIPTION + end + + def self.short_description + "Lists lines of source code" + end + + def execute + msg = "No sourcefile available for #{frame.file}" + raise(msg) unless File.exist?(frame.file) + + b, e = range(@match[2]) + + display_lines(b, e) + + processor.prev_line = b + end + + private + + # + # Line range to be printed by `list`. + # + # If is set, range is parsed from it. + # + # Otherwise it's automatically chosen. + # + def range(input) + return auto_range(@match[1] || "+") unless input + + b, e = parse_range(input) + raise("Invalid line range") unless valid_range?(b, e) + + [b, e] + end + + def valid_range?(first, last) + first <= last && (1..max_line).cover?(first) && (1..max_line).cover?(last) + end + + # + # Set line range to be printed by list + # + # @return first line number to list + # @return last line number to list + # + def auto_range(direction) + prev_line = processor.prev_line + + if direction == "=" || prev_line.nil? + source_file_formatter.range_around(frame.line) + else + source_file_formatter.range_from(move(prev_line, size, direction)) + end + end + + def parse_range(input) + first, err = get_int(lower_bound(input), "List", 1, max_line) + raise(err) unless first + + if upper_bound(input) + last, err = get_int(upper_bound(input), "List", 1, max_line) + raise(err) unless last + + last = amend_final(last) + else + first -= (size / 2) + end + + [first, last || move(first, size - 1)] + end + + def move(line, size, direction = "+") + line.send(direction, size) + end + + # + # Show a range of lines in the current file. + # + # @param min [Integer] Lower bound + # @param max [Integer] Upper bound + # + def display_lines(min, max) + puts "\n[#{min}, #{max}] in #{frame.file}" + + puts source_file_formatter.lines(min, max).join + end + + # + # @param range [String] A string with an integer range format + # + # @return [String] The lower bound of the given range + # + def lower_bound(range) + split_range(range)[0] + end + + # + # @param range [String] A string with an integer range format + # + # @return [String] The upper bound of the given range + # + def upper_bound(range) + split_range(range)[1] + end + + # + # @param str [String] A string with an integer range format + # + # @return [Array] The upper & lower bounds of the given range + # + def split_range(str) + str.split(/[-,]/) + end + + extend Forwardable + + def_delegators :source_file_formatter, :amend_final, :size, :max_line + + def source_file_formatter + @source_file_formatter ||= SourceFileFormatter.new( + frame.file, + ->(n) { n == frame.line ? "=>" : " " } + ) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/method.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/method.rb new file mode 100644 index 0000000..1d43417 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/method.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require_relative "../command" +require_relative "../helpers/eval" + +module Byebug + # + # Show methods of specific classes/modules/objects. + # + class MethodCommand < Command + include Helpers::EvalHelper + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* m(?:ethod)? \s+ (i(:?nstance)?\s+)?/x + end + + def self.description + <<-DESCRIPTION + m[ethod] (i[nstance][ ]|) + + #{short_description} + + When invoked with "instance", shows instance methods of the object + specified as argument or of self no object was specified. + + When invoked only with a class or module, shows class methods of the + class or module specified as argument. + DESCRIPTION + end + + def self.short_description + "Shows methods of an object, class or module" + end + + def execute + obj = warning_eval(@match.post_match) + + result = + if @match[1] + prc("method.methods", obj.methods.sort) { |item, _| { name: item } } + elsif !obj.is_a?(Module) + pr("variable.errors.not_module", object: @match.post_match) + else + prc("method.methods", obj.instance_methods(false).sort) do |item, _| + { name: item } + end + end + puts result + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/next.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/next.rb new file mode 100644 index 0000000..14f4acb --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/next.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require_relative "../command" +require_relative "../helpers/parse" + +module Byebug + # + # Implements the next functionality. + # + # Allows the user the continue execution until the next instruction in the + # current frame. + # + class NextCommand < Command + include Helpers::ParseHelper + + def self.regexp + /^\s* n(?:ext)? (?:\s+(\S+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + n[ext][ nnn] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Runs one or more lines of code" + end + + def execute + steps, err = parse_steps(@match[1], "Next") + return errmsg(err) unless steps + + context.step_over(steps, context.frame.pos) + processor.proceed! + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/pry.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/pry.rb new file mode 100644 index 0000000..97d1dc4 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/pry.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require_relative "../command" +require_relative "../helpers/eval" + +module Byebug + # + # Enter Pry from byebug's prompt + # + class PryCommand < Command + self.allow_in_post_mortem = true + + def self.regexp + /^\s* pry \s*$/x + end + + def self.description + <<-DESCRIPTION + pry + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Starts a Pry session" + end + + def execute + return errmsg(pr("base.errors.only_local")) unless processor.interface.instance_of?(LocalInterface) + + begin + require "pry" + rescue LoadError + return errmsg(pr("pry.errors.not_installed")) + end + + Pry.start(context.frame._binding) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/quit.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/quit.rb new file mode 100644 index 0000000..b9a7532 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/quit.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require_relative "../command" + +module Byebug + # + # Exit from byebug. + # + class QuitCommand < Command + self.allow_in_control = true + self.allow_in_post_mortem = true + + def self.regexp + /^\s* q(?:uit)? \s* (?:(!|\s+unconditionally))? \s*$/x + end + + def self.description + <<-DESCRIPTION + q[uit][!| unconditionally] + + #{short_description} + + Normally we prompt before exiting. However, if the parameter + "unconditionally" is given or command is suffixed with "!", we exit + without asking further questions. + DESCRIPTION + end + + def self.short_description + "Exits byebug" + end + + def execute + return unless @match[1] || confirm(pr("quit.confirmations.really")) + + processor.interface.autosave + processor.interface.close + + Process.exit! + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/restart.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/restart.rb new file mode 100644 index 0000000..87b6bea --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/restart.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require_relative "../command" +require_relative "../helpers/bin" +require_relative "../helpers/path" +require "shellwords" +require "English" +require "rbconfig" + +module Byebug + # + # Restart debugged program from within byebug. + # + class RestartCommand < Command + include Helpers::BinHelper + include Helpers::PathHelper + + self.allow_in_control = true + self.allow_in_post_mortem = true + + def self.regexp + /^\s* restart (?:\s+(?.+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + restart [args] + + #{short_description} + + This is a re-exec - all byebug state is lost. If command arguments are + passed those are used. + DESCRIPTION + end + + def self.short_description + "Restarts the debugged program" + end + + def execute + cmd = [$PROGRAM_NAME] + + cmd = prepend_byebug_bin(cmd) + cmd = prepend_ruby_bin(cmd) + + cmd += (@match[:args] ? @match[:args].shellsplit : $ARGV) + + puts pr("restart.success", cmd: cmd.shelljoin) + Kernel.exec(*cmd) + end + + private + + def prepend_byebug_bin(cmd) + cmd.unshift(bin_file) if Byebug.mode == :standalone + cmd + end + + def prepend_ruby_bin(cmd) + cmd.unshift(RbConfig.ruby) if which("ruby") != which(cmd.first) + cmd + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/save.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/save.rb new file mode 100644 index 0000000..e94120c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/save.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require_relative "../command" + +module Byebug + # + # Save current settings to use them in another debug session. + # + class SaveCommand < Command + self.allow_in_control = true + self.allow_in_post_mortem = true + + def self.regexp + /^\s* sa(?:ve)? (?:\s+(\S+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + save[ FILE] + + #{short_description} + + Byebug state is saved as a script file. This includes breakpoints, + catchpoints, display expressions and some settings. If no filename is + given, byebug will fabricate one. + + Use the "source" command in another debug session to restore the saved + file. + DESCRIPTION + end + + def self.short_description + "Saves current byebug session to a file" + end + + def execute + file = File.open(@match[1] || Setting[:savefile], "w") + + save_breakpoints(file) + save_catchpoints(file) + save_displays(file) + save_settings(file) + + print pr("save.messages.done", path: file.path) + file.close + end + + private + + def save_breakpoints(file) + Byebug.breakpoints.each do |b| + file.puts "break #{b.source}:#{b.pos}#{" if #{b.expr}" if b.expr}" + end + end + + def save_catchpoints(file) + Byebug.catchpoints.each_key do |c| + file.puts "catch #{c}" + end + end + + def save_displays(file) + Byebug.displays.each { |d| file.puts "display #{d[1]}" if d[0] } + end + + def save_settings(file) + %w[autoirb autolist basename].each do |setting| + file.puts "set #{setting} #{Setting[setting.to_sym]}" + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/set.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/set.rb new file mode 100644 index 0000000..b64ab60 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/set.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require_relative "../command" +require_relative "../helpers/parse" + +module Byebug + # + # Change byebug settings. + # + class SetCommand < Command + include Helpers::ParseHelper + + self.allow_in_control = true + self.allow_in_post_mortem = true + + def self.regexp + /^\s* set (?:\s+(?\w+))? (?:\s+(?\S+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + set + + #{short_description} + + Boolean values take "on", "off", "true", "false", "1" or "0". If you + don't specify a value, the boolean setting will be enabled. Conversely, + you can use "set no" to disable them. + + You can see these environment settings with the "show" command. + DESCRIPTION + end + + def self.short_description + "Modifies byebug settings" + end + + def self.help + super + Setting.help_all + end + + def execute + key = @match[:setting] + value = @match[:value] + return puts(help) if key.nil? && value.nil? + + setting = Setting.find(key) + return errmsg(pr("set.errors.unknown_setting", key: key)) unless setting + + if !setting.boolean? && value.nil? + err = pr("set.errors.must_specify_value", key: key) + elsif setting.boolean? + value, err = get_onoff(value, /^no/.match?(key) ? false : true) + elsif setting.integer? + value, err = get_int(value, setting.to_sym, 1) + end + return errmsg(err) if value.nil? + + setting.value = value + + puts setting.to_s + end + + private + + def get_onoff(arg, default) + return default if arg.nil? + + case arg + when "1", "on", "true" + true + when "0", "off", "false" + false + else + [nil, pr("set.errors.on_off", arg: arg)] + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/show.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/show.rb new file mode 100644 index 0000000..d784865 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/show.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require_relative "../command" + +module Byebug + # + # Show byebug settings. + # + class ShowCommand < Command + self.allow_in_control = true + self.allow_in_post_mortem = true + + def self.regexp + /^\s* show (?:\s+(?\w+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + show + + #{short_description} + + You can change them with the "set" command. + DESCRIPTION + end + + def self.short_description + "Shows byebug settings" + end + + def self.help + super + Setting.help_all + end + + def execute + key = @match[:setting] + return puts(help) unless key + + setting = Setting.find(key) + return errmsg(pr("show.errors.unknown_setting", key: key)) unless setting + + puts Setting.settings[setting.to_sym] + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/skip.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/skip.rb new file mode 100644 index 0000000..0b86c2a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/skip.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +require_relative "../command" +require_relative "../helpers/parse" + +module Byebug + # + # Allows the user to continue execution until the next breakpoint, as + # long as it is different from the current one + # + class SkipCommand < Command + include Helpers::ParseHelper + + class << self + attr_writer :file_line, :file_path + attr_reader :previous_autolist + + def file_line + @file_line ||= 0 + end + + def file_path + @file_path ||= "" + end + + def setup_autolist(value) + @previous_autolist = ListCommand.always_run + ListCommand.always_run = value + end + + def restore_autolist + ListCommand.always_run = @previous_autolist + @previous_autolist = nil + end + end + + def self.regexp + /^\s* sk(?:ip)? \s*$/x + end + + def self.description + <<-DESCRIPTION + sk[ip] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Runs until the next breakpoint as long as it is different from the current one" + end + + def initialize_attributes + self.class.always_run = 2 + self.class.setup_autolist(0) + self.class.file_path = frame.file + self.class.file_line = frame.line + end + + def keep_execution + [self.class.file_path, self.class.file_line] == [frame.file, frame.line] + end + + def reset_attributes + self.class.always_run = 0 + ListCommand.new(processor).execute if self.class.previous_autolist == 1 + self.class.restore_autolist + end + + def auto_run + return false unless self.class.always_run == 2 + + keep_execution ? processor.proceed! : reset_attributes + true + end + + def execute + return if auto_run + + initialize_attributes + processor.proceed! + Byebug.stop if Byebug.stoppable? + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/source.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/source.rb new file mode 100644 index 0000000..c7d2b51 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/source.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require_relative "../command" + +module Byebug + # + # Execute a file containing byebug commands. + # + # It can be used to restore a previously saved debugging session. + # + class SourceCommand < Command + self.allow_in_control = true + self.allow_in_post_mortem = true + + def self.regexp + /^\s* so(?:urce)? (?:\s+(\S+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + source + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Restores a previously saved byebug session" + end + + def execute + return puts(help) unless @match[1] + + file = File.expand_path(@match[1]).strip + return errmsg(pr("source.errors.not_found", file: file)) unless File.exist?(file) + + processor.interface.read_file(file) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/step.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/step.rb new file mode 100644 index 0000000..32f7a77 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/step.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require_relative "../command" +require_relative "../helpers/parse" + +module Byebug + # + # Implements the step functionality. + # + # Allows the user the continue execution until the next instruction, possibily + # in a different frame. Use step to step into method calls or blocks. + # + class StepCommand < Command + include Helpers::ParseHelper + + def self.regexp + /^\s* s(?:tep)? (?:\s+(\S+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + s[tep][ times] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Steps into blocks or methods one or more times" + end + + def execute + steps, err = parse_steps(@match[1], "Steps") + return errmsg(err) unless steps + + context.step_into(steps, context.frame.pos) + processor.proceed! + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/thread.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/thread.rb new file mode 100644 index 0000000..0047b81 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/thread.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require_relative "../subcommands" + +require_relative "../commands/thread/current" +require_relative "../commands/thread/list" +require_relative "../commands/thread/resume" +require_relative "../commands/thread/stop" +require_relative "../commands/thread/switch" + +module Byebug + # + # Manipulation of Ruby threads + # + class ThreadCommand < Command + include Subcommands + + def self.regexp + /^\s* th(?:read)? (?:\s+ (.+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + th[read] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Commands to manipulate threads" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/thread/current.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/thread/current.rb new file mode 100644 index 0000000..236ac85 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/thread/current.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require_relative "../../helpers/thread" + +module Byebug + # + # Reopens the +thread+ command to define the +current+ subcommand + # + class ThreadCommand < Command + # + # Information about the current thread + # + class CurrentCommand < Command + include Helpers::ThreadHelper + + def self.regexp + /^\s* c(?:urrent)? \s*$/x + end + + def self.description + <<-DESCRIPTION + th[read] c[urrent] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Shows current thread information" + end + + def execute + display_context(context) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/thread/list.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/thread/list.rb new file mode 100644 index 0000000..ee263e2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/thread/list.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require_relative "../../helpers/thread" + +module Byebug + # + # Reopens the +thread+ command to define the +list+ subcommand + # + class ThreadCommand < Command + # + # Information about threads + # + class ListCommand < Command + include Helpers::ThreadHelper + + def self.regexp + /^\s* l(?:ist)? \s*$/x + end + + def self.description + <<-DESCRIPTION + th[read] l[ist] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Lists all threads" + end + + def execute + contexts = Byebug.contexts.sort_by(&:thnum) + + thread_list = prc("thread.context", contexts) do |context, _| + thread_arguments(context) + end + + print(thread_list) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/thread/resume.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/thread/resume.rb new file mode 100644 index 0000000..dcca263 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/thread/resume.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require_relative "../../helpers/thread" + +module Byebug + # + # Reopens the +thread+ command to define the +resume+ subcommand + # + class ThreadCommand < Command + # + # Resumes the specified thread + # + class ResumeCommand < Command + include Helpers::ThreadHelper + + def self.regexp + /^\s* r(?:esume)? (?: \s* (\d+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + th[read] r[esume] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Resumes execution of the specified thread" + end + + def execute + return puts(help) unless @match[1] + + context, err = context_from_thread(@match[1]) + return errmsg(err) if err + + return errmsg(pr("thread.errors.already_running")) unless context.suspended? + + context.resume + display_context(context) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/thread/stop.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/thread/stop.rb new file mode 100644 index 0000000..921f6a0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/thread/stop.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require_relative "../../helpers/thread" + +module Byebug + # + # Reopens the +thread+ command to define the +stop+ subcommand + # + class ThreadCommand < Command + # + # Stops the specified thread + # + class StopCommand < Command + include Helpers::ThreadHelper + + def self.regexp + /^\s* st(?:op)? (?: \s* (\d+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + th[read] st[op] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Stops the execution of the specified thread" + end + + def execute + return puts(help) unless @match[1] + + context, err = context_from_thread(@match[1]) + return errmsg(err) if err + + context.suspend + display_context(context) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/thread/switch.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/thread/switch.rb new file mode 100644 index 0000000..039dcda --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/thread/switch.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require_relative "../../helpers/thread" + +module Byebug + # + # Reopens the +thread+ command to define the +switch+ subcommand + # + class ThreadCommand < Command + # + # Switches to the specified thread + # + class SwitchCommand < Command + include Helpers::ThreadHelper + + def self.regexp + /^\s* sw(?:itch)? (?: \s* (\d+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + th[read] sw[itch] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Switches execution to the specified thread" + end + + def execute + return puts(help) unless @match[1] + + context, err = context_from_thread(@match[1]) + return errmsg(err) if err + + display_context(context) + + context.switch + + processor.proceed! + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/tracevar.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/tracevar.rb new file mode 100644 index 0000000..94e817a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/tracevar.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require_relative "../command" + +module Byebug + # + # Show (and possibily stop) at every line that changes a global variable. + # + class TracevarCommand < Command + def self.regexp + /^\s* tr(?:acevar)? (?: \s+ (\S+))? # (variable-name)? + (?: \s+ (stop|nostop))? + \s*$/x + end + + def self.description + <<-DESCRIPTION + tr[acevar] [[no]stop] + + #{short_description} + + If "stop" is specified, execution will stop every time the variable + changes its value. If nothing or "nostop" is specified, execution won't + stop, changes will just be logged in byebug's output. + DESCRIPTION + end + + def self.short_description + "Enables tracing of a global variable" + end + + def execute + var = @match[1] + return errmsg(pr("trace.errors.needs_global_variable")) unless var + return errmsg(pr("trace.errors.var_is_not_global", name: var)) unless global_variables.include?(:"#{var}") + + stop = @match[2] && @match[2] !~ /nostop/ + + instance_eval do + trace_var(:"#{var}") { |val| on_change(var, val, stop) } + end + + puts pr("trace.messages.success", var: var) + end + + private + + def on_change(name, value, stop) + puts pr("trace.messages.on_change", name: name, value: value) + + context.step_out(1, false) if stop + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/undisplay.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/undisplay.rb new file mode 100644 index 0000000..72703d9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/undisplay.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require_relative "../command" +require_relative "../helpers/parse" + +module Byebug + # + # Remove expressions from display list. + # + class UndisplayCommand < Command + include Helpers::ParseHelper + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* undisp(?:lay)? (?:\s+(\S+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + undisp[lay][ nnn] + + #{short_description} + + Arguments are the code numbers of the expressions to stop displaying. No + argument means cancel all automatic-display expressions. Type "info + display" to see the current list of code numbers. + DESCRIPTION + end + + def self.short_description + "Stops displaying all or some expressions when program stops" + end + + def execute + if @match[1] + pos, err = get_int(@match[1], "Undisplay", 1, Byebug.displays.size) + return errmsg(err) unless err.nil? + + last_display = Byebug.displays[pos - 1] + return errmsg(pr("display.errors.undefined", expr: pos)) unless last_display + + last_display[0] = nil + else + return unless confirm(pr("display.confirmations.clear_all")) + + Byebug.displays.each { |d| d[0] = false } + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/untracevar.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/untracevar.rb new file mode 100644 index 0000000..5e395ee --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/untracevar.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require_relative "../command" + +module Byebug + # + # Stop tracing a global variable. + # + class UntracevarCommand < Command + def self.regexp + /^\s* untr(?:acevar)? (?:\s+ (\S+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + untr[acevar] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Stops tracing a global variable" + end + + def execute + var = @match[1] + if global_variables.include?(:"#{var}") + untrace_var(:"#{var}") + puts pr("trace.messages.undo", var: var) + else + errmsg pr("trace.errors.not_global", var: var) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/up.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/up.rb new file mode 100644 index 0000000..ff73513 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/up.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require "pathname" +require_relative "../command" +require_relative "../helpers/frame" +require_relative "../helpers/parse" + +module Byebug + # + # Move the current frame up in the backtrace. + # + class UpCommand < Command + include Helpers::FrameHelper + include Helpers::ParseHelper + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* up (?:\s+(\S+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + up[ count] + + #{short_description} + + Use the "bt" command to find out where you want to go. + DESCRIPTION + end + + def self.short_description + "Moves to a higher frame in the stack trace" + end + + def execute + pos, err = parse_steps(@match[1], "Up") + return errmsg(err) unless pos + + jump_frames(pos) + + ListCommand.new(processor).execute if Setting[:autolist] + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/var.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/var.rb new file mode 100644 index 0000000..1abf587 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/var.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require_relative "../subcommands" + +require_relative "var/all" +require_relative "var/args" +require_relative "var/const" +require_relative "var/instance" +require_relative "var/local" +require_relative "var/global" + +module Byebug + # + # Shows variables and its values + # + class VarCommand < Command + include Subcommands + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* v(?:ar)? (?:\s+ (.+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + [v]ar + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Shows variables and its values" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/var/all.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/var/all.rb new file mode 100644 index 0000000..8332b02 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/var/all.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require_relative "../../helpers/var" + +module Byebug + # + # Reopens the +var+ command to define the +all+ subcommand + # + class VarCommand < Command + # + # Shows global, instance and local variables + # + class AllCommand < Command + include Helpers::VarHelper + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* a(?:ll)? \s*$/x + end + + def self.description + <<-DESCRIPTION + v[ar] a[ll] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Shows local, global and instance variables of self." + end + + def execute + var_global + var_instance("self") + var_local + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/var/args.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/var/args.rb new file mode 100644 index 0000000..c9a5350 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/var/args.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require_relative "../../helpers/var" + +module Byebug + # + # Reopens the +var+ command to define the +args+ subcommand + # + class VarCommand < Command + # + # Information about arguments of the current method/block + # + class ArgsCommand < Command + include Helpers::VarHelper + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* a(?:rgs)? \s*$/x + end + + def self.description + <<-DESCRIPTION + v[ar] a[args] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Information about arguments of the current scope" + end + + def execute + var_args + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/var/const.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/var/const.rb new file mode 100644 index 0000000..9f0a601 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/var/const.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require_relative "../../helpers/eval" + +module Byebug + # + # Reopens the +var+ command to define the +const+ subcommand + # + class VarCommand < Command + # + # Shows constants + # + class ConstCommand < Command + include Helpers::EvalHelper + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* c(?:onst)? (?:\s+ (.+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + v[ar] c[onstant] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Shows constants of an object." + end + + def execute + obj = warning_eval(str_obj) + return errmsg(pr("variable.errors.not_module", object: str_obj)) unless obj.is_a?(Module) + + constants = warning_eval("#{str_obj}.constants") + puts prv(constants.sort.map { |c| [c, obj.const_get(c)] }, "constant") + end + + private + + def str_obj + @str_obj ||= @match[1] || "self.class" + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/var/global.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/var/global.rb new file mode 100644 index 0000000..fb28bd2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/var/global.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Byebug + # + # Reopens the +var+ command to define the +global+ subcommand + # + class VarCommand < Command + # + # Shows global variables + # + class GlobalCommand < Command + include Helpers::VarHelper + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* g(?:lobal)? \s*$/x + end + + def self.description + <<-DESCRIPTION + v[ar] g[lobal] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Shows global variables." + end + + def execute + var_global + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/var/instance.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/var/instance.rb new file mode 100644 index 0000000..bf99e9e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/var/instance.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require_relative "../../helpers/var" + +module Byebug + # + # Reopens the +var+ command to define the +instance+ subcommand + # + class VarCommand < Command + # + # Shows instance variables + # + class InstanceCommand < Command + include Helpers::VarHelper + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* i(?:nstance)? (?:\s+ (.+))? \s*$/x + end + + def self.description + <<-DESCRIPTION + v[ar] i[nstance][ ] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Shows instance variables of self or a specific object." + end + + def execute + var_instance(@match[1]) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/var/local.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/var/local.rb new file mode 100644 index 0000000..462760c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/var/local.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require_relative "../../helpers/var" + +module Byebug + # + # Reopens the +var+ command to define the +local+ subcommand + # + class VarCommand < Command + # + # Shows local variables in current scope + # + class LocalCommand < Command + include Helpers::VarHelper + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* l(?:ocal)? \s*$/x + end + + def self.description + <<-DESCRIPTION + v[ar] l[ocal] + + #{short_description} + DESCRIPTION + end + + def self.short_description + "Shows local variables in current scope." + end + + def execute + var_local + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/where.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/where.rb new file mode 100644 index 0000000..ce46a48 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/commands/where.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require "pathname" +require_relative "../command" +require_relative "../helpers/frame" + +module Byebug + # + # Show current backtrace. + # + class WhereCommand < Command + include Helpers::FrameHelper + + self.allow_in_post_mortem = true + + def self.regexp + /^\s* (?:w(?:here)?|bt|backtrace) \s*$/x + end + + def self.description + <<-DESCRIPTION + w[here]|bt|backtrace + + #{short_description} + + Print the entire stack frame. Each frame is numbered; the most recent + frame is 0. A frame number can be referred to in the "frame" command. + "up" and "down" add or subtract respectively to frame numbers shown. + The position of the current frame is marked with -->. C-frames hang + from their most immediate Ruby frame to indicate that they are not + navigable. + DESCRIPTION + end + + def self.short_description + "Displays the backtrace" + end + + def execute + print_backtrace + end + + private + + def print_backtrace + bt = prc("frame.line", (0...context.stack_size)) do |_, index| + Frame.new(context, index).to_hash + end + + print(bt) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/context.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/context.rb new file mode 100644 index 0000000..d54c853 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/context.rb @@ -0,0 +1,157 @@ +# frozen_string_literal: true + +require_relative "frame" +require_relative "helpers/path" +require_relative "helpers/file" +require_relative "processors/command_processor" + +module Byebug + # + # Mantains context information for the debugger and it's the main + # communication point between the library and the C-extension through the + # at_breakpoint, at_catchpoint, at_tracing, at_line and at_return callbacks + # + class Context + include Helpers::FileHelper + + class << self + include Helpers::PathHelper + + attr_writer :ignored_files + + # + # List of files byebug will ignore while debugging + # + def ignored_files + @ignored_files ||= + Byebug.mode == :standalone ? lib_files + [bin_file] : lib_files + end + + attr_writer :interface + + def interface + @interface ||= LocalInterface.new + end + + attr_writer :processor + + def processor + @processor ||= CommandProcessor + end + end + + # + # Reader for the current frame + # + def frame + @frame ||= Frame.new(self, 0) + end + + # + # Writer for the current frame + # + def frame=(pos) + @frame = Frame.new(self, pos) + end + + extend Forwardable + def_delegators :frame, :file, :line + + # + # Current file & line information + # + def location + "#{normalize(file)}:#{line}" + end + + # + # Current file, line and source code information + # + def full_location + return location if virtual_file?(file) + + "#{location} #{get_line(file, line)}" + end + + # + # Context's stack size + # + def stack_size + return 0 unless backtrace + + backtrace.drop_while { |l| ignored_file?(l.first.path) } + .take_while { |l| !ignored_file?(l.first.path) } + .size + end + + def interrupt + step_into 1 + end + + # + # Line handler + # + def at_line + self.frame = 0 + return if ignored_file?(file) + + processor.at_line + end + + # + # Tracing handler + # + def at_tracing + return if ignored_file?(file) + + processor.at_tracing + end + + # + # Breakpoint handler + # + def at_breakpoint(breakpoint) + processor.at_breakpoint(breakpoint) + end + + # + # Catchpoint handler + # + def at_catchpoint(exception) + processor.at_catchpoint(exception) + end + + # + # Return handler + # + def at_return(return_value) + return if ignored_file?(file) + + processor.at_return(return_value) + end + + # + # End of class definition handler + # + def at_end + return if ignored_file?(file) + + processor.at_end + end + + private + + def processor + @processor ||= self.class.processor.new(self, self.class.interface) + end + + # + # Tells whether a file is ignored by the debugger. + # + # @param path [String] filename to be checked. + # + def ignored_file?(path) + self.class.ignored_files.include?(path) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/core.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/core.rb new file mode 100644 index 0000000..bbb71ec --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/core.rb @@ -0,0 +1,115 @@ +# frozen_string_literal: true + +require_relative "helpers/reflection" +require "byebug/byebug" +require_relative "context" +require_relative "breakpoint" +require_relative "interface" +require_relative "processors/script_processor" +require_relative "processors/post_mortem_processor" +require_relative "commands" +require_relative "remote" +require_relative "printers/plain" + +# +# Main debugger's container module. Everything is defined under this module +# +module Byebug + include Helpers::ReflectionHelper + + extend self + + # + # Configuration file used for startup commands. Default value is .byebugrc + # + attr_accessor :init_file + self.init_file = ".byebugrc" + + # + # Debugger's display expressions + # + attr_accessor :displays + self.displays = [] + + # + # Running mode of the debugger. Can be either: + # + # * :attached => Attached to a running program through the `byebug` method. + # * :standalone => Started through `byebug` script. + # * :off => Ignoring any `byebug` method calls. + # + attr_accessor :mode + + # + # Runs normal byebug initialization scripts. + # + # Reads and executes the commands from init file (if any) in the current + # working directory. This is only done if the current directory is different + # from your home directory. Thus, you can have more than one init file, one + # generic in your home directory, and another, specific to the program you + # are debugging, in the directory where you invoke byebug. + # + def run_init_script + rc_dirs.each do |dir| + rc_file = File.expand_path(File.join(dir, init_file)) + next unless File.exist?(rc_file) + + run_rc_file(rc_file) + end + end + + def self.load_settings + Dir.glob(File.join(__dir__, "settings", "*.rb")).each do |file| + require file + end + + constants.grep(/[a-z]Setting/).map do |name| + setting = const_get(name).new + Byebug::Setting.settings[setting.to_sym] = setting + end + end + + # + # Saves information about the unhandled exception and gives a byebug + # prompt back to the user before program termination. + # + def self.handle_post_mortem + return unless raised_exception + + context = raised_exception.__bb_context + + PostMortemProcessor.new(context).at_line + end + + at_exit { Byebug.handle_post_mortem if Byebug.post_mortem? } + + private + + # + # Runs a initialization script file + # + def run_rc_file(rc_file) + interface = ScriptInterface.new(rc_file) + + ScriptProcessor.new(nil, interface).process_commands + end + + # + # List of folders to load rc files from + # + # @note Files will be loaded in the order specified here. + # + def rc_dirs + [ENV["HOME"], Dir.pwd].compact.uniq + end +end + +Byebug.load_settings + +# +# Extends the extension class to be able to pass information about the +# debugging environment from the c-extension to the user. +# +class Exception + attr_reader :__bb_context +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/errors.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/errors.rb new file mode 100644 index 0000000..01ee4f4 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/errors.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Byebug + # + # Custom exception exception to signal "command not found" errors + # + class CommandNotFound < NoMethodError + def initialize(input, parent = nil) + @input = input + @parent = parent + + super("Unknown command '#{name}'. Try '#{help}'") + end + + private + + def name + build_cmd(@parent, @input) + end + + def help + build_cmd("help", @parent) + end + + def build_cmd(*args) + args.compact.join(" ") + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/frame.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/frame.rb new file mode 100644 index 0000000..4272dd3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/frame.rb @@ -0,0 +1,185 @@ +# frozen_string_literal: true + +require_relative "helpers/file" + +module Byebug + # + # Represents a frame in the stack trace + # + class Frame + include Helpers::FileHelper + + attr_reader :pos + + def initialize(context, pos) + @context = context + @pos = pos + end + + def file + @context.frame_file(pos) + end + + def line + @context.frame_line(pos) + end + + def _self + @context.frame_self(pos) + end + + def _binding + @context.frame_binding(pos) + end + + def _class + @context.frame_class(pos) + end + + def _method + @context.frame_method(pos) + end + + def current? + @context.frame.pos == pos + end + + # + # Gets local variables for the frame. + # + def locals + return [] unless _binding + + _binding.local_variables.each_with_object({}) do |e, a| + a[e] = _binding.local_variable_get(e) + a + end + end + + # + # Gets current method arguments for the frame. + # + def args + return c_args unless _binding + + ruby_args + end + + # + # Returns the current class in the frame or an empty string if the current + # +callstyle+ setting is 'short' + # + def deco_class + Setting[:callstyle] == "short" || _class.to_s.empty? ? "" : "#{_class}." + end + + def deco_block + _method[/(?:block(?: \(\d+ levels\))?|rescue) in /] || "" + end + + def deco_method + _method[/((?:block(?: \(\d+ levels\))?|rescue) in )?(.*)/] + end + + # + # Builds a string containing all available args in the frame number, in a + # verbose or non verbose way according to the value of the +callstyle+ + # setting + # + def deco_args + return "" if args.empty? + + my_args = args.map do |arg| + prefix, default = prefix_and_default(arg[0]) + + kls = use_short_style?(arg) ? "" : "##{locals[arg[1]].class}" + + "#{prefix}#{arg[1] || default}#{kls}" + end + + "(#{my_args.join(', ')})" + end + + # + # Builds a formatted string containing information about current method call + # + def deco_call + deco_block + deco_class + deco_method + deco_args + end + + # + # Formatted filename in frame + # + def deco_file + Setting[:fullpath] ? File.expand_path(file) : shortpath(file) + end + + # + # Properly formatted frame number of frame + # + def deco_pos + format("%-2d", pos: pos) + end + + # + # Formatted mark for the frame. + # + # --> marks the current frame + # ͱ-- marks c-frames + # marks regular frames + # + def mark + return "-->" if current? + return " ͱ--" if c_frame? + + " " + end + + # + # Checks whether the frame is a c-frame + # + def c_frame? + _binding.nil? + end + + def to_hash + { + mark: mark, + pos: deco_pos, + call: deco_call, + file: deco_file, + line: line, + full_path: File.expand_path(deco_file) + } + end + + private + + def c_args + return [] unless _self.to_s != "main" + + _class.instance_method(_method).parameters + end + + def ruby_args + meth_name = _binding.eval("__method__") + return [] unless meth_name + + meth_obj = _class.instance_method(meth_name) + return [] unless meth_obj + + meth_obj.parameters + end + + def use_short_style?(arg) + Setting[:callstyle] == "short" || arg[1].nil? || locals.empty? + end + + def prefix_and_default(arg_type) + return ["&", "block"] if arg_type == :block + return ["*", "args"] if arg_type == :rest + + ["", nil] + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/bin.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/bin.rb new file mode 100644 index 0000000..9d79966 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/bin.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +module Byebug + module Helpers + # + # Utilities for interaction with executables + # + module BinHelper + # + # Cross-platform way of finding an executable in the $PATH. + # Adapted from: https://gist.github.com/steakknife/88b6c3837a5e90a08296 + # + def which(cmd) + return File.expand_path(cmd) if File.exist?(cmd) + + [nil, *search_paths].each do |path| + exe = find_executable(path, cmd) + return exe if exe + end + + nil + end + + def find_executable(path, cmd) + executable_file_extensions.each do |ext| + exe = File.expand_path(cmd + ext, path) + + return exe if real_executable?(exe) + end + + nil + end + + def search_paths + ENV["PATH"].split(File::PATH_SEPARATOR) + end + + def executable_file_extensions + ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""] + end + + def real_executable?(file) + File.executable?(file) && !File.directory?(file) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/eval.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/eval.rb new file mode 100644 index 0000000..f1393c8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/eval.rb @@ -0,0 +1,126 @@ +# frozen_string_literal: true + +module Byebug + module Helpers + # + # Utilities to assist evaluation of code strings + # + module EvalHelper + # + # Evaluates an +expression+ in a separate thread. + # + # @param expression [String] Expression to evaluate + # + def separate_thread_eval(expression) + allowing_other_threads do + in_new_thread { warning_eval(expression) } + end + end + + # + # Evaluates an +expression+ that might use or defer execution to threads + # other than the current one. + # + # @note This is necessary because when in byebug's prompt, every thread is + # "frozen" so that nothing gets run. So we need to unlock threads prior + # to evaluation or we will run into a deadlock. + # + # @param expression [String] Expression to evaluate + # + def multiple_thread_eval(expression) + allowing_other_threads { warning_eval(expression) } + end + + # + # Evaluates a string containing Ruby code in a specific binding, + # returning nil in an error happens. + # + def silent_eval(str, binding = frame._binding) + safe_eval(str, binding) { |_e| nil } + end + + # + # Evaluates a string containing Ruby code in a specific binding, + # handling the errors at an error level. + # + def error_eval(str, binding = frame._binding) + safe_eval(str, binding) { |e| raise(e, msg(e)) } + end + + # + # Evaluates a string containing Ruby code in a specific binding, + # handling the errors at a warning level. + # + def warning_eval(str, binding = frame._binding) + safe_eval(str, binding) { |e| errmsg(msg(e)) } + end + + private + + def safe_eval(str, binding) + binding.eval(str.gsub(/\Aeval /, ""), "(byebug)", 1) + rescue StandardError, ScriptError => e + yield(e) + end + + def msg(exception) + msg = Setting[:stack_on_error] ? error_msg(exception) : warning_msg(exception) + + pr("eval.exception", text_message: msg) + end + + def error_msg(exception) + at = exception.backtrace + + locations = ["#{at.shift}: #{warning_msg(exception)}"] + locations += at.map { |path| " from #{path}" } + locations.join("\n") + end + + def warning_msg(exception) + "#{exception.class} Exception: #{exception.message}" + end + + # + # Run block temporarily ignoring all TracePoint events. + # + # Used to evaluate stuff within Byebug's prompt. Otherwise, any code + # creating new threads won't be properly evaluated because new threads + # will get blocked by byebug's main thread. + # + def allowing_other_threads + Byebug.unlock + + res = yield + + Byebug.lock + + res + end + + # + # Runs the given block in a new thread, waits for it to finish and + # returns the new thread's result. + # + def in_new_thread + res = nil + + Thread.new { res = yield }.join + + res + end + + def safe_inspect(var) + var.inspect + rescue StandardError + safe_to_s(var) + end + + def safe_to_s(var) + var.to_s + rescue StandardError + "*Error in evaluation*" + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/file.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/file.rb new file mode 100644 index 0000000..636b1e0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/file.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +module Byebug + module Helpers + # + # Utilities for interaction with files + # + module FileHelper + # + # Reads lines of source file +filename+ into an array + # + def get_lines(filename) + File.foreach(filename).reduce([]) { |acc, elem| acc << elem.chomp } + end + + # + # Reads line number +lineno+ from file named +filename+ + # + def get_line(filename, lineno) + File.open(filename) do |f| + f.gets until f.lineno == lineno - 1 + f.gets + end + end + + # + # Returns the number of lines in file +filename+ in a portable, + # one-line-at-a-time way. + # + def n_lines(filename) + File.foreach(filename).reduce(0) { |acc, _elem| acc + 1 } + end + + # + # Regularize file name. + # + def normalize(filename) + return filename if virtual_file?(filename) + + return File.basename(filename) if Setting[:basename] + + File.exist?(filename) ? File.realpath(filename) : filename + end + + # + # A short version of a long path + # + def shortpath(fullpath) + components = Pathname(fullpath).each_filename.to_a + return fullpath if components.size <= 2 + + File.join("...", components[-3..-1]) + end + + # + # True for special files like -e, false otherwise + # + def virtual_file?(name) + ["(irb)", "-e", "(byebug)", "(eval)"].include?(name) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/frame.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/frame.rb new file mode 100644 index 0000000..64a5f8e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/frame.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +module Byebug + module Helpers + # + # Utilities to assist frame navigation + # + module FrameHelper + def switch_to_frame(frame) + new_frame = index_from_start(frame) + return frame_err("c_frame") if Frame.new(context, new_frame).c_frame? + + adjust_frame(new_frame) + end + + def jump_frames(steps) + adjust_frame(navigate_to_frame(steps)) + end + + private + + def adjust_frame(new_frame) + return frame_err("too_low") if new_frame >= context.stack_size + return frame_err("too_high") if new_frame.negative? + + context.frame = new_frame + processor.prev_line = nil + end + + def navigate_to_frame(jump_no) + current_jumps = 0 + current_pos = context.frame.pos + + loop do + current_pos += direction(jump_no) + break if out_of_bounds?(current_pos) + + next if Frame.new(context, current_pos).c_frame? + + current_jumps += 1 + break if current_jumps == jump_no.abs + end + + current_pos + end + + def out_of_bounds?(pos) + !(0...context.stack_size).cover?(pos) + end + + def frame_err(msg) + errmsg(pr("frame.errors.#{msg}")) + end + + # + # @param step [Integer] A positive or negative integer + # + # @return [Integer] +1 if step is positive / -1 if negative + # + def direction(step) + step / step.abs + end + + # + # Convert a possibly negative index to a positive index from the start + # of the callstack. -1 is the last position in the stack and so on. + # + # @param i [Integer] Integer to be converted in a proper positive index. + # + def index_from_start(index) + index >= 0 ? index : context.stack_size + index + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/parse.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/parse.rb new file mode 100644 index 0000000..dae26c9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/parse.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +module Byebug + module Helpers + # + # Utilities to assist command parsing + # + module ParseHelper + # + # Parses +str+ of command +cmd+ as an integer between +min+ and +max+. + # + # If either +min+ or +max+ is nil, that value has no bound. + # + # @todo Remove the `cmd` parameter. It has nothing to do with the method's + # purpose. + # + def get_int(str, cmd, min = nil, max = nil) + return nil, pr("parse.errors.int.not_number", cmd: cmd, str: str) unless /\A-?[0-9]+\z/.match?(str) + + int = str.to_i + if min && int < min + err = pr("parse.errors.int.too_low", cmd: cmd, str: str, min: min) + return nil, err + elsif max && int > max + err = pr("parse.errors.int.too_high", cmd: cmd, str: str, max: max) + return nil, err + end + + int + end + + # + # @return true if code is syntactically correct for Ruby, false otherwise + # + def syntax_valid?(code) + return true unless code + + without_stderr do + begin + RubyVM::InstructionSequence.compile(code) + true + rescue SyntaxError + false + end + end + end + + # + # @return +str+ as an integer or 1 if +str+ is empty. + # + def parse_steps(str, cmd) + return 1 unless str + + steps, err = get_int(str, cmd, 1) + return nil, err unless steps + + steps + end + + private + + # + # Temporarily disable output to $stderr + # + def without_stderr + old_stderr = $stderr + $stderr = StringIO.new + + yield + ensure + $stderr = old_stderr + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/path.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/path.rb new file mode 100644 index 0000000..c242249 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/path.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Byebug + module Helpers + # + # Utilities for managing gem paths + # + module PathHelper + def bin_file + @bin_file ||= File.join(root_path, "exe", "byebug") + end + + def root_path + @root_path ||= File.expand_path(File.join("..", "..", ".."), __dir__) + end + + def lib_files + @lib_files ||= glob_for("lib") + end + + def test_files + @test_files ||= glob_for("test") + end + + def gem_files + @gem_files ||= [bin_file] + lib_files + end + + def all_files + @all_files ||= gem_files + test_files + end + + private + + def glob_for(dir) + Dir.glob(File.join(root_path, dir, "**", "*.rb")) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/reflection.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/reflection.rb new file mode 100644 index 0000000..ceb8fb4 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/reflection.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Byebug + module Helpers + # + # Reflection utilitie + # + module ReflectionHelper + # + # List of "command" classes in the including module + # + def commands + constants(false) + .map { |const| const_get(const, false) } + .select { |c| c.is_a?(Class) && c.name =~ /[a-z]Command$/ } + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/string.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/string.rb new file mode 100644 index 0000000..d265d96 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/string.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Byebug + module Helpers + # + # Utilities for interaction with strings + # + module StringHelper + # + # Converts +str+ from an_underscored-or-dasherized_string to + # ACamelizedString. + # + def camelize(str) + str.dup.split(/[_-]/).map(&:capitalize).join("") + end + + # + # Improves indentation and spacing in +str+ for readability in Byebug's + # command prompt. + # + def prettify(str) + "\n" + deindent(str) + "\n" + end + + # + # Removes a number of leading whitespace for each input line. + # + def deindent(str, leading_spaces: 6) + str.gsub(/^ {#{leading_spaces}}/, "") + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/thread.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/thread.rb new file mode 100644 index 0000000..02c9f8e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/thread.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +module Byebug + module Helpers + # + # Utilities for thread subcommands + # + module ThreadHelper + def display_context(ctx) + puts pr("thread.context", thread_arguments(ctx)) + end + + def thread_arguments(ctx) + { + status_flag: status_flag(ctx), + debug_flag: debug_flag(ctx), + id: ctx.thnum, + thread: ctx.thread.inspect, + file_line: location(ctx), + pid: Process.pid, + status: ctx.thread.status, + current: current_thread?(ctx) + } + end + + def current_thread?(ctx) + ctx.thread == Thread.current + end + + def context_from_thread(thnum) + ctx = Byebug.contexts.find { |c| c.thnum.to_s == thnum } + + err = if ctx.nil? + pr("thread.errors.no_thread") + elsif ctx == context + pr("thread.errors.current_thread") + elsif ctx.ignored? + pr("thread.errors.ignored", arg: thnum) + end + + [ctx, err] + end + + private + + # @todo Check whether it is Byebug.current_context or context + def location(ctx) + return context.location if ctx == Byebug.current_context + + backtrace = ctx.thread.backtrace_locations + return "" unless backtrace && backtrace[0] + + "#{backtrace[0].path}:#{backtrace[0].lineno}" + end + + def status_flag(ctx) + return "$" if ctx.suspended? + + current_thread?(ctx) ? "+" : " " + end + + def debug_flag(ctx) + ctx.ignored? ? "!" : " " + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/toggle.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/toggle.rb new file mode 100644 index 0000000..b92bf62 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/toggle.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require_relative "parse" + +module Byebug + module Helpers + # + # Utilities to assist breakpoint/display enabling/disabling. + # + module ToggleHelper + include ParseHelper + + def enable_disable_breakpoints(is_enable, args) + raise pr("toggle.errors.no_breakpoints") if Breakpoint.none? + + select_breakpoints(is_enable, args).each do |b| + enabled = (is_enable == "enable") + raise pr("toggle.errors.expression", expr: b.expr) if enabled && !syntax_valid?(b.expr) + + puts pr("toggle.messages.toggled", bpnum: b.id, + endis: enabled ? "en" : "dis") + b.enabled = enabled + end + end + + def enable_disable_display(is_enable, args) + raise pr("toggle.errors.no_display") if n_displays.zero? + + selected_displays = args ? args.split(/ +/) : [1..n_displays + 1] + + selected_displays.each do |pos| + pos, err = get_int(pos, "#{is_enable} display", 1, n_displays) + raise err unless err.nil? + + Byebug.displays[pos - 1][0] = (is_enable == "enable") + end + end + + private + + def select_breakpoints(is_enable, args) + all_breakpoints = Byebug.breakpoints.sort_by(&:id) + return all_breakpoints if args.nil? + + selected_ids = [] + args.split(/ +/).each do |pos| + last_id = all_breakpoints.last.id + pos, err = get_int(pos, "#{is_enable} breakpoints", 1, last_id) + raise(ArgumentError, err) unless pos + + selected_ids << pos + end + + all_breakpoints.select { |b| selected_ids.include?(b.id) } + end + + def n_displays + Byebug.displays.size + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/var.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/var.rb new file mode 100644 index 0000000..40fadb8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/helpers/var.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require_relative "eval" + +module Byebug + module Helpers + # + # Utilities for variable subcommands + # + module VarHelper + include EvalHelper + + def var_list(ary, binding = context.frame._binding) + vars = ary.sort.map do |name| + [name, safe_inspect(silent_eval(name.to_s, binding))] + end + + puts prv(vars, "instance") + end + + def var_global + globals = global_variables.reject do |v| + %i[$IGNORECASE $= $KCODE $-K $binding].include?(v) + end + + var_list(globals) + end + + def var_instance(str) + obj = warning_eval(str || "self") + + var_list(obj.instance_variables, obj.instance_eval { binding }) + end + + def var_local + locals = context.frame.locals + cur_self = context.frame._self + locals[:self] = cur_self unless cur_self.to_s == "main" + puts prv(locals.keys.sort.map { |k| [k, locals[k]] }, "instance") + end + + def var_args + args = context.frame.args + return if args == [[:rest]] + + all_locals = context.frame.locals + arg_values = args.map { |arg| arg[1] } + + locals = all_locals.select { |k, _| arg_values.include?(k) } + puts prv(locals.keys.sort.map { |k| [k, locals[k]] }, "instance") + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/history.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/history.rb new file mode 100644 index 0000000..cf95c29 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/history.rb @@ -0,0 +1,130 @@ +# frozen_string_literal: true + +begin + require "readline" +rescue LoadError + warn <<-MESSAGE + Sorry, you can't use byebug without Readline. To solve this, you need to + rebuild Ruby with Readline support. If using Ubuntu, try `sudo apt-get + install libreadline-dev` and then reinstall your Ruby. + MESSAGE + + raise +end + +module Byebug + # + # Handles byebug's history of commands. + # + class History + attr_accessor :size + + def initialize + self.size = 0 + end + + # + # Array holding the list of commands in history + # + def buffer + Readline::HISTORY.to_a + end + + # + # Restores history from disk. + # + def restore + return unless File.exist?(Setting[:histfile]) + + File.readlines(Setting[:histfile]).reverse_each { |l| push(l.chomp) } + end + + # + # Saves history to disk. + # + def save + n_cmds = Setting[:histsize] > size ? size : Setting[:histsize] + + File.open(Setting[:histfile], "w") do |file| + n_cmds.times { file.puts(pop) } + end + + clear + end + + # + # Discards history. + # + def clear + size.times { pop } + end + + # + # Adds a new command to Readline's history. + # + def push(cmd) + return if ignore?(cmd) + + self.size += 1 + Readline::HISTORY.push(cmd) + end + + # + # Removes a command from Readline's history. + # + def pop + self.size -= 1 + Readline::HISTORY.pop + end + + # + # Prints the requested numbers of history entries. + # + def to_s(n_cmds) + show_size = n_cmds ? specific_max_size(n_cmds) : default_max_size + + commands = buffer.last(show_size) + + last_ids(show_size).zip(commands).map do |l| + format("%5d %s", position: l[0], command: l[1]) + end.join("\n") + "\n" + end + + # + # Array of ids of the last +number+ commands. + # + def last_ids(number) + (1 + size - number..size).to_a + end + + # + # Max number of commands to be displayed when no size has been specified. + # + # Never more than Setting[:histsize]. + # + def default_max_size + [Setting[:histsize], self.size].min + end + + # + # Max number of commands to be displayed when a size has been specified. + # + # The only bound here is not showing more items than available. + # + def specific_max_size(number) + [self.size, number].min + end + + # + # Whether a specific command should not be stored in history. + # + # For now, empty lines and consecutive duplicates. + # + def ignore?(buf) + return true if /^\s*$/.match?(buf) + return false if Readline::HISTORY.empty? + + buffer[Readline::HISTORY.length - 1] == buf + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/interface.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/interface.rb new file mode 100644 index 0000000..43290c6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/interface.rb @@ -0,0 +1,146 @@ +# frozen_string_literal: true + +require_relative "setting" +require_relative "history" +require_relative "helpers/file" + +# +# Namespace for all of byebug's code +# +module Byebug + # + # Main Interface class + # + # Contains common functionality to all implemented interfaces. + # + class Interface + include Helpers::FileHelper + + attr_accessor :command_queue, :history + attr_reader :input, :output, :error + + def initialize + @command_queue = [] + @history = History.new + @last_line = "" + end + + def last_if_empty(input) + @last_line = input.empty? ? @last_line : input + end + + # + # Pops a command from the input stream. + # + def read_command(prompt) + return command_queue.shift unless command_queue.empty? + + read_input(prompt) + end + + # + # Pushes lines in +filename+ to the command queue. + # + def read_file(filename) + command_queue.concat(get_lines(filename)) + end + + # + # Reads a new line from the interface's input stream, parses it into + # commands and saves it to history. + # + # @return [String] Representing something to be run by the debugger. + # + def read_input(prompt, save_hist = true) + line = prepare_input(prompt) + return unless line + + history.push(line) if save_hist + + command_queue.concat(split_commands(line)) + command_queue.shift + end + + # + # Reads a new line from the interface's input stream. + # + # @return [String] New string read or the previous string if the string + # read now was empty. + # + def prepare_input(prompt) + line = readline(prompt) + return unless line + + last_if_empty(line) + end + + # + # Prints an error message to the error stream. + # + def errmsg(message) + error.print("*** #{message}\n") + end + + # + # Prints an output message to the output stream. + # + def puts(message) + output.puts(message) + end + + # + # Prints an output message to the output stream without a final "\n". + # + def print(message) + output.print(message) + end + + # + # Confirms user introduced an affirmative response to the input stream. + # + def confirm(prompt) + readline(prompt) == "y" + end + + def close + end + + # + # Saves or clears history according to +autosave+ setting. + # + def autosave + Setting[:autosave] ? history.save : history.clear + end + + # + # Restores history according to +autosave+ setting. + # + def autorestore + history.restore if Setting[:autosave] + end + + private + + # + # Splits a command line of the form "cmd1 ; cmd2 ; ... ; cmdN" into an + # array of commands: [cmd1, cmd2, ..., cmdN] + # + def split_commands(cmd_line) + return [""] if cmd_line.empty? + + cmd_line.split(/;/).each_with_object([]) do |v, m| + if m.empty? || m.last[-1] != '\\' + m << v.strip + next + end + + m.last[-1, 1] = "" + m.last << ";" << v + end + end + end +end + +require_relative "interfaces/local_interface" +require_relative "interfaces/script_interface" +require_relative "interfaces/remote_interface" diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/local_interface.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/local_interface.rb new file mode 100644 index 0000000..094dff5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/local_interface.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +module Byebug + # + # Interface class for standard byebug use. + # + class LocalInterface < Interface + EOF_ALIAS = "continue" + + def initialize + super() + @input = $stdin + @output = $stdout + @error = $stderr + end + + # + # Reads a single line of input using Readline. If Ctrl-D is pressed, it + # returns "continue", meaning that program's execution will go on. + # + # @param prompt Prompt to be displayed. + # + def readline(prompt) + with_repl_like_sigint { without_readline_completion { Readline.readline(prompt) || EOF_ALIAS } } + end + + # + # Yields the block handling Ctrl-C the following way: if pressed while + # waiting for input, the line is reset to only the prompt and we ask for + # input again. + # + # @note Any external 'INT' traps are overriden during this method. + # + def with_repl_like_sigint + orig_handler = trap("INT") { raise Interrupt } + yield + rescue Interrupt + puts("^C") + retry + ensure + trap("INT", orig_handler) + end + + # + # Disable any Readline completion procs. + # + # Other gems, for example, IRB could've installed completion procs that are + # dependent on them being loaded. Disable those while byebug is the REPL + # making use of Readline. + # + def without_readline_completion + orig_completion = Readline.completion_proc + return yield unless orig_completion + + begin + Readline.completion_proc = ->(_) { nil } + yield + ensure + Readline.completion_proc = orig_completion + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/remote_interface.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/remote_interface.rb new file mode 100644 index 0000000..2daf2cb --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/remote_interface.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require_relative "../history" + +module Byebug + # + # Interface class for remote use of byebug. + # + class RemoteInterface < Interface + def initialize(socket) + super() + @input = socket + @output = socket + @error = socket + end + + def read_command(prompt) + super("PROMPT #{prompt}") + rescue Errno::EPIPE, Errno::ECONNABORTED + "continue" + end + + def confirm(prompt) + super("CONFIRM #{prompt}") + rescue Errno::EPIPE, Errno::ECONNABORTED + false + end + + def print(message) + super(message) + rescue Errno::EPIPE, Errno::ECONNABORTED + nil + end + + def puts(message) + super(message) + rescue Errno::EPIPE, Errno::ECONNABORTED + nil + end + + def close + output.close + end + + def readline(prompt) + puts(prompt) + (input.gets || "continue").chomp + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/script_interface.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/script_interface.rb new file mode 100644 index 0000000..43f40f1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/script_interface.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Byebug + # + # Interface class for command execution from script files. + # + class ScriptInterface < Interface + def initialize(file, verbose = false) + super() + @verbose = verbose + @input = File.open(file) + @output = verbose ? $stdout : StringIO.new + @error = $stderr + end + + def read_command(prompt) + readline(prompt, false) + end + + def close + input.close + end + + def readline(*) + while (result = input.gets) + output.puts "+ #{result}" if @verbose + next if /^\s*#/.match?(result) + + return result.chomp + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/test_interface.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/test_interface.rb new file mode 100644 index 0000000..d732d5f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/test_interface.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +module Byebug + # + # Custom interface for easier assertions + # + class TestInterface < Interface + attr_accessor :test_block + + def initialize + super() + + clear + end + + def errmsg(message) + error.concat(prepare(message)) + end + + def print(message) + output.concat(prepare(message)) + end + + def puts(message) + output.concat(prepare(message)) + end + + def read_command(prompt) + cmd = super(prompt) + + return cmd unless cmd.nil? && test_block + + test_block.call + self.test_block = nil + end + + def clear + @input = [] + @output = [] + @error = [] + history.clear + end + + def inspect + [ + "Input:", input.join("\n"), + "Output:", output.join("\n"), + "Error:", error.join("\n") + ].join("\n") + end + + def readline(prompt) + puts(prompt) + + cmd = input.shift + cmd.is_a?(Proc) ? cmd.call : cmd + end + + private + + def prepare(message) + return message.map(&:to_s) if message.respond_to?(:map) + + message.to_s.split("\n") + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/option_setter.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/option_setter.rb new file mode 100644 index 0000000..d86537c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/option_setter.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +module Byebug + # + # Handles byebug's command line options + # + class OptionSetter + def initialize(runner, opts) + @runner = runner + @opts = opts + end + + def setup + debug + include_flag + post_mortem + quit + rc + stop + require_flag + remote + trace + version + help + end + + private + + def debug + @opts.on "-d", "--debug", "Set $DEBUG=true" do + $DEBUG = true + end + end + + def include_flag + @opts.on "-I", "--include list", "Add to paths to $LOAD_PATH" do |list| + $LOAD_PATH.push(list.split(":")).flatten! + end + end + + def post_mortem + @opts.on "-m", "--[no-]post-mortem", "Use post-mortem mode" do |v| + Setting[:post_mortem] = v + end + end + + def quit + @opts.on "-q", "--[no-]quit", "Quit when script finishes" do |v| + @runner.quit = v + end + end + + def rc + @opts.on "-x", "--[no-]rc", "Run byebug initialization file" do |v| + @runner.init_script = v + end + end + + def stop + @opts.on "-s", "--[no-]stop", "Stop when script is loaded" do |v| + @runner.stop = v + end + end + + def require_flag + @opts.on "-r", "--require file", "Require library before script" do |lib| + require lib + end + end + + def remote + @opts.on "-R", "--remote [host:]port", "Remote debug [host:]port" do |p| + @runner.remote = p + end + end + + def trace + @opts.on "-t", "--[no-]trace", "Turn on line tracing" do |v| + Setting[:linetrace] = v + end + end + + def version + @opts.on "-v", "--version", "Print program version" do + @runner.version = Byebug::VERSION + end + end + + def help + @opts.on "-h", "--help", "Display this message" do + @runner.help = @opts.help + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/printers/base.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/printers/base.rb new file mode 100644 index 0000000..88bfab0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/printers/base.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +require "yaml" + +module Byebug + module Printers + # + # Base printer + # + class Base + class MissedPath < StandardError; end + class MissedArgument < StandardError; end + + SEPARATOR = "." + + def type + self.class.name.split("::").last.downcase + end + + private + + def locate(path) + result = nil + contents.each_value do |contents| + result = parts(path).reduce(contents) do |r, part| + r&.key?(part) ? r[part] : nil + end + break if result + end + raise MissedPath, "Can't find part path '#{path}'" unless result + + result + end + + def translate(string, args = {}) + # they may contain #{} string interpolation + string.gsub(/\|\w+$/, "").gsub(/([^#]?){([^}]*)}/) do + key = Regexp.last_match[2].to_s + raise MissedArgument, "Missed argument #{key} for '#{string}'" unless args.key?(key.to_sym) + + "#{Regexp.last_match[1]}#{args[key.to_sym]}" + end + end + + def parts(path) + path.split(SEPARATOR) + end + + def contents + @contents ||= contents_files.each_with_object({}) do |filename, hash| + hash[filename] = YAML.load_file(filename) || {} + end + end + + def array_of_args(collection, &_block) + collection_with_index = collection.each.with_index + collection_with_index.each_with_object([]) do |(item, index), array| + args = yield item, index + array << args if args + end + end + + def contents_files + [File.join(__dir__, "texts", "base.yml")] + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/printers/plain.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/printers/plain.rb new file mode 100644 index 0000000..876bdee --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/printers/plain.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require_relative "base" + +module Byebug + module Printers + # + # Plain text printer + # + class Plain < Base + def print(path, args = {}) + message = translate(locate(path), args) + tail = parts(path).include?("confirmations") ? " (y/n) " : "\n" + message << tail + end + + def print_collection(path, collection, &block) + lines = array_of_args(collection, &block).map do |args| + print(path, args) + end + + lines.join + end + + def print_variables(variables, *_unused) + print_collection("variable.variable", variables) do |(key, value), _| + value = value.nil? ? "nil" : value.to_s + if "#{key} = #{value}".size > Setting[:width] + key_size = "#{key} = ".size + value = value[0..Setting[:width] - key_size - 4] + "..." + end + + { key: key, value: value } + end + end + + private + + def contents_files + [File.join(__dir__, "texts", "plain.yml")] + super + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/printers/texts/base.yml b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/printers/texts/base.yml new file mode 100644 index 0000000..97778e3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/printers/texts/base.yml @@ -0,0 +1,115 @@ +base: + errors: + only_local: "Command is available only in local mode." + +break: + errors: + line: "Line {line} is not a valid breakpoint in file {file}.\n\nValid break points are:\n{valid_breakpoints}" + location: "Invalid breakpoint location" + state: "We are not in a state that has an associated file" + class: "Unknown class {klass}" + far_line: "There are only {lines} lines in file {file}" + source: "No file named {file}" + expression: "Incorrect expression \"{expr}\"; breakpoint disabled" + no_breakpoint: "Invalid breakpoint id. Use \"info breakpoint\" to find out the correct id" + no_breakpoint_delete: "No breakpoint number {pos}" + not_changed: "Incorrect expression \"{expr}\", breakpoint not changed" + confirmations: + delete_all: "Delete all breakpoints?" + messages: + breakpoint_deleted: "Deleted breakpoint {pos}" + +catch: + added: "Catching exception {exception}." + removed: "Catch for exception {exception} removed" + errors: + off: "Off expected. Got {off}" + not_class: "Warning {class} is not known to be a Class" + not_found: "Catch for exception {exception} not found" + confirmations: + delete_all: "Delete all catchpoints? (y or n) " + +condition: + errors: + no_breakpoints: "No breakpoints have been set" + +continue: + errors: + unstopped_line: "Line {line} is not a valid stopping point in file" + +display: + confirmations: + clear_all: "Clear all expressions?" + errors: + undefined: "Display expression {expr} is not defined" + +edit: + errors: + state: "We are not in a state that has an associated file" + file_line: "Invalid file[:line] number specification: {file_line}" + not_readable: "File {file} is not readable." + not_exist: "File {file} does not exist." + +frame: + errors: + too_low: "Can't navigate beyond the oldest frame" + too_high: "Can't navigate beyond the newest frame" + c_frame: "Can't navigate to c-frame" + +info: + errors: + undefined_file: "{file} is not a valid source file" + +pry: + errors: + not_installed: "You need to install pry in order to run this command" + +quit: + confirmations: + really: "Really quit?" + +save: + messages: + done: "Saved to '{path}'" + +set: + errors: + unknown_setting: "Unknown setting :{key}" + must_specify_value: "You must specify a value for setting :{key}" + on_off: "Expecting 'on', 1, true, 'off', 0, false. Got: {arg}." + +show: + errors: + unknown_setting: "Unknown setting :{key}" + +source: + errors: + not_found: "File \"{file}\" not found" + +thread: + errors: + no_thread: "No such thread" + current_thread: "It's the current thread" + wrong_action: "Can't {subcmd} thread {arg}" + already_running: "Already running" + +toggle: + errors: + no_breakpoints: "No breakpoints have been set" + no_display: "No display expressions have been set" + syntax: "\"{toggle}\" must be followed by \"display\", \"breakpoints\" or breakpoint ids" + expression: "Expression \"{expr}\" syntactically incorrect; breakpoint remains disabled." + messages: + toggled: "Breakpoint {bpnum} {endis}abled" + +parse: + errors: + int: + too_low: "\"{cmd}\" argument \"{str}\" needs to be at least {min}" + too_high: "\"{cmd}\" argument \"{str}\" needs to be at most {max}" + not_number: "\"{cmd}\" argument \"{str}\" needs to be a number" + +variable: + errors: + not_module: "Should be Class/Module: {object}" + cant_get_class_vars: "can't get class variables here.\n" diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/printers/texts/plain.yml b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/printers/texts/plain.yml new file mode 100644 index 0000000..9ede0f7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/printers/texts/plain.yml @@ -0,0 +1,33 @@ +break: + created: "Created breakpoint {id} at {file}:{line}" + +display: + result: "{n}: {exp} = {result}" + +eval: + exception: "{text_message}" + result: "{result}" + +frame: + line: "{mark} #{pos} {call} at {file}:{line}" + +method: + methods: "{name}|c" + +restart: + success: "Re exec'ing:\n {cmd}" + +thread: + context: "{status_flag}{debug_flag}{id} {thread} {file_line}" + +trace: + messages: + success: "Tracing global variable \"{var}\"." + on_change: "traced global variable '{name}' has value '{value}'" + undo: "Not tracing global variable \"{var}\" anymore." + errors: + var_is_not_global: "'{name}' is not a global variable." + needs_global_variable: "tracevar needs a global variable name" + +variable: + variable: "{key} = {value}" diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/processors/command_processor.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/processors/command_processor.rb new file mode 100644 index 0000000..9f6a09f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/processors/command_processor.rb @@ -0,0 +1,173 @@ +# frozen_string_literal: true + +require "forwardable" + +require_relative "../helpers/eval" +require_relative "../errors" + +module Byebug + # + # Processes commands in regular mode. + # + # You can override this class to create your own command processor that, for + # example, whitelists only certain commands to be executed. + # + # @see PostMortemProcessor for a example + # + class CommandProcessor + include Helpers::EvalHelper + + attr_accessor :prev_line + attr_reader :context, :interface + + def initialize(context, interface = LocalInterface.new) + @context = context + @interface = interface + + @proceed = false + @prev_line = nil + end + + def printer + @printer ||= Printers::Plain.new + end + + extend Forwardable + + def_delegators :@context, :frame + + def_delegator :printer, :print, :pr + def_delegator :printer, :print_collection, :prc + def_delegator :printer, :print_variables, :prv + + def_delegators :interface, :errmsg, :puts, :confirm + + def_delegators :Byebug, :commands + + # + # Available commands + # + def command_list + @command_list ||= CommandList.new(commands) + end + + def at_line + process_commands + end + + def at_tracing + puts "Tracing: #{context.full_location}" + + run_auto_cmds(2) + end + + def at_breakpoint(brkpt) + number = Byebug.breakpoints.index(brkpt) + 1 + + puts "Stopped by breakpoint #{number} at #{frame.file}:#{frame.line}" + end + + def at_catchpoint(exception) + puts "Catchpoint at #{context.location}: `#{exception}'" + end + + def at_return(return_value) + puts "Return value is: #{safe_inspect(return_value)}" + + process_commands + end + + def at_end + process_commands + end + + # + # Let the execution continue + # + def proceed! + @proceed = true + end + + # + # Handle byebug commands. + # + def process_commands + before_repl + + repl + ensure + after_repl + end + + protected + + # + # Prompt shown before reading a command. + # + def prompt + "(byebug) " + end + + def before_repl + @proceed = false + @prev_line = nil + + run_auto_cmds(1) + interface.autorestore + end + + def after_repl + interface.autosave + end + + # + # Main byebug's REPL + # + def repl + until @proceed + cmd = interface.read_command(prompt) + return if cmd.nil? + + next if cmd == "" + + run_cmd(cmd) + end + end + + private + + def auto_cmds_for(run_level) + command_list.select { |cmd| cmd.always_run >= run_level } + end + + # + # Run permanent commands. + # + def run_auto_cmds(run_level) + safely do + auto_cmds_for(run_level).each { |cmd| cmd.new(self).execute } + end + end + + # + # Executes the received input + # + # Instantiates a command matching the input and runs it. If a matching + # command is not found, it evaluates the unknown input. + # + def run_cmd(input) + safely do + command = command_list.match(input) + return command.new(self, input).execute if command + + puts safe_inspect(multiple_thread_eval(input)) + end + end + + def safely + yield + rescue StandardError => e + errmsg(e.message) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/processors/control_processor.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/processors/control_processor.rb new file mode 100644 index 0000000..61ee529 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/processors/control_processor.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +require_relative "command_processor" + +module Byebug + # + # Processes commands when there's not program running + # + class ControlProcessor < CommandProcessor + # + # Available commands + # + def commands + super.select(&:allow_in_control) + end + + # + # Prompt shown before reading a command. + # + def prompt + "(byebug:ctrl) " + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/processors/post_mortem_processor.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/processors/post_mortem_processor.rb new file mode 100644 index 0000000..8dfe8d8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/processors/post_mortem_processor.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require_relative "command_processor" + +module Byebug + # + # Processes commands in post_mortem mode + # + class PostMortemProcessor < CommandProcessor + def commands + super.select(&:allow_in_post_mortem) + end + + def prompt + "(byebug:post_mortem) " + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/processors/script_processor.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/processors/script_processor.rb new file mode 100644 index 0000000..49d4570 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/processors/script_processor.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require_relative "command_processor" + +module Byebug + # + # Processes commands from a file + # + class ScriptProcessor < CommandProcessor + # + # Available commands + # + def commands + super.select(&:allow_in_control) + end + + def repl + while (input = interface.read_command(prompt)) + safely do + command = command_list.match(input) + raise CommandNotFound.new(input) unless command + + command.new(self, input).execute + end + end + end + + def after_repl + super + + interface.close + end + + # + # Prompt shown before reading a command. + # + def prompt + "(byebug:ctrl) " + end + + private + + def without_exceptions + yield + rescue StandardError + nil + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/remote.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/remote.rb new file mode 100644 index 0000000..fa97d78 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/remote.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +require "socket" +require_relative "processors/control_processor" +require_relative "remote/server" +require_relative "remote/client" + +# +# Remote debugging functionality. +# +module Byebug + # Port number used for remote debugging + PORT = 8989 unless defined?(PORT) + + class << self + # If in remote mode, wait for the remote connection + attr_accessor :wait_connection + + # The actual port that the server is started at + def actual_port + server.actual_port + end + + # The actual port that the control server is started at + def actual_control_port + control.actual_port + end + + # + # Interrupts the current thread + # + def interrupt + current_context.interrupt + end + + # + # Starts the remote server main thread + # + def start_server(host = nil, port = PORT) + start_control(host, port.zero? ? 0 : port + 1) + + server.start(host, port) + end + + # + # Starts the remote server control thread + # + def start_control(host = nil, port = PORT + 1) + control.start(host, port) + end + + # + # Connects to the remote byebug + # + def start_client(host = "localhost", port = PORT) + client.start(host, port) + end + + def parse_host_and_port(host_port_spec) + location = host_port_spec.split(":") + location[1] ? [location[0], location[1].to_i] : ["localhost", location[0]] + end + + private + + def client + @client ||= Remote::Client.new(Context.interface) + end + + def server + @server ||= Remote::Server.new(wait_connection: wait_connection) do |s| + Context.interface = RemoteInterface.new(s) + end + end + + def control + @control ||= Remote::Server.new(wait_connection: false) do |s| + context = Byebug.current_context + interface = RemoteInterface.new(s) + + ControlProcessor.new(context, interface).process_commands + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/remote/client.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/remote/client.rb new file mode 100644 index 0000000..0c762f0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/remote/client.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require "socket" + +module Byebug + module Remote + # + # Client for remote debugging + # + class Client + attr_reader :interface, :socket + + def initialize(interface) + @interface = interface + @socket = nil + end + + # + # Connects to the remote byebug + # + def start(host = "localhost", port = PORT) + connect_at(host, port) + + while (line = socket.gets) + case line + when /^PROMPT (.*)$/ + input = interface.read_command(Regexp.last_match[1]) + break unless input + + socket.puts input + when /^CONFIRM (.*)$/ + input = interface.readline(Regexp.last_match[1]) + break unless input + + socket.puts input + else + interface.puts line + end + end + + socket.close + end + + def started? + !socket.nil? + end + + private + + def connect_at(host, port) + interface.puts "Connecting to byebug server at #{host}:#{port}..." + @socket = TCPSocket.new(host, port) + interface.puts "Connected." + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/remote/server.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/remote/server.rb new file mode 100644 index 0000000..e027a5d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/remote/server.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require "socket" + +module Byebug + module Remote + # + # Server for remote debugging + # + class Server + attr_reader :actual_port, :wait_connection + + def initialize(wait_connection:, &block) + @thread = nil + @wait_connection = wait_connection + @main_loop = block + end + + # + # Start the remote debugging server + # + def start(host, port) + return if @thread + + if wait_connection + mutex = Mutex.new + proceed = ConditionVariable.new + end + + server = TCPServer.new(host, port) + @actual_port = server.addr[1] + + yield if block_given? + + @thread = DebugThread.new do + while (session = server.accept) + @main_loop.call(session) + + mutex.synchronize { proceed.signal } if wait_connection + end + end + + mutex.synchronize { proceed.wait(mutex) } if wait_connection + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/runner.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/runner.rb new file mode 100644 index 0000000..27957f0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/runner.rb @@ -0,0 +1,198 @@ +# frozen_string_literal: true + +require "optparse" +require "English" +require_relative "core" +require_relative "version" +require_relative "helpers/bin" +require_relative "helpers/parse" +require_relative "helpers/string" +require_relative "option_setter" +require_relative "processors/control_processor" + +module Byebug + # + # Responsible for starting the debugger when started from the command line. + # + class Runner + include Helpers::BinHelper + include Helpers::ParseHelper + include Helpers::StringHelper + + # + # Special working modes that don't actually start the debugger. + # + attr_reader :help, :version, :remote + + # + # Signals that we should exit after the debugged program is finished. + # + attr_accessor :quit + + # + # Signals that we should stop before program starts + # + attr_accessor :stop + + # + # Signals that we should run rc scripts before program starts + # + attr_writer :init_script + + # + # @param stop [Boolean] Whether the runner should stop right before + # starting the program. + # + # @param quit [Boolean] Whether the runner should quit right after + # finishing the program. + # + def initialize(stop = true, quit = true) + @stop = stop + @quit = quit + end + + def help=(text) + @help ||= text + + interface.puts("#{text}\n") + end + + def version=(number) + @version ||= number + + interface.puts prettify <<-VERSION + Running byebug #{number} + VERSION + end + + def remote=(host_and_port) + @remote ||= Byebug.parse_host_and_port(host_and_port) + + Byebug.start_client(*@remote) + end + + def init_script + defined?(@init_script) ? @init_script : true + end + + # + # Usage banner. + # + def banner + prettify <<-BANNER + byebug #{Byebug::VERSION} + + Usage: byebug [options] -- + BANNER + end + + # + # Starts byebug to debug a program. + # + def run + Byebug.mode = :standalone + + option_parser.order!($ARGV) + return if non_script_option? || error_in_script? + + $PROGRAM_NAME = program + + Byebug.run_init_script if init_script + + loop do + debug_program + + break if quit + + ControlProcessor.new(nil, interface).process_commands + end + end + + def interface + @interface ||= Context.interface + end + + # + # Processes options passed from the command line. + # + def option_parser + @option_parser ||= OptionParser.new(banner, 25) do |opts| + opts.banner = banner + + OptionSetter.new(self, opts).setup + end + end + + def program + @program ||= begin + candidate = which($ARGV.shift) + + if [which("ruby"), RbConfig.ruby].include?(candidate) + which($ARGV.shift) + else + candidate + end + end + end + + # + # An option that doesn't need a script specified was given + # + def non_script_option? + version || help || remote + end + + # + # There is an error with the specified script + # + def error_in_script? + no_script? || non_existing_script? || invalid_script? + end + + # + # No script to debug specified + # + def no_script? + return false unless $ARGV.empty? + + print_error("You must specify a program to debug") + true + end + + # + # Extracts debugged program from command line args. + # + def non_existing_script? + return false if program + + print_error("The script doesn't exist") + true + end + + # + # Checks the debugged script has correct syntax + # + def invalid_script? + return false if syntax_valid?(File.read(program)) + + print_error("The script has incorrect syntax") + true + end + + # + # Debugs a script only if syntax checks okay. + # + def debug_program + error = Byebug.debug_load(program, stop) + puts "#{error}\n#{error.backtrace}" if error + end + + # + # Prints an error message and a help string + # + def print_error(msg) + interface.errmsg(msg) + interface.puts(option_parser.help) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/setting.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/setting.rb new file mode 100644 index 0000000..ace0ccd --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/setting.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require_relative "helpers/string" + +module Byebug + # + # Parent class for all byebug settings. + # + class Setting + attr_accessor :value + + DEFAULT = false + + def initialize + @value = self.class::DEFAULT + end + + def boolean? + [true, false].include?(value) + end + + def integer? + Integer(value) ? true : false + rescue ArgumentError + false + end + + def help + prettify(banner) + end + + def to_sym + name = self.class.name.gsub(/^Byebug::/, "").gsub(/Setting$/, "") + name.gsub(/(.)([A-Z])/, '\1_\2').downcase.to_sym + end + + def to_s + "#{to_sym} is #{value ? 'on' : 'off'}\n" + end + + class << self + def settings + @settings ||= {} + end + + def [](name) + settings[name].value + end + + def []=(name, value) + settings[name].value = value + end + + def find(shortcut) + abbr = /^no/.match?(shortcut) ? shortcut[2..-1] : shortcut + matches = settings.select do |key, value| + key =~ (value.boolean? ? /#{abbr}/ : /#{shortcut}/) + end + matches.size == 1 ? matches.values.first : nil + end + + # + # @todo DRY this up. Very similar code exists in the CommandList class + # + def help_all + output = " List of supported settings:\n\n" + width = settings.keys.max_by(&:size).size + settings.each_value do |sett| + output += format( + " %-#{width}s -- %s\n", + name: sett.to_sym, + description: sett.banner + ) + end + output + "\n" + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/autoirb.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/autoirb.rb new file mode 100644 index 0000000..72238e3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/autoirb.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require_relative "../setting" +require_relative "../commands/irb" + +module Byebug + # + # Setting for automatically invoking IRB on every stop. + # + class AutoirbSetting < Setting + DEFAULT = 0 + + def initialize + IrbCommand.always_run = DEFAULT + end + + def banner + "Invoke IRB on every stop" + end + + def value=(val) + IrbCommand.always_run = val ? 1 : 0 + end + + def value + IrbCommand.always_run == 1 + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/autolist.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/autolist.rb new file mode 100644 index 0000000..df64503 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/autolist.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require_relative "../setting" +require_relative "../commands/list" + +module Byebug + # + # Setting for automatically listing source code on every stop. + # + class AutolistSetting < Setting + DEFAULT = 1 + + def initialize + ListCommand.always_run = DEFAULT + end + + def banner + "Invoke list command on every stop" + end + + def value=(val) + ListCommand.always_run = val ? 1 : 0 + end + + def value + ListCommand.always_run == 1 + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/autopry.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/autopry.rb new file mode 100644 index 0000000..5ddf996 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/autopry.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require_relative "../setting" +require_relative "../commands/pry" + +module Byebug + # + # Setting for automatically invoking Pry on every stop. + # + class AutoprySetting < Setting + DEFAULT = 0 + + def initialize + PryCommand.always_run = DEFAULT + end + + def banner + "Invoke Pry on every stop" + end + + def value=(val) + PryCommand.always_run = val ? 1 : 0 + end + + def value + PryCommand.always_run == 1 + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/autosave.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/autosave.rb new file mode 100644 index 0000000..8c84c17 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/autosave.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require_relative "../setting" + +module Byebug + # + # Setting for automatically saving previously entered commands to history + # when exiting the debugger. + # + class AutosaveSetting < Setting + DEFAULT = true + + def banner + "Automatically save command history record on exit" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/basename.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/basename.rb new file mode 100644 index 0000000..1e07322 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/basename.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require_relative "../setting" + +module Byebug + # + # Command to display short paths in file names. + # + # For example, when displaying source code information. + # + class BasenameSetting < Setting + def banner + ": information after every stop uses short paths" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/callstyle.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/callstyle.rb new file mode 100644 index 0000000..5424d70 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/callstyle.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require_relative "../setting" + +module Byebug + # + # Setting to customize the verbosity level for stack frames. + # + class CallstyleSetting < Setting + DEFAULT = "long" + + def banner + "Set how you want method call parameters to be displayed" + end + + def to_s + "Frame display callstyle is '#{value}'" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/fullpath.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/fullpath.rb new file mode 100644 index 0000000..d9ebbd8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/fullpath.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require_relative "../setting" + +module Byebug + # + # Setting to display full paths in backtraces. + # + class FullpathSetting < Setting + DEFAULT = true + + def banner + "Display full file names in backtraces" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/histfile.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/histfile.rb new file mode 100644 index 0000000..2e69952 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/histfile.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require_relative "../setting" + +module Byebug + # + # Setting to customize the file where byebug's history is saved. + # + class HistfileSetting < Setting + DEFAULT = File.expand_path(".byebug_history") + + def banner + "File where cmd history is saved to. Default: ./.byebug_history" + end + + def to_s + "The command history file is #{value}\n" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/histsize.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/histsize.rb new file mode 100644 index 0000000..d29c1a6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/histsize.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require_relative "../setting" + +module Byebug + # + # Setting to customize the number of byebug commands to be saved in history. + # + class HistsizeSetting < Setting + DEFAULT = 256 + + def banner + "Maximum number of commands that can be stored in byebug history" + end + + def to_s + "Maximum size of byebug's command history is #{value}" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/linetrace.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/linetrace.rb new file mode 100644 index 0000000..421960e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/linetrace.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require_relative "../setting" + +module Byebug + # + # Setting to enable/disable linetracing. + # + class LinetraceSetting < Setting + def banner + "Enable line execution tracing" + end + + def value=(val) + Byebug.tracing = val + end + + def value + Byebug.tracing? + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/listsize.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/listsize.rb new file mode 100644 index 0000000..e89cb9b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/listsize.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require_relative "../setting" + +module Byebug + # + # Setting to customize the number of source code lines to be displayed every + # time the "list" command is invoked. + # + class ListsizeSetting < Setting + DEFAULT = 10 + + def banner + "Set number of source lines to list by default" + end + + def to_s + "Number of source lines to list is #{value}\n" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/post_mortem.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/post_mortem.rb new file mode 100644 index 0000000..2fc6855 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/post_mortem.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require_relative "../setting" + +module Byebug + # + # Setting to enable/disable post_mortem mode, i.e., a debugger prompt after + # program termination by unhandled exception. + # + class PostMortemSetting < Setting + def initialize + Byebug.post_mortem = DEFAULT + end + + def banner + "Enable/disable post-mortem mode" + end + + def value=(val) + Byebug.post_mortem = val + end + + def value + Byebug.post_mortem? + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/savefile.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/savefile.rb new file mode 100644 index 0000000..8083d8a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/savefile.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require_relative "../setting" + +module Byebug + # + # Setting to customize the file where byebug's history is saved. + # + class SavefileSetting < Setting + DEFAULT = File.expand_path("#{ENV['HOME'] || '.'}/.byebug_save") + + def banner + "File where settings are saved to. Default: ~/.byebug_save" + end + + def to_s + "The command history file is #{value}\n" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/stack_on_error.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/stack_on_error.rb new file mode 100644 index 0000000..4923ea0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/stack_on_error.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require_relative "../setting" + +module Byebug + # + # Setting to enable/disable the display of backtraces when evaluations raise + # errors. + # + class StackOnErrorSetting < Setting + def banner + "Display stack trace when `eval` raises an exception" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/width.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/width.rb new file mode 100644 index 0000000..1622ab1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/settings/width.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require_relative "../setting" + +module Byebug + # + # Setting to customize the maximum width of byebug's output. + # + class WidthSetting < Setting + DEFAULT = 160 + + def banner + "Number of characters per line in byebug's output" + end + + def to_s + "Maximum width of byebug's output is #{value}" + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/source_file_formatter.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/source_file_formatter.rb new file mode 100644 index 0000000..845e5ef --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/source_file_formatter.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +require_relative "helpers/file" +require_relative "setting" + +module Byebug + # + # Formats specific line ranges in a source file + # + class SourceFileFormatter + include Helpers::FileHelper + + attr_reader :file, :annotator + + def initialize(file, annotator) + @file = file + @annotator = annotator + end + + def lines(min, max) + File.foreach(file).with_index.map do |line, lineno| + next unless (min..max).cover?(lineno + 1) + + format( + "%s %#{max.to_s.size}d: %s", + annotation: annotator.call(lineno + 1), + lineno: lineno + 1, + source: line + ) + end + end + + def lines_around(center) + lines(*range_around(center)) + end + + def range_around(center) + range_from(center - size / 2) + end + + def range_from(min) + first = amend_initial(min) + + [first, first + size - 1] + end + + def amend_initial(line) + amend(line, max_initial_line) + end + + def amend_final(line) + amend(line, max_line) + end + + def max_initial_line + max_line - size + 1 + end + + def max_line + @max_line ||= n_lines(file) + end + + def size + [Setting[:listsize], max_line].min + end + + def amend(line, ceiling) + [ceiling, [1, line].max].min + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/subcommands.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/subcommands.rb new file mode 100644 index 0000000..714859e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/subcommands.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require "forwardable" + +require_relative "helpers/reflection" +require_relative "command_list" + +module Byebug + # + # Subcommand additions. + # + module Subcommands + def self.included(command) + command.extend(ClassMethods) + end + + extend Forwardable + def_delegators "self.class", :subcommand_list + + # + # Delegates to subcommands or prints help if no subcommand specified. + # + def execute + subcmd_name = @match[1] + return puts(help) unless subcmd_name + + subcmd = subcommand_list.match(subcmd_name) + raise CommandNotFound.new(subcmd_name, self.class) unless subcmd + + subcmd.new(processor, arguments).execute + end + + # + # Class methods added to subcommands + # + module ClassMethods + include Helpers::ReflectionHelper + + # + # Default help text for a command with subcommands + # + def help + super + subcommand_list.to_s + end + + # + # Command's subcommands. + # + def subcommand_list + @subcommand_list ||= CommandList.new(commands) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/version.rb b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/version.rb new file mode 100644 index 0000000..9078ae6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/byebug-11.1.3/lib/byebug/version.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# +# Reopen main module to define the library version +# +module Byebug + VERSION = "11.1.3" +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/MIT-LICENSE b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/MIT-LICENSE new file mode 100644 index 0000000..d8d009d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/MIT-LICENSE @@ -0,0 +1,22 @@ +Copyright (C) 2005-2012 Kornelius Kalnbach (@murphy_karasu) + +http://coderay.rubychan.de/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/README_INDEX.rdoc b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/README_INDEX.rdoc new file mode 100644 index 0000000..7332653 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/README_INDEX.rdoc @@ -0,0 +1,123 @@ += CodeRay + +Tired of blue'n'gray? Try the original version of this documentation on +coderay.rubychan.de[http://coderay.rubychan.de/doc/] :-) + +== About + +CodeRay is a Ruby library for syntax highlighting. + +You put your code in, and you get it back colored; Keywords, strings, +floats, comments - all in different colors. And with line numbers. + +*Syntax* *Highlighting*... +* makes code easier to read and maintain +* lets you detect syntax errors faster +* helps you to understand the syntax of a language +* looks nice +* is what everybody wants to have on their website +* solves all your problems and makes the girls run after you + + +== Installation + + % gem install coderay + + +=== Dependencies + +CodeRay needs Ruby 1.8.7+ or 1.9.2+. It also runs on Rubinius and JRuby. + + +== Example Usage + + require 'coderay' + + html = CodeRay.scan("puts 'Hello, world!'", :ruby).div(:line_numbers => :table) + + +== Documentation + +See CodeRay. + + +== Credits + +=== Special Thanks to + +* licenser (Heinz N. Gies) for ending my QBasic career, inventing the Coder + project and the input/output plugin system. + CodeRay would not exist without him. +* bovi (Daniel Bovensiepen) for helping me out on various occasions. + +=== Thanks to + +* Caleb Clausen for writing RubyLexer (see + http://rubyforge.org/projects/rubylexer) and lots of very interesting mail + traffic +* birkenfeld (Georg Brandl) and mitsuhiku (Arnim Ronacher) for PyKleur, now pygments. + You guys rock! +* Jamis Buck for writing Syntax (see http://rubyforge.org/projects/syntax) + I got some useful ideas from it. +* Doug Kearns and everyone else who worked on ruby.vim - it not only helped me + coding CodeRay, but also gave me a wonderful target to reach for the Ruby + scanner. +* everyone who uses CodeBB on http://www.rubyforen.de and http://www.python-forum.de +* iGEL, magichisoka, manveru, WoNáDo and everyone I forgot from rubyforen.de +* Dethix from ruby-mine.de +* zickzackw +* Dookie (who is no longer with us...) and Leonidas from http://www.python-forum.de +* Andreas Schwarz for finding out that CaseIgnoringWordList was not case + ignoring! Such things really make you write tests. +* closure for the first version of the Scheme scanner. +* Stefan Walk for the first version of the JavaScript and PHP scanners. +* Josh Goebel for another version of the JavaScript scanner, a SQL and a Diff scanner. +* Jonathan Younger for pointing out the licence confusion caused by wrong LICENSE file. +* Jeremy Hinegardner for finding the shebang-on-empty-file bug in FileType. +* Charles Oliver Nutter and Yehuda Katz for helping me benchmark CodeRay on JRuby. +* Andreas Neuhaus for pointing out a markup bug in coderay/for_redcloth. +* 0xf30fc7 for the FileType patch concerning Delphi file extensions. +* The folks at redmine.org - thank you for using and fixing CodeRay! +* Keith Pitt for his SQL scanners +* Rob Aldred for the terminal encoder +* Trans for pointing out $DEBUG dependencies +* Flameeyes for finding that Term::ANSIColor was obsolete +* matz and all Ruby gods and gurus +* The inventors of: the computer, the internet, the true color display, HTML & + CSS, VIM, Ruby, pizza, microwaves, guitars, scouting, programming, anime, + manga, coke and green ice tea. + +Where would we be without all those people? + +=== Created using + +* Ruby[http://ruby-lang.org/] +* Chihiro (my Sony VAIO laptop); Henrietta (my old MacBook); + Triella, born Rico (my new MacBook); as well as + Seras and Hikari (my PCs) +* RDE[http://homepage2.nifty.com/sakazuki/rde_e.html], + VIM[http://vim.org] and TextMate[http://macromates.com] +* Subversion[http://subversion.tigris.org/] +* Redmine[http://redmine.org/] +* Firefox[http://www.mozilla.org/products/firefox/], + Firebug[http://getfirebug.com/], Safari[http://www.apple.com/safari/], and + Thunderbird[http://www.mozilla.org/products/thunderbird/] +* RubyGems[http://docs.rubygems.org/] and Rake[http://rake.rubyforge.org/] +* TortoiseSVN[http://tortoisesvn.tigris.org/] using Apache via + XAMPP[http://www.apachefriends.org/en/xampp.html] +* RDoc (though I'm quite unsatisfied with it) +* Microsoft Windows (yes, I confess!) and MacOS X +* GNUWin32, MinGW and some other tools to make the shell under windows a bit + less useless +* Term::ANSIColor[http://term-ansicolor.rubyforge.org/] +* PLEAC[http://pleac.sourceforge.net/] code examples +* Github +* Travis CI (http://travis-ci.org/rubychan/github) + +=== Free + +* As you can see, CodeRay was created under heavy use of *free* software. +* So CodeRay is also *free*. +* If you use CodeRay to create software, think about making this software + *free*, too. +* Thanks :) diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/bin/coderay b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/bin/coderay new file mode 100755 index 0000000..130a50b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/bin/coderay @@ -0,0 +1,215 @@ +#!/usr/bin/env ruby +require 'coderay' + +$options, args = ARGV.partition { |arg| arg[/^-[hv]$|--\w+/] } +subcommand = args.first if /^\w/ === args.first +subcommand = nil if subcommand && File.exist?(subcommand) +args.delete subcommand + +def option? *options + !($options & options).empty? +end + +def tty? + $stdout.tty? || option?('--tty') +end + +def version + puts <<-USAGE +CodeRay #{CodeRay::VERSION} + USAGE +end + +def help + puts <<-HELP +This is CodeRay #{CodeRay::VERSION}, a syntax highlighting tool for selected languages. + +usage: + coderay [-language] [input] [-format] [output] + +defaults: + language detect from input file name or shebang; fall back to plain text + input STDIN + format detect from output file name or use terminal; fall back to HTML + output STDOUT + +common: + coderay file.rb # highlight file to terminal + coderay file.rb -page > file.html # highlight file to HTML page + coderay file.rb -div > file.html # highlight file to HTML snippet + +configure output: + coderay file.py output.json # output tokens as JSON + coderay file.py -loc # count lines of code in Python file + +configure input: + coderay -python file # specify the input language + coderay -ruby # take input from STDIN + +more: + coderay stylesheet [style] # print CSS stylesheet + HELP +end + +def commands + puts <<-COMMANDS + general: + highlight code highlighting (default command, optional) + stylesheet print the CSS stylesheet with the given name (aliases: style, css) + + about: + list [of] list all available plugins (or just the scanners|encoders|styles|filetypes) + commands print this list + help show some help + version print CodeRay version + COMMANDS +end + +def print_list_of plugin_host + plugins = plugin_host.all_plugins.map do |plugin| + info = " #{plugin.plugin_id}: #{plugin.title}" + + aliases = (plugin.aliases - [:default]).map { |key| "-#{key}" }.sort_by { |key| key.size } + if plugin.respond_to?(:file_extension) || !aliases.empty? + additional_info = [] + additional_info << aliases.join(', ') unless aliases.empty? + info << " (#{additional_info.join('; ')})" + end + + info << ' <-- default' if plugin.aliases.include? :default + + info + end + puts plugins.sort +end + +if option? '-v', '--version' + version +end + +if option? '-h', '--help' + help +end + +case subcommand +when 'highlight', nil + if ARGV.empty? + version + help + else + signature = args.map { |arg| arg[/^-/] ? '-' : 'f' }.join + names = args.map { |arg| arg.sub(/^-/, '') } + case signature + when /^$/ + exit + when /^ff?$/ + input_file, output_file, = *names + when /^f-f?$/ + input_file, output_format, output_file, = *names + when /^-ff?$/ + input_lang, input_file, output_file, = *names + when /^-f-f?$/ + input_lang, input_file, output_format, output_file, = *names + when /^--?f?$/ + input_lang, output_format, output_file, = *names + else + $stdout = $stderr + help + puts + puts "Unknown parameter order: #{args.join ' '}, expected: [-language] [input] [-format] [output]" + exit 1 + end + + if input_file + input_lang ||= CodeRay::FileType.fetch input_file, :text, true + end + + if output_file + output_format ||= CodeRay::FileType[output_file] || :plain + else + output_format ||= :terminal + end + + output_format = :page if output_format.to_s == 'html' + + if input_file + input = File.read input_file + else + input = $stdin.read + end + + begin + file = + if output_file + File.open output_file, 'w' + else + $stdout + end + CodeRay.encode(input, input_lang, output_format, :out => file) + file.puts + rescue CodeRay::PluginHost::PluginNotFound => boom + $stdout = $stderr + if boom.message[/CodeRay::(\w+)s could not load plugin :?(.*?): /] + puts "I don't know the #$1 \"#$2\"." + else + puts boom.message + end + # puts "I don't know this plugin: #{boom.message[/Could not load plugin (.*?): /, 1]}." + rescue CodeRay::Scanners::Scanner::ScanError + # this is sometimes raised by pagers; ignore + # FIXME: rescue Errno::EPIPE + ensure + file.close if output_file + end + end +when 'li', 'list' + arg = args.first && args.first.downcase + if [nil, 's', 'sc', 'scanner', 'scanners'].include? arg + puts 'input languages (Scanners):' + print_list_of CodeRay::Scanners + end + + if [nil, 'e', 'en', 'enc', 'encoder', 'encoders'].include? arg + puts 'output formats (Encoders):' + print_list_of CodeRay::Encoders + end + + if [nil, 'st', 'style', 'styles'].include? arg + puts 'CSS themes for HTML output (Styles):' + print_list_of CodeRay::Styles + end + + if [nil, 'f', 'ft', 'file', 'filetype', 'filetypes'].include? arg + puts 'recognized file types:' + + filetypes = Hash.new { |h, k| h[k] = [] } + CodeRay::FileType::TypeFromExt.inject filetypes do |types, (ext, type)| + types[type.to_s] << ".#{ext}" + types + end + CodeRay::FileType::TypeFromName.inject filetypes do |types, (name, type)| + types[type.to_s] << name + types + end + + filetypes.sort.each do |type, exts| + puts " #{type}: #{exts.sort_by { |ext| ext.size }.join(', ')}" + end + end +when 'stylesheet', 'style', 'css' + puts CodeRay::Encoders[:html]::CSS.new(args.first || :default).stylesheet +when 'commands' + commands +when 'help' + help +else + $stdout = $stderr + help + puts + if subcommand[/\A\w+\z/] + puts "Unknown command: #{subcommand}" + else + puts "File not found: #{subcommand}" + end + exit 1 +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay.rb new file mode 100644 index 0000000..c3de20b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay.rb @@ -0,0 +1,284 @@ +# encoding: utf-8 +# Encoding.default_internal = 'UTF-8' + +# = CodeRay Library +# +# CodeRay is a Ruby library for syntax highlighting. +# +# I try to make CodeRay easy to use and intuitive, but at the same time fully +# featured, complete, fast and efficient. +# +# See README. +# +# It consists mainly of +# * the main engine: CodeRay (Scanners::Scanner, Tokens, Encoders::Encoder) +# * the plugin system: PluginHost, Plugin +# * the scanners in CodeRay::Scanners +# * the encoders in CodeRay::Encoders +# * the styles in CodeRay::Styles +# +# Here's a fancy graphic to light up this gray docu: +# +# http://cycnus.de/raindark/coderay/scheme.png +# +# == Documentation +# +# See CodeRay, Encoders, Scanners, Tokens. +# +# == Usage +# +# Remember you need RubyGems to use CodeRay, unless you have it in your load +# path. Run Ruby with -rubygems option if required. +# +# === Highlight Ruby code in a string as html +# +# require 'coderay' +# print CodeRay.scan('puts "Hello, world!"', :ruby).html +# +# # prints something like this: +# puts "Hello, world!" +# +# +# === Highlight C code from a file in a html div +# +# require 'coderay' +# print CodeRay.scan(File.read('ruby.h'), :c).div +# print CodeRay.scan_file('ruby.h').html.div +# +# You can include this div in your page. The used CSS styles can be printed with +# +# % coderay_stylesheet +# +# === Highlight without typing too much +# +# If you are one of the hasty (or lazy, or extremely curious) people, just run this file: +# +# % ruby -rubygems /path/to/coderay/coderay.rb > example.html +# +# and look at the file it created in your browser. +# +# = CodeRay Module +# +# The CodeRay module provides convenience methods for the engine. +# +# * The +lang+ and +format+ arguments select Scanner and Encoder to use. These are +# simply lower-case symbols, like :python or :html. +# * All methods take an optional hash as last parameter, +options+, that is send to +# the Encoder / Scanner. +# * Input and language are always sorted in this order: +code+, +lang+. +# (This is in alphabetical order, if you need a mnemonic ;) +# +# You should be able to highlight everything you want just using these methods; +# so there is no need to dive into CodeRay's deep class hierarchy. +# +# The examples in the demo directory demonstrate common cases using this interface. +# +# = Basic Access Ways +# +# Read this to get a general view what CodeRay provides. +# +# == Scanning +# +# Scanning means analysing an input string, splitting it up into Tokens. +# Each Token knows about what type it is: string, comment, class name, etc. +# +# Each +lang+ (language) has its own Scanner; for example, :ruby code is +# handled by CodeRay::Scanners::Ruby. +# +# CodeRay.scan:: Scan a string in a given language into Tokens. +# This is the most common method to use. +# CodeRay.scan_file:: Scan a file and guess the language using FileType. +# +# The Tokens object you get from these methods can encode itself; see Tokens. +# +# == Encoding +# +# Encoding means compiling Tokens into an output. This can be colored HTML or +# LaTeX, a textual statistic or just the number of non-whitespace tokens. +# +# Each Encoder provides output in a specific +format+, so you select Encoders via +# formats like :html or :statistic. +# +# CodeRay.encode:: Scan and encode a string in a given language. +# CodeRay.encode_tokens:: Encode the given tokens. +# CodeRay.encode_file:: Scan a file, guess the language using FileType and encode it. +# +# == All-in-One Encoding +# +# CodeRay.encode:: Highlight a string with a given input and output format. +# +# == Instanciating +# +# You can use an Encoder instance to highlight multiple inputs. This way, the setup +# for this Encoder must only be done once. +# +# CodeRay.encoder:: Create an Encoder instance with format and options. +# CodeRay.scanner:: Create an Scanner instance for lang, with '' as default code. +# +# To make use of CodeRay.scanner, use CodeRay::Scanner::code=. +# +# The scanning methods provide more flexibility; we recommend to use these. +# +# == Reusing Scanners and Encoders +# +# If you want to re-use scanners and encoders (because that is faster), see +# CodeRay::Duo for the most convenient (and recommended) interface. +module CodeRay + + $CODERAY_DEBUG ||= false + + CODERAY_PATH = File.expand_path('../coderay', __FILE__) + + # Assuming the path is a subpath of lib/coderay/ + def self.coderay_path *path + File.join CODERAY_PATH, *path + end + + autoload :VERSION, 'coderay/version' + + # helpers + autoload :FileType, coderay_path('helpers', 'file_type') + + # Tokens + autoload :Tokens, coderay_path('tokens') + autoload :TokensProxy, coderay_path('tokens_proxy') + autoload :TokenKinds, coderay_path('token_kinds') + + # Plugin system + autoload :PluginHost, coderay_path('helpers', 'plugin_host') + autoload :Plugin, coderay_path('helpers', 'plugin') + + # Plugins + autoload :Scanners, coderay_path('scanners') + autoload :Encoders, coderay_path('encoders') + autoload :Styles, coderay_path('styles') + + # convenience access and reusable Encoder/Scanner pair + autoload :Duo, coderay_path('duo') + + class << self + + # Scans the given +code+ (a String) with the Scanner for +lang+. + # + # This is a simple way to use CodeRay. Example: + # require 'coderay' + # page = CodeRay.scan("puts 'Hello, world!'", :ruby).html + # + # See also demo/demo_simple. + def scan code, lang, options = {}, &block + CodeRay::TokensProxy.new code, lang, options, block + end + + # Scans +filename+ (a path to a code file) with the Scanner for +lang+. + # + # If +lang+ is :auto or omitted, the CodeRay::FileType module is used to + # determine it. If it cannot find out what type it is, it uses + # CodeRay::Scanners::Text. + # + # Calls CodeRay.scan. + # + # Example: + # require 'coderay' + # page = CodeRay.scan_file('some_c_code.c').html + def scan_file filename, lang = :auto, options = {}, &block + lang = CodeRay::FileType.fetch filename, :text, true if lang == :auto + code = File.read filename + scan code, lang, options, &block + end + + # Encode a string. + # + # This scans +code+ with the the Scanner for +lang+ and then + # encodes it with the Encoder for +format+. + # +options+ will be passed to the Encoder. + # + # See CodeRay::Encoder.encode. + def encode code, lang, format, options = {} + encoder(format, options).encode code, lang, options + end + + # Encode pre-scanned Tokens. + # Use this together with CodeRay.scan: + # + # require 'coderay' + # + # # Highlight a short Ruby code example in a HTML span + # tokens = CodeRay.scan '1 + 2', :ruby + # puts CodeRay.encode_tokens(tokens, :span) + # + def encode_tokens tokens, format, options = {} + encoder(format, options).encode_tokens tokens, options + end + + # Encodes +filename+ (a path to a code file) with the Scanner for +lang+. + # + # See CodeRay.scan_file. + # Notice that the second argument is the output +format+, not the input language. + # + # Example: + # require 'coderay' + # page = CodeRay.encode_file 'some_c_code.c', :html + def encode_file filename, format, options = {} + tokens = scan_file filename, :auto, get_scanner_options(options) + encode_tokens tokens, format, options + end + + # Highlight a string into a HTML
. + # + # CSS styles use classes, so you have to include a stylesheet + # in your output. + # + # See encode. + def highlight code, lang, options = { :css => :class }, format = :div + encode code, lang, format, options + end + + # Highlight a file into a HTML
. + # + # CSS styles use classes, so you have to include a stylesheet + # in your output. + # + # See encode. + def highlight_file filename, options = { :css => :class }, format = :div + encode_file filename, format, options + end + + # Finds the Encoder class for +format+ and creates an instance, passing + # +options+ to it. + # + # Example: + # require 'coderay' + # + # stats = CodeRay.encoder(:statistic) + # stats.encode("puts 17 + 4\n", :ruby) + # + # puts '%d out of %d tokens have the kind :integer.' % [ + # stats.type_stats[:integer].count, + # stats.real_token_count + # ] + # #-> 2 out of 4 tokens have the kind :integer. + def encoder format, options = {} + CodeRay::Encoders[format].new options + end + + # Finds the Scanner class for +lang+ and creates an instance, passing + # +options+ to it. + # + # See Scanner.new. + def scanner lang, options = {}, &block + CodeRay::Scanners[lang].new '', options, &block + end + + # Extract the options for the scanner from the +options+ hash. + # + # Returns an empty Hash if :scanner_options is not set. + # + # This is used if a method like CodeRay.encode has to provide options + # for Encoder _and_ scanner. + def get_scanner_options options + options.fetch :scanner_options, {} + end + + end + +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/duo.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/duo.rb new file mode 100644 index 0000000..cb3f8ee --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/duo.rb @@ -0,0 +1,81 @@ +module CodeRay + + # = Duo + # + # A Duo is a convenient way to use CodeRay. You just create a Duo, + # giving it a lang (language of the input code) and a format (desired + # output format), and call Duo#highlight with the code. + # + # Duo makes it easy to re-use both scanner and encoder for a repetitive + # task. It also provides a very easy interface syntax: + # + # require 'coderay' + # CodeRay::Duo[:python, :div].highlight 'import this' + # + # Until you want to do uncommon things with CodeRay, I recommend to use + # this method, since it takes care of everything. + class Duo + + attr_accessor :lang, :format, :options + + # Create a new Duo, holding a lang and a format to highlight code. + # + # simple: + # CodeRay::Duo[:ruby, :html].highlight 'bla 42' + # + # with options: + # CodeRay::Duo[:ruby, :html, :hint => :debug].highlight '????::??' + # + # alternative syntax without options: + # CodeRay::Duo[:ruby => :statistic].encode 'class << self; end' + # + # alternative syntax with options: + # CodeRay::Duo[{ :ruby => :statistic }, :do => :something].encode 'abc' + # + # The options are forwarded to scanner and encoder + # (see CodeRay.get_scanner_options). + def initialize lang = nil, format = nil, options = {} + if format.nil? && lang.is_a?(Hash) && lang.size == 1 + @lang = lang.keys.first + @format = lang[@lang] + else + @lang = lang + @format = format + end + @options = options + end + + class << self + # To allow calls like Duo[:ruby, :html].highlight. + alias [] new + end + + # The scanner of the duo. Only created once. + def scanner + @scanner ||= CodeRay.scanner @lang, CodeRay.get_scanner_options(@options) + end + + # The encoder of the duo. Only created once. + def encoder + @encoder ||= CodeRay.encoder @format, @options + end + + # Tokenize and highlight the code using +scanner+ and +encoder+. + def encode code, options = {} + options = @options.merge options + encoder.encode(code, @lang, options) + end + alias highlight encode + + # Allows to use Duo like a proc object: + # + # CodeRay::Duo[:python => :yaml].call(code) + # + # or, in Ruby 1.9 and later: + # + # CodeRay::Duo[:python => :yaml].(code) + alias call encode + + end + +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders.rb new file mode 100644 index 0000000..6599186 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders.rb @@ -0,0 +1,18 @@ +module CodeRay + + # This module holds the Encoder class and its subclasses. + # For example, the HTML encoder is named CodeRay::Encoders::HTML + # can be found in coderay/encoders/html. + # + # Encoders also provides methods and constants for the register + # mechanism and the [] method that returns the Encoder class + # belonging to the given format. + module Encoders + + extend PluginHost + plugin_path File.dirname(__FILE__), 'encoders' + + autoload :Encoder, CodeRay.coderay_path('encoders', 'encoder') + + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/_map.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/_map.rb new file mode 100644 index 0000000..4cca196 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/_map.rb @@ -0,0 +1,17 @@ +module CodeRay +module Encoders + + map \ + :loc => :lines_of_code, + :plain => :text, + :plaintext => :text, + :remove_comments => :comment_filter, + :stats => :statistic, + :term => :terminal, + :tty => :terminal, + :yml => :yaml + + # No default because Tokens#nonsense should raise NoMethodError. + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/comment_filter.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/comment_filter.rb new file mode 100644 index 0000000..28336b3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/comment_filter.rb @@ -0,0 +1,25 @@ +module CodeRay +module Encoders + + load :token_kind_filter + + # A simple Filter that removes all tokens of the :comment kind. + # + # Alias: +remove_comments+ + # + # Usage: + # CodeRay.scan('print # foo', :ruby).comment_filter.text + # #-> "print " + # + # See also: TokenKindFilter, LinesOfCode + class CommentFilter < TokenKindFilter + + register_for :comment_filter + + DEFAULT_OPTIONS = superclass::DEFAULT_OPTIONS.merge \ + :exclude => [:comment, :docstring] + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/count.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/count.rb new file mode 100644 index 0000000..98a427e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/count.rb @@ -0,0 +1,39 @@ +module CodeRay +module Encoders + + # Returns the number of tokens. + # + # Text and block tokens are counted. + class Count < Encoder + + register_for :count + + protected + + def setup options + super + + @count = 0 + end + + def finish options + output @count + end + + public + + def text_token text, kind + @count += 1 + end + + def begin_group kind + @count += 1 + end + alias end_group begin_group + alias begin_line begin_group + alias end_line begin_group + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/debug.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/debug.rb new file mode 100644 index 0000000..f4db330 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/debug.rb @@ -0,0 +1,49 @@ +module CodeRay +module Encoders + + # = Debug Encoder + # + # Fast encoder producing simple debug output. + # + # It is readable and diff-able and is used for testing. + # + # You cannot fully restore the tokens information from the + # output, because consecutive :space tokens are merged. + # + # See also: Scanners::Debug + class Debug < Encoder + + register_for :debug + + FILE_EXTENSION = 'raydebug' + + def text_token text, kind + if kind == :space + @out << text + else + text = text.gsub('\\', '\\\\\\\\') if text.index('\\') + text = text.gsub(')', '\\\\)') if text.index(')') + @out << "#{kind}(#{text})" + end + end + + def begin_group kind + @out << "#{kind}<" + end + + def end_group kind + @out << '>' + end + + def begin_line kind + @out << "#{kind}[" + end + + def end_line kind + @out << ']' + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/debug_lint.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/debug_lint.rb new file mode 100644 index 0000000..a4eba2c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/debug_lint.rb @@ -0,0 +1,63 @@ +module CodeRay +module Encoders + + load :lint + + # = Debug Lint Encoder + # + # Debug encoder with additional checks for: + # + # - empty tokens + # - incorrect nesting + # + # It will raise an InvalidTokenStream exception when any of the above occurs. + # + # See also: Encoders::Debug + class DebugLint < Debug + + register_for :debug_lint + + def text_token text, kind + raise Lint::EmptyToken, 'empty token for %p' % [kind] if text.empty? + raise Lint::UnknownTokenKind, 'unknown token kind %p (text was %p)' % [kind, text] unless TokenKinds.has_key? kind + super + end + + def begin_group kind + @opened << kind + super + end + + def end_group kind + raise Lint::IncorrectTokenGroupNesting, 'We are inside %s, not %p (end_group)' % [@opened.reverse.map(&:inspect).join(' < '), kind] if @opened.last != kind + @opened.pop + super + end + + def begin_line kind + @opened << kind + super + end + + def end_line kind + raise Lint::IncorrectTokenGroupNesting, 'We are inside %s, not %p (end_line)' % [@opened.reverse.map(&:inspect).join(' < '), kind] if @opened.last != kind + @opened.pop + super + end + + protected + + def setup options + super + @opened = [] + end + + def finish options + raise 'Some tokens still open at end of token stream: %p' % [@opened] unless @opened.empty? + super + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/div.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/div.rb new file mode 100644 index 0000000..efd9435 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/div.rb @@ -0,0 +1,23 @@ +module CodeRay +module Encoders + + load :html + + # Wraps HTML output into a DIV element, using inline styles by default. + # + # See Encoders::HTML for available options. + class Div < HTML + + FILE_EXTENSION = 'div.html' + + register_for :div + + DEFAULT_OPTIONS = HTML::DEFAULT_OPTIONS.merge \ + :css => :style, + :wrap => :div, + :line_numbers => false + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/encoder.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/encoder.rb new file mode 100644 index 0000000..2baeedb --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/encoder.rb @@ -0,0 +1,190 @@ +module CodeRay + module Encoders + + # = Encoder + # + # The Encoder base class. Together with Scanner and + # Tokens, it forms the highlighting triad. + # + # Encoder instances take a Tokens object and do something with it. + # + # The most common Encoder is surely the HTML encoder + # (CodeRay::Encoders::HTML). It highlights the code in a colorful + # html page. + # If you want the highlighted code in a div or a span instead, + # use its subclasses Div and Span. + class Encoder + extend Plugin + plugin_host Encoders + + class << self + + # If FILE_EXTENSION isn't defined, this method returns the + # downcase class name instead. + def const_missing sym + if sym == :FILE_EXTENSION + (defined?(@plugin_id) && @plugin_id || name[/\w+$/].downcase).to_s + else + super + end + end + + # The default file extension for output file of this encoder class. + def file_extension + self::FILE_EXTENSION + end + + end + + # Subclasses are to store their default options in this constant. + DEFAULT_OPTIONS = { } + + # The options you gave the Encoder at creating. + attr_accessor :options, :scanner + + # Creates a new Encoder. + # +options+ is saved and used for all encode operations, as long + # as you don't overwrite it there by passing additional options. + # + # Encoder objects provide three encode methods: + # - encode simply takes a +code+ string and a +lang+ + # - encode_tokens expects a +tokens+ object instead + # + # Each method has an optional +options+ parameter. These are + # added to the options you passed at creation. + def initialize options = {} + @options = self.class::DEFAULT_OPTIONS.merge options + @@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN = false + end + + # Encode a Tokens object. + def encode_tokens tokens, options = {} + options = @options.merge options + @scanner = tokens.scanner if tokens.respond_to? :scanner + setup options + compile tokens, options + finish options + end + + # Encode the given +code+ using the Scanner for +lang+. + def encode code, lang, options = {} + options = @options.merge options + @scanner = Scanners[lang].new code, CodeRay.get_scanner_options(options).update(:tokens => self) + setup options + @scanner.tokenize + finish options + end + + # You can use highlight instead of encode, if that seems + # more clear to you. + alias highlight encode + + # The default file extension for this encoder. + def file_extension + self.class.file_extension + end + + def << token + unless @@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN + warn 'Using old Tokens#<< interface.' + @@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN = true + end + self.token(*token) + end + + # Called with +content+ and +kind+ of the currently scanned token. + # For simple scanners, it's enougth to implement this method. + # + # By default, it calls text_token, begin_group, end_group, begin_line, + # or end_line, depending on the +content+. + def token content, kind + case content + when String + text_token content, kind + when :begin_group + begin_group kind + when :end_group + end_group kind + when :begin_line + begin_line kind + when :end_line + end_line kind + else + raise ArgumentError, 'Unknown token content type: %p, kind = %p' % [content, kind] + end + end + + # Called for each text token ([text, kind]), where text is a String. + def text_token text, kind + @out << text + end + + # Starts a token group with the given +kind+. + def begin_group kind + end + + # Ends a token group with the given +kind+. + def end_group kind + end + + # Starts a new line token group with the given +kind+. + def begin_line kind + end + + # Ends a new line token group with the given +kind+. + def end_line kind + end + + protected + + # Called with merged options before encoding starts. + # Sets @out to an empty string. + # + # See the HTML Encoder for an example of option caching. + def setup options + @out = get_output(options) + end + + def get_output options + options[:out] || ''.dup + end + + # Append data.to_s to the output. Returns the argument. + def output data + @out << data.to_s + data + end + + # Called with merged options after encoding starts. + # The return value is the result of encoding, typically @out. + def finish options + @out + end + + # Do the encoding. + # + # The already created +tokens+ object must be used; it must be a + # Tokens object. + def compile tokens, options = {} + content = nil + for item in tokens + if item.is_a? Array + raise ArgumentError, 'Two-element array tokens are no longer supported.' + end + if content + token content, item + content = nil + else + content = item + end + end + raise 'odd number list for Tokens' if content + end + + alias tokens compile + public :tokens + + end + + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/filter.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/filter.rb new file mode 100644 index 0000000..e7f34d6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/filter.rb @@ -0,0 +1,58 @@ +module CodeRay +module Encoders + + # A Filter encoder has another Tokens instance as output. + # It can be subclass to select, remove, or modify tokens in the stream. + # + # Subclasses of Filter are called "Filters" and can be chained. + # + # == Options + # + # === :tokens + # + # The Tokens object which will receive the output. + # + # Default: Tokens.new + # + # See also: TokenKindFilter + class Filter < Encoder + + register_for :filter + + protected + def setup options + super + + @tokens = options[:tokens] || Tokens.new + end + + def finish options + output @tokens + end + + public + + def text_token text, kind # :nodoc: + @tokens.text_token text, kind + end + + def begin_group kind # :nodoc: + @tokens.begin_group kind + end + + def begin_line kind # :nodoc: + @tokens.begin_line kind + end + + def end_group kind # :nodoc: + @tokens.end_group kind + end + + def end_line kind # :nodoc: + @tokens.end_line kind + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/html.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/html.rb new file mode 100644 index 0000000..1b33e92 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/html.rb @@ -0,0 +1,333 @@ +require 'set' + +module CodeRay +module Encoders + + # = HTML Encoder + # + # This is CodeRay's most important highlighter: + # It provides save, fast XHTML generation and CSS support. + # + # == Usage + # + # require 'coderay' + # puts CodeRay.scan('Some /code/', :ruby).html #-> a HTML page + # puts CodeRay.scan('Some /code/', :ruby).html(:wrap => :span) + # #-> Some /code/ + # puts CodeRay.scan('Some /code/', :ruby).span #-> the same + # + # puts CodeRay.scan('Some code', :ruby).html( + # :wrap => nil, + # :line_numbers => :inline, + # :css => :style + # ) + # + # == Options + # + # === :tab_width + # Convert \t characters to +n+ spaces (a number or false.) + # false will keep tab characters untouched. + # + # Default: 8 + # + # === :css + # How to include the styles; can be :class or :style. + # + # Default: :class + # + # === :wrap + # Wrap in :page, :div, :span or nil. + # + # You can also use Encoders::Div and Encoders::Span. + # + # Default: nil + # + # === :title + # + # The title of the HTML page (works only when :wrap is set to :page.) + # + # Default: 'CodeRay output' + # + # === :break_lines + # + # Split multiline blocks at line breaks. + # Forced to true if :line_numbers option is set to :inline. + # + # Default: false + # + # === :line_numbers + # Include line numbers in :table, :inline, or nil (no line numbers) + # + # Default: nil + # + # === :line_number_anchors + # Adds anchors and links to the line numbers. Can be false (off), true (on), + # or a prefix string that will be prepended to the anchor name. + # + # The prefix must consist only of letters, digits, and underscores. + # + # Default: true, default prefix name: "line" + # + # === :line_number_start + # Where to start with line number counting. + # + # Default: 1 + # + # === :bold_every + # Make every +n+-th number appear bold. + # + # Default: 10 + # + # === :highlight_lines + # + # Highlights certain line numbers. + # Can be any Enumerable, typically just an Array or Range, of numbers. + # + # Bolding is deactivated when :highlight_lines is set. It only makes sense + # in combination with :line_numbers. + # + # Default: nil + # + # === :hint + # Include some information into the output using the title attribute. + # Can be :info (show token kind on mouse-over), :info_long (with full path) + # or :debug (via inspect). + # + # Default: false + class HTML < Encoder + + register_for :html + + FILE_EXTENSION = 'snippet.html' + + DEFAULT_OPTIONS = { + :tab_width => 8, + + :css => :class, + :style => :alpha, + :wrap => nil, + :title => 'CodeRay output', + + :break_lines => false, + + :line_numbers => nil, + :line_number_anchors => 'n', + :line_number_start => 1, + :bold_every => 10, + :highlight_lines => nil, + + :hint => false, + } + + autoload :Output, CodeRay.coderay_path('encoders', 'html', 'output') + autoload :CSS, CodeRay.coderay_path('encoders', 'html', 'css') + autoload :Numbering, CodeRay.coderay_path('encoders', 'html', 'numbering') + + attr_reader :css + + protected + + def self.make_html_escape_hash + { + '&' => '&', + '"' => '"', + '>' => '>', + '<' => '<', + # "\t" => will be set to ' ' * options[:tab_width] during setup + }.tap do |hash| + # Escape ASCII control codes except \x9 == \t and \xA == \n. + (Array(0x00..0x8) + Array(0xB..0x1F)).each { |invalid| hash[invalid.chr] = ' ' } + end + end + + HTML_ESCAPE = make_html_escape_hash + HTML_ESCAPE_PATTERN = /[\t"&><\0-\x8\xB-\x1F]/ + + TOKEN_KIND_TO_INFO = Hash.new do |h, kind| + h[kind] = kind.to_s.gsub(/_/, ' ').gsub(/\b\w/) { $&.capitalize } + end + + TRANSPARENT_TOKEN_KINDS = Set[ + :delimiter, :modifier, :content, :escape, :inline_delimiter, + ] + + # Generate a hint about the given +kinds+ in a +hint+ style. + # + # +hint+ may be :info, :info_long or :debug. + def self.token_path_to_hint hint, kinds + kinds = Array kinds + title = + case hint + when :info + kinds = kinds[1..-1] if TRANSPARENT_TOKEN_KINDS.include? kinds.first + TOKEN_KIND_TO_INFO[kinds.first] + when :info_long + kinds.reverse.map { |kind| TOKEN_KIND_TO_INFO[kind] }.join('/') + when :debug + kinds.inspect + end + title ? " title=\"#{title}\"" : '' + end + + def setup options + super + + check_options! options + + if options[:wrap] || options[:line_numbers] + @real_out = @out + @out = ''.dup + end + + @break_lines = (options[:break_lines] == true) + + @HTML_ESCAPE = HTML_ESCAPE.merge("\t" => options[:tab_width] ? ' ' * options[:tab_width] : "\t") + + @opened = [] + @last_opened = nil + @css = CSS.new options[:style] + + @span_for_kinds = make_span_for_kinds(options[:css], options[:hint]) + + @set_last_opened = options[:hint] || options[:css] == :style + end + + def finish options + unless @opened.empty? + @out << '' while @opened.pop + @last_opened = nil + end + + if @out.respond_to? :to_str + @out.extend Output + @out.css = @css + if options[:line_numbers] + Numbering.number! @out, options[:line_numbers], options + end + @out.wrap! options[:wrap] + @out.apply_title! options[:title] + end + + if defined?(@real_out) && @real_out + @real_out << @out + @out = @real_out + end + + super + end + + public + + def text_token text, kind + style = @span_for_kinds[@last_opened ? [kind, *@opened] : kind] + + text = text.gsub(/#{HTML_ESCAPE_PATTERN}/o) { |m| @HTML_ESCAPE[m] } if text =~ /#{HTML_ESCAPE_PATTERN}/o + text = break_lines(text, style) if @break_lines && (style || @opened.size > 0) && text.index("\n") + + if style + @out << style << text << '' + else + @out << text + end + end + + # token groups, eg. strings + def begin_group kind + @out << (@span_for_kinds[@last_opened ? [kind, *@opened] : kind] || '') + @opened << kind + @last_opened = kind if @set_last_opened + end + + def end_group kind + check_group_nesting 'token group', kind if $CODERAY_DEBUG + close_span + end + + # whole lines to be highlighted, eg. a deleted line in a diff + def begin_line kind + if style = @span_for_kinds[@last_opened ? [kind, *@opened] : kind] + if style['class="'] + @out << style.sub('class="', 'class="line ') + else + @out << style.sub('>', ' class="line">') + end + else + @out << '' + end + @opened << kind + @last_opened = kind if @options[:css] == :style + end + + def end_line kind + check_group_nesting 'line', kind if $CODERAY_DEBUG + close_span + end + + protected + + def check_options! options + unless [false, nil, :debug, :info, :info_long].include? options[:hint] + raise ArgumentError, "Unknown value %p for :hint; expected :info, :info_long, :debug, false, or nil." % [options[:hint]] + end + + unless [:class, :style].include? options[:css] + raise ArgumentError, 'Unknown value %p for :css.' % [options[:css]] + end + + options[:break_lines] = true if options[:line_numbers] == :inline + end + + def css_class_for_kinds kinds + TokenKinds[kinds.is_a?(Symbol) ? kinds : kinds.first] + end + + def style_for_kinds kinds + css_classes = kinds.is_a?(Array) ? kinds.map { |c| TokenKinds[c] } : [TokenKinds[kinds]] + @css.get_style_for_css_classes css_classes + end + + def make_span_for_kinds method, hint + Hash.new do |h, kinds| + begin + css_class = css_class_for_kinds(kinds) + title = HTML.token_path_to_hint hint, kinds if hint + + if css_class || title + if method == :style + style = style_for_kinds(kinds) + "" + else + "" + end + end + end.tap do |span| + h.clear if h.size >= 100 + h[kinds] = span + end + end + end + + def check_group_nesting name, kind + if @opened.empty? || @opened.last != kind + warn "Malformed token stream: Trying to close a #{name} (%p) that is not open. Open are: %p." % [kind, @opened[1..-1]] + end + end + + def break_lines text, style + reopen = ''.dup + @opened.each_with_index do |kind, index| + reopen << (@span_for_kinds[index > 0 ? [kind, *@opened[0...index]] : kind] || '') + end + text.gsub("\n", "#{'' * @opened.size}#{'' if style}\n#{reopen}#{style}") + end + + def close_span + if @opened.pop + @out << '' + @last_opened = @opened.last if @last_opened + end + end + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/html/css.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/html/css.rb new file mode 100644 index 0000000..164d7f8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/html/css.rb @@ -0,0 +1,65 @@ +module CodeRay +module Encoders + + class HTML + class CSS # :nodoc: + + attr :stylesheet + + def CSS.load_stylesheet style = nil + CodeRay::Styles[style] + end + + def initialize style = :default + @styles = Hash.new + style = CSS.load_stylesheet style + @stylesheet = [ + style::CSS_MAIN_STYLES, + style::TOKEN_COLORS.gsub(/^(?!$)/, '.CodeRay ') + ].join("\n") + parse style::TOKEN_COLORS + end + + def get_style_for_css_classes css_classes + cl = @styles[css_classes.first] + return '' unless cl + style = '' + 1.upto css_classes.size do |offset| + break if style = cl[css_classes[offset .. -1]] + end + # warn 'Style not found: %p' % [styles] if style.empty? + return style + end + + private + + CSS_CLASS_PATTERN = / + ( # $1 = selectors + (?: + (?: \s* \. [-\w]+ )+ + \s* ,? + )+ + ) + \s* \{ \s* + ( [^\}]+ )? # $2 = style + \s* \} \s* + | + ( [^\n]+ ) # $3 = error + /mx + def parse stylesheet + stylesheet.scan CSS_CLASS_PATTERN do |selectors, style, error| + raise "CSS parse error: '#{error.inspect}' not recognized" if error + for selector in selectors.split(',') + classes = selector.scan(/[-\w]+/) + cl = classes.pop + @styles[cl] ||= Hash.new + @styles[cl][classes] = style.to_s.strip.delete(' ').chomp(';') + end + end + end + + end + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/html/numbering.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/html/numbering.rb new file mode 100644 index 0000000..a1b9c04 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/html/numbering.rb @@ -0,0 +1,108 @@ +module CodeRay +module Encoders + + class HTML + + module Numbering # :nodoc: + + def self.number! output, mode = :table, options = {} + return self unless mode + + options = DEFAULT_OPTIONS.merge options + + start = options[:line_number_start] + unless start.is_a? Integer + raise ArgumentError, "Invalid value %p for :line_number_start; Integer expected." % start + end + + anchor_prefix = options[:line_number_anchors] + anchor_prefix = 'line' if anchor_prefix == true + anchor_prefix = anchor_prefix.to_s[/[\w-]+/] if anchor_prefix + anchoring = + if anchor_prefix + proc do |line| + line = line.to_s + anchor = anchor_prefix + line + "#{line}" + end + else + :to_s.to_proc + end + + bold_every = options[:bold_every] + highlight_lines = options[:highlight_lines] + bolding = + if bold_every == false && highlight_lines == nil + anchoring + elsif highlight_lines.is_a? Enumerable + highlight_lines = highlight_lines.to_set + proc do |line| + if highlight_lines.include? line + "#{anchoring[line]}" # highlighted line numbers in bold + else + anchoring[line] + end + end + elsif bold_every.is_a? Integer + raise ArgumentError, ":bolding can't be 0." if bold_every == 0 + proc do |line| + if line % bold_every == 0 + "#{anchoring[line]}" # every bold_every-th number in bold + else + anchoring[line] + end + end + else + raise ArgumentError, 'Invalid value %p for :bolding; false or Integer expected.' % bold_every + end + + if position_of_last_newline = output.rindex(RUBY_VERSION >= '1.9' ? /\n/ : ?\n) + after_last_newline = output[position_of_last_newline + 1 .. -1] + ends_with_newline = after_last_newline[/\A(?:<\/span>)*\z/] + + if ends_with_newline + line_count = output.count("\n") + else + line_count = output.count("\n") + 1 + end + else + line_count = 1 + end + + case mode + when :inline + max_width = (start + line_count).to_s.size + line_number = start + output.gsub!(/^.*$\n?/) do |line| + line_number_text = bolding.call line_number + indent = ' ' * (max_width - line_number.to_s.size) + line_number += 1 + "#{indent}#{line_number_text}#{line}" + end + + when :table + line_numbers = (start ... start + line_count).map(&bolding).join("\n") + line_numbers << "\n" + line_numbers_table_template = Output::TABLE.apply('LINE_NUMBERS', line_numbers) + + output.gsub!(/<\/div>\n/, '
') + output.wrap_in! line_numbers_table_template + output.wrapped_in = :div + + when :list + raise NotImplementedError, 'The :list option is no longer available. Use :table.' + + else + raise ArgumentError, 'Unknown value %p for mode: expected one of %p' % + [mode, [:table, :inline]] + end + + output + end + + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/html/output.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/html/output.rb new file mode 100644 index 0000000..ee87fea --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/html/output.rb @@ -0,0 +1,164 @@ +module CodeRay +module Encoders + + class HTML + + # This module is included in the output String of the HTML Encoder. + # + # It provides methods like wrap, div, page etc. + # + # Remember to use #clone instead of #dup to keep the modules the object was + # extended with. + # + # TODO: Rewrite this without monkey patching. + module Output + + attr_accessor :css + + class << self + + # Raises an exception if an object that doesn't respond to to_str is extended by Output, + # to prevent users from misuse. Use Module#remove_method to disable. + def extended o # :nodoc: + warn "The Output module is intended to extend instances of String, not #{o.class}." unless o.respond_to? :to_str + end + + def make_stylesheet css, in_tag = false # :nodoc: + sheet = css.stylesheet + sheet = <<-'CSS' if in_tag + + CSS + sheet + end + + def page_template_for_css css # :nodoc: + sheet = make_stylesheet css + PAGE.apply 'CSS', sheet + end + + end + + def wrapped_in? element + wrapped_in == element + end + + def wrapped_in + @wrapped_in ||= nil + end + attr_writer :wrapped_in + + def wrap_in! template + Template.wrap! self, template, 'CONTENT' + self + end + + def apply_title! title + self.sub!(/()(<\/title>)/) { $1 + title + $2 } + self + end + + def wrap! element, *args + return self if not element or element == wrapped_in + case element + when :div + raise "Can't wrap %p in %p" % [wrapped_in, element] unless wrapped_in? nil + wrap_in! DIV + when :span + raise "Can't wrap %p in %p" % [wrapped_in, element] unless wrapped_in? nil + wrap_in! SPAN + when :page + wrap! :div if wrapped_in? nil + raise "Can't wrap %p in %p" % [wrapped_in, element] unless wrapped_in? :div + wrap_in! Output.page_template_for_css(@css) + if args.first.is_a?(Hash) && title = args.first[:title] + apply_title! title + end + self + else + raise "Unknown value %p for :wrap" % element + end + @wrapped_in = element + self + end + + def stylesheet in_tag = false + Output.make_stylesheet @css, in_tag + end + +#-- don't include the templates in docu + + class Template < String # :nodoc: + + def self.wrap! str, template, target + target = Regexp.new(Regexp.escape("<%#{target}%>")) + if template =~ target + str[0,0] = $` + str << $' + else + raise "Template target <%%%p%%> not found" % target + end + end + + def apply target, replacement + target = Regexp.new(Regexp.escape("<%#{target}%>")) + if self =~ target + Template.new($` + replacement + $') + else + raise "Template target <%%%p%%> not found" % target + end + end + + end + + SPAN = Template.new '<span class="CodeRay"><%CONTENT%></span>' + + DIV = Template.new <<-DIV +<div class="CodeRay"> + <div class="code"><pre><%CONTENT%></pre></div> +</div> + DIV + + TABLE = Template.new <<-TABLE +<table class="CodeRay"><tr> + <td class="line-numbers"><pre><%LINE_NUMBERS%></pre></td> + <td class="code"><pre><%CONTENT%></pre></td> +</tr></table> + TABLE + + PAGE = Template.new <<-PAGE +<!DOCTYPE html> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <title> + + + + +<%CONTENT%> + + + PAGE + + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/json.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/json.rb new file mode 100644 index 0000000..a9e40dc --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/json.rb @@ -0,0 +1,83 @@ +module CodeRay +module Encoders + + # A simple JSON Encoder. + # + # Example: + # CodeRay.scan('puts "Hello world!"', :ruby).json + # yields + # [ + # {"type"=>"text", "text"=>"puts", "kind"=>"ident"}, + # {"type"=>"text", "text"=>" ", "kind"=>"space"}, + # {"type"=>"block", "action"=>"open", "kind"=>"string"}, + # {"type"=>"text", "text"=>"\"", "kind"=>"delimiter"}, + # {"type"=>"text", "text"=>"Hello world!", "kind"=>"content"}, + # {"type"=>"text", "text"=>"\"", "kind"=>"delimiter"}, + # {"type"=>"block", "action"=>"close", "kind"=>"string"}, + # ] + class JSON < Encoder + + begin + require 'json' + rescue LoadError + begin + require 'rubygems' unless defined? Gem + gem 'json' + require 'json' + rescue LoadError + $stderr.puts "The JSON encoder needs the JSON library.\n" \ + "Please gem install json." + raise + end + end + + register_for :json + FILE_EXTENSION = 'json' + + protected + def setup options + super + + @first = true + @out << '[' + end + + def finish options + @out << ']' + end + + def append data + if @first + @first = false + else + @out << ',' + end + + @out << data.to_json + end + + public + def text_token text, kind + append :type => 'text', :text => text, :kind => kind + end + + def begin_group kind + append :type => 'block', :action => 'open', :kind => kind + end + + def end_group kind + append :type => 'block', :action => 'close', :kind => kind + end + + def begin_line kind + append :type => 'block', :action => 'begin_line', :kind => kind + end + + def end_line kind + append :type => 'block', :action => 'end_line', :kind => kind + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/lines_of_code.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/lines_of_code.rb new file mode 100644 index 0000000..5f8422f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/lines_of_code.rb @@ -0,0 +1,45 @@ +module CodeRay +module Encoders + + # Counts the LoC (Lines of Code). Returns an Integer >= 0. + # + # Alias: +loc+ + # + # Everything that is not comment, markup, doctype/shebang, or an empty line, + # is considered to be code. + # + # For example, + # * HTML files not containing JavaScript have 0 LoC + # * in a Java class without comments, LoC is the number of non-empty lines + # + # A Scanner class should define the token kinds that are not code in the + # KINDS_NOT_LOC constant, which defaults to [:comment, :doctype]. + class LinesOfCode < TokenKindFilter + + register_for :lines_of_code + + NON_EMPTY_LINE = /^\s*\S.*$/ + + protected + + def setup options + if scanner + kinds_not_loc = scanner.class::KINDS_NOT_LOC + else + warn "Tokens have no associated scanner, counting all nonempty lines." if $VERBOSE + kinds_not_loc = CodeRay::Scanners::Scanner::KINDS_NOT_LOC + end + + options[:exclude] = kinds_not_loc + + super options + end + + def finish options + output @tokens.text.scan(NON_EMPTY_LINE).size + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/lint.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/lint.rb new file mode 100644 index 0000000..88c8bd1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/lint.rb @@ -0,0 +1,59 @@ +module CodeRay +module Encoders + + # = Lint Encoder + # + # Checks for: + # + # - empty tokens + # - incorrect nesting + # + # It will raise an InvalidTokenStream exception when any of the above occurs. + # + # See also: Encoders::DebugLint + class Lint < Debug + + register_for :lint + + InvalidTokenStream = Class.new StandardError + EmptyToken = Class.new InvalidTokenStream + UnknownTokenKind = Class.new InvalidTokenStream + IncorrectTokenGroupNesting = Class.new InvalidTokenStream + + def text_token text, kind + raise EmptyToken, 'empty token for %p' % [kind] if text.empty? + raise UnknownTokenKind, 'unknown token kind %p (text was %p)' % [kind, text] unless TokenKinds.has_key? kind + end + + def begin_group kind + @opened << kind + end + + def end_group kind + raise IncorrectTokenGroupNesting, 'We are inside %s, not %p (end_group)' % [@opened.reverse.map(&:inspect).join(' < '), kind] if @opened.last != kind + @opened.pop + end + + def begin_line kind + @opened << kind + end + + def end_line kind + raise IncorrectTokenGroupNesting, 'We are inside %s, not %p (end_line)' % [@opened.reverse.map(&:inspect).join(' < '), kind] if @opened.last != kind + @opened.pop + end + + protected + + def setup options + @opened = [] + end + + def finish options + raise 'Some tokens still open at end of token stream: %p' % [@opened] unless @opened.empty? + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/null.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/null.rb new file mode 100644 index 0000000..73ba47d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/null.rb @@ -0,0 +1,18 @@ +module CodeRay +module Encoders + + # = Null Encoder + # + # Does nothing and returns an empty string. + class Null < Encoder + + register_for :null + + def text_token text, kind + # do nothing + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/page.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/page.rb new file mode 100644 index 0000000..800e73f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/page.rb @@ -0,0 +1,24 @@ +module CodeRay +module Encoders + + load :html + + # Wraps the output into a HTML page, using CSS classes and + # line numbers in the table format by default. + # + # See Encoders::HTML for available options. + class Page < HTML + + FILE_EXTENSION = 'html' + + register_for :page + + DEFAULT_OPTIONS = HTML::DEFAULT_OPTIONS.merge \ + :css => :class, + :wrap => :page, + :line_numbers => :table + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/span.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/span.rb new file mode 100644 index 0000000..da705bd --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/span.rb @@ -0,0 +1,23 @@ +module CodeRay +module Encoders + + load :html + + # Wraps HTML output into a SPAN element, using inline styles by default. + # + # See Encoders::HTML for available options. + class Span < HTML + + FILE_EXTENSION = 'span.html' + + register_for :span + + DEFAULT_OPTIONS = HTML::DEFAULT_OPTIONS.merge \ + :css => :style, + :wrap => :span, + :line_numbers => false + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/statistic.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/statistic.rb new file mode 100644 index 0000000..b2f8b83 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/statistic.rb @@ -0,0 +1,95 @@ +module CodeRay +module Encoders + + # Makes a statistic for the given tokens. + # + # Alias: +stats+ + class Statistic < Encoder + + register_for :statistic + + attr_reader :type_stats, :real_token_count # :nodoc: + + TypeStats = Struct.new :count, :size # :nodoc: + + protected + + def setup options + super + + @type_stats = Hash.new { |h, k| h[k] = TypeStats.new 0, 0 } + @real_token_count = 0 + end + + STATS = <<-STATS # :nodoc: + +Code Statistics + +Tokens %8d + Non-Whitespace %8d +Bytes Total %8d + +Token Types (%d): + type count ratio size (average) +------------------------------------------------------------- +%s + STATS + + TOKEN_TYPES_ROW = <<-TKR # :nodoc: + %-20s %8d %6.2f %% %5.1f + TKR + + def finish options + all = @type_stats['TOTAL'] + all_count, all_size = all.count, all.size + @type_stats.each do |type, stat| + stat.size /= stat.count.to_f + end + types_stats = @type_stats.sort_by { |k, v| [-v.count, k.to_s] }.map do |k, v| + TOKEN_TYPES_ROW % [k, v.count, 100.0 * v.count / all_count, v.size] + end.join + @out << STATS % [ + all_count, @real_token_count, all_size, + @type_stats.delete_if { |k, v| k.is_a? String }.size, + types_stats + ] + + super + end + + public + + def text_token text, kind + @real_token_count += 1 unless kind == :space + @type_stats[kind].count += 1 + @type_stats[kind].size += text.size + @type_stats['TOTAL'].size += text.size + @type_stats['TOTAL'].count += 1 + end + + def begin_group kind + block_token ':begin_group', kind + end + + def end_group kind + block_token ':end_group', kind + end + + def begin_line kind + block_token ':begin_line', kind + end + + def end_line kind + block_token ':end_line', kind + end + + def block_token action, kind + @type_stats['TOTAL'].count += 1 + @type_stats[action].count += 1 + @type_stats[kind].count += 1 + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/terminal.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/terminal.rb new file mode 100644 index 0000000..c7ae014 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/terminal.rb @@ -0,0 +1,195 @@ +module CodeRay + module Encoders + + # Outputs code highlighted for a color terminal. + # + # Note: This encoder is in beta. It currently doesn't use the Styles. + # + # Alias: +term+ + # + # == Authors & License + # + # By Rob Aldred (http://robaldred.co.uk) + # + # Based on idea by Nathan Weizenbaum (http://nex-3.com) + # + # MIT License (http://www.opensource.org/licenses/mit-license.php) + class Terminal < Encoder + + register_for :terminal + + TOKEN_COLORS = { + :debug => "\e[1;37;44m", + + :annotation => "\e[34m", + :attribute_name => "\e[35m", + :attribute_value => "\e[31m", + :binary => { + :self => "\e[31m", + :char => "\e[1;31m", + :delimiter => "\e[1;31m", + }, + :char => { + :self => "\e[35m", + :delimiter => "\e[1;35m" + }, + :class => "\e[1;35;4m", + :class_variable => "\e[36m", + :color => "\e[32m", + :comment => { + :self => "\e[1;30m", + :char => "\e[37m", + :delimiter => "\e[37m", + }, + :constant => "\e[1;34;4m", + :decorator => "\e[35m", + :definition => "\e[1;33m", + :directive => "\e[33m", + :docstring => "\e[31m", + :doctype => "\e[1;34m", + :done => "\e[1;30;2m", + :entity => "\e[31m", + :error => "\e[1;37;41m", + :exception => "\e[1;31m", + :float => "\e[1;35m", + :function => "\e[1;34m", + :global_variable => "\e[1;32m", + :hex => "\e[1;36m", + :id => "\e[1;34m", + :include => "\e[31m", + :integer => "\e[1;34m", + :imaginary => "\e[1;34m", + :important => "\e[1;31m", + :key => { + :self => "\e[35m", + :char => "\e[1;35m", + :delimiter => "\e[1;35m", + }, + :keyword => "\e[32m", + :label => "\e[1;33m", + :local_variable => "\e[33m", + :namespace => "\e[1;35m", + :octal => "\e[1;34m", + :predefined => "\e[36m", + :predefined_constant => "\e[1;36m", + :predefined_type => "\e[1;32m", + :preprocessor => "\e[1;36m", + :pseudo_class => "\e[1;34m", + :regexp => { + :self => "\e[35m", + :delimiter => "\e[1;35m", + :modifier => "\e[35m", + :char => "\e[1;35m", + }, + :reserved => "\e[32m", + :shell => { + :self => "\e[33m", + :char => "\e[1;33m", + :delimiter => "\e[1;33m", + :escape => "\e[1;33m", + }, + :string => { + :self => "\e[31m", + :modifier => "\e[1;31m", + :char => "\e[1;35m", + :delimiter => "\e[1;31m", + :escape => "\e[1;31m", + }, + :symbol => { + :self => "\e[33m", + :delimiter => "\e[1;33m", + }, + :tag => "\e[32m", + :type => "\e[1;34m", + :value => "\e[36m", + :variable => "\e[34m", + + :insert => { + :self => "\e[42m", + :insert => "\e[1;32;42m", + :eyecatcher => "\e[102m", + }, + :delete => { + :self => "\e[41m", + :delete => "\e[1;31;41m", + :eyecatcher => "\e[101m", + }, + :change => { + :self => "\e[44m", + :change => "\e[37;44m", + }, + :head => { + :self => "\e[45m", + :filename => "\e[37;45m" + }, + } + + TOKEN_COLORS[:keyword] = TOKEN_COLORS[:reserved] + TOKEN_COLORS[:method] = TOKEN_COLORS[:function] + TOKEN_COLORS[:escape] = TOKEN_COLORS[:delimiter] + + protected + + def setup(options) + super + @opened = [] + @color_scopes = [TOKEN_COLORS] + end + + public + + def text_token text, kind + if color = @color_scopes.last[kind] + color = color[:self] if color.is_a? Hash + + @out << color + @out << (text.index("\n") ? text.gsub("\n", "\e[0m\n" + color) : text) + @out << "\e[0m" + if outer_color = @color_scopes.last[:self] + @out << outer_color + end + else + @out << text + end + end + + def begin_group kind + @opened << kind + @out << open_token(kind) + end + alias begin_line begin_group + + def end_group kind + if @opened.pop + @color_scopes.pop + @out << "\e[0m" + if outer_color = @color_scopes.last[:self] + @out << outer_color + end + end + end + + def end_line kind + @out << (@line_filler ||= "\t" * 100) + end_group kind + end + + private + + def open_token kind + if color = @color_scopes.last[kind] + if color.is_a? Hash + @color_scopes << color + color[:self] + else + @color_scopes << @color_scopes.last + color + end + else + @color_scopes << @color_scopes.last + '' + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/text.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/text.rb new file mode 100644 index 0000000..15c66f9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/text.rb @@ -0,0 +1,46 @@ +module CodeRay +module Encoders + + # Concats the tokens into a single string, resulting in the original + # code string if no tokens were removed. + # + # Alias: +plain+, +plaintext+ + # + # == Options + # + # === :separator + # A separator string to join the tokens. + # + # Default: empty String + class Text < Encoder + + register_for :text + + FILE_EXTENSION = 'txt' + + DEFAULT_OPTIONS = { + :separator => nil + } + + def text_token text, kind + super + + if @first + @first = false + else + @out << @sep + end if @sep + end + + protected + def setup options + super + + @first = true + @sep = options[:separator] + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/token_kind_filter.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/token_kind_filter.rb new file mode 100644 index 0000000..4773ea3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/token_kind_filter.rb @@ -0,0 +1,111 @@ +module CodeRay +module Encoders + + load :filter + + # A Filter that selects tokens based on their token kind. + # + # == Options + # + # === :exclude + # + # One or many symbols (in an Array) which shall be excluded. + # + # Default: [] + # + # === :include + # + # One or many symbols (in an array) which shall be included. + # + # Default: :all, which means all tokens are included. + # + # Exclusion wins over inclusion. + # + # See also: CommentFilter + class TokenKindFilter < Filter + + register_for :token_kind_filter + + DEFAULT_OPTIONS = { + :exclude => [], + :include => :all + } + + protected + def setup options + super + + @group_excluded = false + @exclude = options[:exclude] + @exclude = Array(@exclude) unless @exclude == :all + @include = options[:include] + @include = Array(@include) unless @include == :all + end + + def include_text_token? text, kind + include_group? kind + end + + def include_group? kind + (@include == :all || @include.include?(kind)) && + !(@exclude == :all || @exclude.include?(kind)) + end + + public + + # Add the token to the output stream if +kind+ matches the conditions. + def text_token text, kind + super if !@group_excluded && include_text_token?(text, kind) + end + + # Add the token group to the output stream if +kind+ matches the + # conditions. + # + # If it does not, all tokens inside the group are excluded from the + # stream, even if their kinds match. + def begin_group kind + if @group_excluded + @group_excluded += 1 + elsif include_group? kind + super + else + @group_excluded = 1 + end + end + + # See +begin_group+. + def begin_line kind + if @group_excluded + @group_excluded += 1 + elsif include_group? kind + super + else + @group_excluded = 1 + end + end + + # Take care of re-enabling the delegation of tokens to the output stream + # if an exluded group has ended. + def end_group kind + if @group_excluded + @group_excluded -= 1 + @group_excluded = false if @group_excluded.zero? + else + super + end + end + + # See +end_group+. + def end_line kind + if @group_excluded + @group_excluded -= 1 + @group_excluded = false if @group_excluded.zero? + else + super + end + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/xml.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/xml.rb new file mode 100644 index 0000000..3d306a6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/xml.rb @@ -0,0 +1,72 @@ +module CodeRay +module Encoders + + # = XML Encoder + # + # Uses REXML. Very slow. + class XML < Encoder + + register_for :xml + + FILE_EXTENSION = 'xml' + + autoload :REXML, 'rexml/document' + + DEFAULT_OPTIONS = { + :tab_width => 8, + :pretty => -1, + :transitive => false, + } + + protected + def setup options + super + + @doc = REXML::Document.new + @doc << REXML::XMLDecl.new + @tab_width = options[:tab_width] + @root = @node = @doc.add_element('coderay-tokens') + end + + def finish options + @doc.write @out, options[:pretty], options[:transitive], true + + super + end + + public + def text_token text, kind + if kind == :space + token = @node + else + token = @node.add_element kind.to_s + end + text.scan(/(\x20+)|(\t+)|(\n)|[^\x20\t\n]+/) do |space, tab, nl| + case + when space + token << REXML::Text.new(space, true) + when tab + token << REXML::Text.new(tab, true) + when nl + token << REXML::Text.new(nl, true) + else + token << REXML::Text.new($&) + end + end + end + + def begin_group kind + @node = @node.add_element kind.to_s + end + + def end_group kind + if @node == @root + raise 'no token to close!' + end + @node = @node.parent + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/yaml.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/yaml.rb new file mode 100644 index 0000000..ba6e715 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/encoders/yaml.rb @@ -0,0 +1,50 @@ +autoload :YAML, 'yaml' + +module CodeRay +module Encoders + + # = YAML Encoder + # + # Slow. + class YAML < Encoder + + register_for :yaml + + FILE_EXTENSION = 'yaml' + + protected + def setup options + super + + @data = [] + end + + def finish options + output ::YAML.dump(@data) + end + + public + def text_token text, kind + @data << [text, kind] + end + + def begin_group kind + @data << [:begin_group, kind] + end + + def end_group kind + @data << [:end_group, kind] + end + + def begin_line kind + @data << [:begin_line, kind] + end + + def end_line kind + @data << [:end_line, kind] + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/for_redcloth.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/for_redcloth.rb new file mode 100644 index 0000000..f9df32b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/for_redcloth.rb @@ -0,0 +1,95 @@ +module CodeRay + + # A little hack to enable CodeRay highlighting in RedCloth. + # + # Usage: + # require 'coderay' + # require 'coderay/for_redcloth' + # RedCloth.new('@[ruby]puts "Hello, World!"@').to_html + # + # Make sure you have RedCloth 4.0.3 activated, for example by calling + # require 'rubygems' + # before RedCloth is loaded and before calling CodeRay.for_redcloth. + module ForRedCloth + + def self.install + gem 'RedCloth', '>= 4.0.3' if defined? gem + require 'redcloth' + unless RedCloth::VERSION.to_s >= '4.0.3' + if defined? gem + raise 'CodeRay.for_redcloth needs RedCloth version 4.0.3 or later. ' + + "You have #{RedCloth::VERSION}. Please gem install RedCloth." + else + $".delete 'redcloth.rb' # sorry, but it works + require 'rubygems' + return install # retry + end + end + unless RedCloth::VERSION.to_s >= '4.2.2' + warn 'CodeRay.for_redcloth works best with RedCloth version 4.2.2 or later.' + end + RedCloth::TextileDoc.send :include, ForRedCloth::TextileDoc + RedCloth::Formatters::HTML.module_eval do + def unescape(html) # :nodoc: + replacements = { + '&' => '&', + '"' => '"', + '>' => '>', + '<' => '<', + } + html.gsub(/&(?:amp|quot|[gl]t);/) { |entity| replacements[entity] } + end + undef code, bc_open, bc_close, escape_pre + def code(opts) # :nodoc: + opts[:block] = true + if !opts[:lang] && RedCloth::VERSION.to_s >= '4.2.0' + # simulating pre-4.2 behavior + if opts[:text].sub!(/\A\[(\w+)\]/, '') + if CodeRay::Scanners[$1].lang == :text + opts[:text] = $& + opts[:text] + else + opts[:lang] = $1 + end + end + end + if opts[:lang] && !filter_coderay + require 'coderay' + @in_bc ||= nil + format = @in_bc ? :div : :span + opts[:text] = unescape(opts[:text]) unless @in_bc + highlighted_code = CodeRay.encode opts[:text], opts[:lang], format + highlighted_code.sub!(/\A<(span|div)/) { |m| m + pba(@in_bc || opts) } + highlighted_code + else + "#{opts[:text]}" + end + end + def bc_open(opts) # :nodoc: + opts[:block] = true + @in_bc = opts + opts[:lang] ? '' : "" + end + def bc_close(opts) # :nodoc: + opts = @in_bc + @in_bc = nil + opts[:lang] ? '' : "\n" + end + def escape_pre(text) # :nodoc: + if @in_bc ||= nil + text + else + html_esc(text, :html_escape_preformatted) + end + end + end + end + + module TextileDoc # :nodoc: + attr_accessor :filter_coderay + end + + end + +end + +CodeRay::ForRedCloth.install \ No newline at end of file diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/helpers/file_type.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/helpers/file_type.rb new file mode 100644 index 0000000..7de34d5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/helpers/file_type.rb @@ -0,0 +1,151 @@ +module CodeRay + + # = FileType + # + # A simple filetype recognizer. + # + # == Usage + # + # # determine the type of the given + # lang = FileType[file_name] + # + # # return :text if the file type is unknown + # lang = FileType.fetch file_name, :text + # + # # try the shebang line, too + # lang = FileType.fetch file_name, :text, true + module FileType + + UnknownFileType = Class.new Exception + + class << self + + # Try to determine the file type of the file. + # + # +filename+ is a relative or absolute path to a file. + # + # The file itself is only accessed when +read_shebang+ is set to true. + # That means you can get filetypes from files that don't exist. + def [] filename, read_shebang = false + name = File.basename filename + ext = File.extname(name).sub(/^\./, '') # from last dot, delete the leading dot + ext2 = filename.to_s[/\.(.*)/, 1] # from first dot + + type = + TypeFromExt[ext] || + TypeFromExt[ext.downcase] || + (TypeFromExt[ext2] if ext2) || + (TypeFromExt[ext2.downcase] if ext2) || + TypeFromName[name] || + TypeFromName[name.downcase] + type ||= type_from_shebang(filename) if read_shebang + + type + end + + # This works like Hash#fetch. + # + # If the filetype cannot be found, the +default+ value + # is returned. + def fetch filename, default = nil, read_shebang = false + if default && block_given? + warn 'Block supersedes default value argument; use either.' + end + + if type = self[filename, read_shebang] + type + else + return yield if block_given? + return default if default + raise UnknownFileType, 'Could not determine type of %p.' % filename + end + end + + protected + + def type_from_shebang filename + return unless File.exist? filename + File.open filename, 'r' do |f| + if first_line = f.gets + if type = first_line[TypeFromShebang] + type.to_sym + end + end + end + end + + end + + TypeFromExt = { + 'c' => :c, + 'cfc' => :xml, + 'cfm' => :xml, + 'clj' => :clojure, + 'css' => :css, + 'diff' => :diff, + 'dpr' => :delphi, + 'erb' => :erb, + 'gemspec' => :ruby, + 'go' => :go, + 'groovy' => :groovy, + 'gvy' => :groovy, + 'h' => :c, + 'haml' => :haml, + 'htm' => :html, + 'html' => :html, + 'html.erb' => :erb, + 'java' => :java, + 'js' => :java_script, + 'json' => :json, + 'lua' => :lua, + 'mab' => :ruby, + 'pas' => :delphi, + 'patch' => :diff, + 'phtml' => :php, + 'php' => :php, + 'php3' => :php, + 'php4' => :php, + 'php5' => :php, + 'prawn' => :ruby, + 'py' => :python, + 'py3' => :python, + 'pyw' => :python, + 'rake' => :ruby, + 'raydebug' => :raydebug, + 'rb' => :ruby, + 'rbw' => :ruby, + 'rhtml' => :erb, + 'rjs' => :ruby, + 'rpdf' => :ruby, + 'ru' => :ruby, # config.ru + 'rxml' => :ruby, + 'sass' => :sass, + 'sql' => :sql, + 'taskpaper' => :taskpaper, + 'template' => :json, # AWS CloudFormation template + 'tmproj' => :xml, + 'xaml' => :xml, + 'xhtml' => :html, + 'xml' => :xml, + 'yaml' => :yaml, + 'yml' => :yaml, + } + for cpp_alias in %w[cc cpp cp cxx c++ C hh hpp h++ cu] + TypeFromExt[cpp_alias] = :cpp + end + + TypeFromShebang = /\b(?:ruby|perl|python|sh)\b/ + + TypeFromName = { + 'Capfile' => :ruby, + 'Rakefile' => :ruby, + 'Rantfile' => :ruby, + 'Gemfile' => :ruby, + 'Guardfile' => :ruby, + 'Vagrantfile' => :ruby, + 'Appraisals' => :ruby + } + + end + +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/helpers/plugin.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/helpers/plugin.rb new file mode 100644 index 0000000..4567943 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/helpers/plugin.rb @@ -0,0 +1,55 @@ +module CodeRay + + # = Plugin + # + # Plugins have to include this module. + # + # IMPORTANT: Use extend for this module. + # + # See CodeRay::PluginHost for examples. + module Plugin + + attr_reader :plugin_id + + # Register this class for the given +id+. + # + # Example: + # class MyPlugin < PluginHost::BaseClass + # register_for :my_id + # ... + # end + # + # See PluginHost.register. + def register_for id + @plugin_id = id + plugin_host.register self, id + end + + # Returns the title of the plugin, or sets it to the + # optional argument +title+. + def title title = nil + if title + @title = title.to_s + else + @title ||= name[/([^:]+)$/, 1] + end + end + + # The PluginHost for this Plugin class. + def plugin_host host = nil + if host.is_a? PluginHost + const_set :PLUGIN_HOST, host + end + self::PLUGIN_HOST + end + + def aliases + plugin_host.plugin_hash.inject [] do |aliases, (key, _)| + aliases << key if plugin_host[key] == self + aliases + end + end + + end + +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/helpers/plugin_host.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/helpers/plugin_host.rb new file mode 100644 index 0000000..e9bc17c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/helpers/plugin_host.rb @@ -0,0 +1,221 @@ +module CodeRay + + # = PluginHost + # + # A simple subclass/subfolder plugin system. + # + # Example: + # class Generators + # extend PluginHost + # plugin_path 'app/generators' + # end + # + # class Generator + # extend Plugin + # PLUGIN_HOST = Generators + # end + # + # class FancyGenerator < Generator + # register_for :fancy + # end + # + # Generators[:fancy] #-> FancyGenerator + # # or + # CodeRay.require_plugin 'Generators/fancy' + # # or + # Generators::Fancy + module PluginHost + + # Raised if Encoders::[] fails because: + # * a file could not be found + # * the requested Plugin is not registered + PluginNotFound = Class.new LoadError + HostNotFound = Class.new LoadError + + PLUGIN_HOSTS = [] + PLUGIN_HOSTS_BY_ID = {} # dummy hash + + # Loads all plugins using list and load. + def load_all + for plugin in list + load plugin + end + end + + # Returns the Plugin for +id+. + # + # Example: + # yaml_plugin = MyPluginHost[:yaml] + def [] id, *args, &blk + plugin = validate_id(id) + begin + plugin = plugin_hash.[](plugin, *args, &blk) + end while plugin.is_a? String + plugin + end + + alias load [] + + # Tries to +load+ the missing plugin by translating +const+ to the + # underscore form (eg. LinesOfCode becomes lines_of_code). + def const_missing const + id = const.to_s. + gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). + gsub(/([a-z\d])([A-Z])/,'\1_\2'). + downcase + load id + end + + class << self + + # Adds the module/class to the PLUGIN_HOSTS list. + def extended mod + PLUGIN_HOSTS << mod + end + + end + + # The path where the plugins can be found. + def plugin_path *args + unless args.empty? + @plugin_path = File.expand_path File.join(*args) + end + @plugin_path ||= '' + end + + # Map a plugin_id to another. + # + # Usage: Put this in a file plugin_path/_map.rb. + # + # class MyColorHost < PluginHost + # map :navy => :dark_blue, + # :maroon => :brown, + # :luna => :moon + # end + def map hash + for from, to in hash + from = validate_id from + to = validate_id to + plugin_hash[from] = to unless plugin_hash.has_key? from + end + end + + # Define the default plugin to use when no plugin is found + # for a given id, or return the default plugin. + # + # See also map. + # + # class MyColorHost < PluginHost + # map :navy => :dark_blue + # default :gray + # end + # + # MyColorHost.default # loads and returns the Gray plugin + def default id = nil + if id + id = validate_id id + raise "The default plugin can't be named \"default\"." if id == :default + plugin_hash[:default] = id + else + load :default + end + end + + # Every plugin must register itself for +id+ by calling register_for, + # which calls this method. + # + # See Plugin#register_for. + def register plugin, id + plugin_hash[validate_id(id)] = plugin + end + + # A Hash of plugion_id => Plugin pairs. + def plugin_hash + @plugin_hash ||= (@plugin_hash = make_plugin_hash).tap { load_plugin_map } + end + + # Returns an array of all .rb files in the plugin path. + # + # The extension .rb is not included. + def list + Dir[path_to('*')].select do |file| + File.basename(file)[/^(?!_)\w+\.rb$/] + end.map do |file| + File.basename(file, '.rb').to_sym + end + end + + # Returns an array of all Plugins. + # + # Note: This loads all plugins using load_all. + def all_plugins + load_all + plugin_hash.values.grep(Class) + end + + # Loads the map file (see map). + # + # This is done automatically when plugin_path is called. + def load_plugin_map + mapfile = path_to '_map' + if File.exist? mapfile + require mapfile + true + else + false + end + end + + protected + + # Return a plugin hash that automatically loads plugins. + def make_plugin_hash + Hash.new do |h, plugin_id| + id = validate_id(plugin_id) + path = path_to id + begin + require path + rescue LoadError => boom + if h.has_key?(:default) + h[:default] + else + raise PluginNotFound, '%p could not load plugin %p: %s' % [self, id, boom] + end + else + # Plugin should have registered by now + if h.has_key? id + h[id] + else + raise PluginNotFound, "No #{self.name} plugin for #{id.inspect} found in #{path}." + end + end + end + end + + # Returns the expected path to the plugin file for the given id. + def path_to plugin_id + File.join plugin_path, "#{plugin_id}.rb" + end + + # Converts +id+ to a valid plugin ID String, or returns +nil+. + # + # Raises +ArgumentError+ for all other objects, or if the + # given String includes non-alphanumeric characters (\W). + def validate_id id + case id + when Symbol + id.to_s + when String + if id[/\w+/] == id + id.downcase + else + raise ArgumentError, "Invalid id given: #{id}" + end + else + raise ArgumentError, "Symbol or String expected, but #{id.class} given." + end + end + + end + +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/helpers/word_list.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/helpers/word_list.rb new file mode 100644 index 0000000..4a42c4a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/helpers/word_list.rb @@ -0,0 +1,72 @@ +module CodeRay + + # = WordList + # + # A Hash subclass designed for mapping word lists to token types. + # + # A WordList is a Hash with some additional features. + # It is intended to be used for keyword recognition. + # + # WordList is optimized to be used in Scanners, + # typically to decide whether a given ident is a special token. + # + # For case insensitive words use WordList::CaseIgnoring. + # + # Example: + # + # # define word arrays + # RESERVED_WORDS = %w[ + # asm break case continue default do else + # ] + # + # PREDEFINED_TYPES = %w[ + # int long short char void + # ] + # + # # make a WordList + # IDENT_KIND = WordList.new(:ident). + # add(RESERVED_WORDS, :reserved). + # add(PREDEFINED_TYPES, :predefined_type) + # + # ... + # + # def scan_tokens tokens, options + # ... + # + # elsif scan(/[A-Za-z_][A-Za-z_0-9]*/) + # # use it + # kind = IDENT_KIND[match] + # ... + class WordList < Hash + + # Create a new WordList with +default+ as default value. + def initialize default = false + super default + end + + # Add words to the list and associate them with +value+. + # + # Returns +self+, so you can concat add calls. + def add words, value = true + words.each { |word| self[word] = value } + self + end + + end + + + # A CaseIgnoring WordList is like a WordList, only that + # keys are compared case-insensitively (normalizing keys using +downcase+). + class WordList::CaseIgnoring < WordList + + def [] key + super key.downcase + end + + def []= key, value + super key.downcase, value + end + + end + +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners.rb new file mode 100644 index 0000000..3c7e594 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners.rb @@ -0,0 +1,27 @@ +require 'strscan' + +module CodeRay + + autoload :WordList, coderay_path('helpers', 'word_list') + + # = Scanners + # + # This module holds the Scanner class and its subclasses. + # For example, the Ruby scanner is named CodeRay::Scanners::Ruby + # can be found in coderay/scanners/ruby. + # + # Scanner also provides methods and constants for the register + # mechanism and the [] method that returns the Scanner class + # belonging to the given lang. + # + # See PluginHost. + module Scanners + + extend PluginHost + plugin_path File.dirname(__FILE__), 'scanners' + + autoload :Scanner, CodeRay.coderay_path('scanners', 'scanner') + + end + +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/_map.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/_map.rb new file mode 100644 index 0000000..a240298 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/_map.rb @@ -0,0 +1,24 @@ +module CodeRay +module Scanners + + map \ + :'c++' => :cpp, + :cplusplus => :cpp, + :ecmascript => :java_script, + :ecma_script => :java_script, + :rhtml => :erb, + :eruby => :erb, + :irb => :ruby, + :javascript => :java_script, + :js => :java_script, + :pascal => :delphi, + :patch => :diff, + :plain => :text, + :plaintext => :text, + :xhtml => :html, + :yml => :yaml + + default :text + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/c.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/c.rb new file mode 100644 index 0000000..fb2f30d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/c.rb @@ -0,0 +1,189 @@ +module CodeRay +module Scanners + + # Scanner for C. + class C < Scanner + + register_for :c + file_extension 'c' + + KEYWORDS = [ + 'asm', 'break', 'case', 'continue', 'default', 'do', + 'else', 'enum', 'for', 'goto', 'if', 'return', + 'sizeof', 'struct', 'switch', 'typedef', 'union', 'while', + 'restrict', # added in C99 + ] # :nodoc: + + PREDEFINED_TYPES = [ + 'int', 'long', 'short', 'char', + 'signed', 'unsigned', 'float', 'double', + 'bool', 'complex', # added in C99 + ] # :nodoc: + + PREDEFINED_CONSTANTS = [ + 'EOF', 'NULL', + 'true', 'false', # added in C99 + ] # :nodoc: + DIRECTIVES = [ + 'auto', 'extern', 'register', 'static', 'void', + 'const', 'volatile', # added in C89 + 'inline', # added in C99 + ] # :nodoc: + + IDENT_KIND = WordList.new(:ident). + add(KEYWORDS, :keyword). + add(PREDEFINED_TYPES, :predefined_type). + add(DIRECTIVES, :directive). + add(PREDEFINED_CONSTANTS, :predefined_constant) # :nodoc: + + ESCAPE = / [rbfntv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x # :nodoc: + UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x # :nodoc: + + protected + + def scan_tokens encoder, options + + state = :initial + label_expected = true + case_expected = false + label_expected_before_preproc_line = nil + in_preproc_line = false + + until eos? + + case state + + when :initial + + if match = scan(/ \s+ | \\\n /x) + if in_preproc_line && match != "\\\n" && match.index(?\n) + in_preproc_line = false + label_expected = label_expected_before_preproc_line + end + encoder.text_token match, :space + + elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx) + encoder.text_token match, :comment + + elsif match = scan(/ [-+*=<>?:;,!&^|()\[\]{}~%]+ | \/=? | \.(?!\d) /x) + label_expected = match =~ /[;\{\}]/ + if case_expected + label_expected = true if match == ':' + case_expected = false + end + encoder.text_token match, :operator + + elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x) + kind = IDENT_KIND[match] + if kind == :ident && label_expected && !in_preproc_line && scan(/:(?!:)/) + kind = :label + match << matched + else + label_expected = false + if kind == :keyword + case match + when 'case', 'default' + case_expected = true + end + end + end + encoder.text_token match, kind + + elsif match = scan(/L?"/) + encoder.begin_group :string + if match[0] == ?L + encoder.text_token 'L', :modifier + match = '"' + end + encoder.text_token match, :delimiter + state = :string + + elsif match = scan(/ \# \s* if \s* 0 /x) + match << scan_until(/ ^\# (?:elif|else|endif) .*? $ | \z /xm) unless eos? + encoder.text_token match, :comment + + elsif match = scan(/#[ \t]*(\w*)/) + encoder.text_token match, :preprocessor + in_preproc_line = true + label_expected_before_preproc_line = label_expected + state = :include_expected if self[1] == 'include' + + elsif match = scan(/ L?' (?: [^\'\n\\] | \\ #{ESCAPE} )? '? /ox) + label_expected = false + encoder.text_token match, :char + + elsif match = scan(/\$/) + encoder.text_token match, :ident + + elsif match = scan(/0[xX][0-9A-Fa-f]+/) + label_expected = false + encoder.text_token match, :hex + + elsif match = scan(/(?:0[0-7]+)(?![89.eEfF])/) + label_expected = false + encoder.text_token match, :octal + + elsif match = scan(/(?:\d+)(?![.eEfF])L?L?/) + label_expected = false + encoder.text_token match, :integer + + elsif match = scan(/\d[fF]?|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/) + label_expected = false + encoder.text_token match, :float + + else + encoder.text_token getch, :error + + end + + when :string + if match = scan(/[^\\\n"]+/) + encoder.text_token match, :content + elsif match = scan(/"/) + encoder.text_token match, :delimiter + encoder.end_group :string + state = :initial + label_expected = false + elsif match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox) + encoder.text_token match, :char + elsif match = scan(/ \\ | $ /x) + encoder.end_group :string + encoder.text_token match, :error unless match.empty? + state = :initial + label_expected = false + else + raise_inspect "else case \" reached; %p not handled." % peek(1), encoder + end + + when :include_expected + if match = scan(/<[^>\n]+>?|"[^"\n\\]*(?:\\.[^"\n\\]*)*"?/) + encoder.text_token match, :include + state = :initial + + elsif match = scan(/\s+/) + encoder.text_token match, :space + state = :initial if match.index ?\n + + else + state = :initial + + end + + else + raise_inspect 'Unknown state', encoder + + end + + end + + if state == :string + encoder.end_group :string + end + + encoder + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/clojure.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/clojure.rb new file mode 100644 index 0000000..f8fbf65 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/clojure.rb @@ -0,0 +1,217 @@ +# encoding: utf-8 +module CodeRay + module Scanners + + # Clojure scanner by Licenser. + class Clojure < Scanner + + register_for :clojure + file_extension 'clj' + + SPECIAL_FORMS = %w[ + def if do let quote var fn loop recur throw try catch monitor-enter monitor-exit . + new + ] # :nodoc: + + CORE_FORMS = %w[ + + - -> ->> .. / * <= < = == >= > accessor aclone add-classpath add-watch + agent agent-error agent-errors aget alength alias all-ns alter alter-meta! + alter-var-root amap ancestors and apply areduce array-map aset aset-boolean + aset-byte aset-char aset-double aset-float aset-int aset-long aset-short + assert assoc assoc! assoc-in associative? atom await await-for bases bean + bigdec bigint binding bit-and bit-and-not bit-clear bit-flip bit-not bit-or + bit-set bit-shift-left bit-shift-right bit-test bit-xor boolean boolean-array + booleans bound-fn bound-fn* bound? butlast byte byte-array bytes case cast char + char-array char-escape-string char-name-string char? chars class class? + clear-agent-errors clojure-version coll? comment commute comp comparator + compare compare-and-set! compile complement concat cond condp conj conj! + cons constantly construct-proxy contains? count counted? create-ns + create-struct cycle dec decimal? declare definline defmacro defmethod defmulti + defn defn- defonce defprotocol defrecord defstruct deftype delay delay? + deliver denominator deref derive descendants disj disj! dissoc dissoc! + distinct distinct? doall doc dorun doseq dosync dotimes doto double + double-array doubles drop drop-last drop-while empty empty? ensure + enumeration-seq error-handler error-mode eval even? every? extend + extend-protocol extend-type extenders extends? false? ffirst file-seq + filter find find-doc find-ns find-var first float float-array float? + floats flush fn fn? fnext for force format future future-call future-cancel + future-cancelled? future-done? future? gen-class gen-interface gensym get + get-in get-method get-proxy-class get-thread-bindings get-validator hash + hash-map hash-set identical? identity if-let if-not ifn? import in-ns + inc init-proxy instance? int int-array integer? interleave intern + interpose into into-array ints io! isa? iterate iterator-seq juxt key + keys keyword keyword? last lazy-cat lazy-seq let letfn line-seq list list* + list? load load-file load-reader load-string loaded-libs locking long + long-array longs loop macroexpand macroexpand-1 make-array make-hierarchy + map map? mapcat max max-key memfn memoize merge merge-with meta methods + min min-key mod name namespace neg? newline next nfirst nil? nnext not + not-any? not-empty not-every? not= ns ns-aliases ns-imports ns-interns + ns-map ns-name ns-publics ns-refers ns-resolve ns-unalias ns-unmap nth + nthnext num number? numerator object-array odd? or parents partial + partition pcalls peek persistent! pmap pop pop! pop-thread-bindings + pos? pr pr-str prefer-method prefers print print-namespace-doc + print-str printf println println-str prn prn-str promise proxy + proxy-mappings proxy-super push-thread-bindings pvalues quot rand + rand-int range ratio? rationalize re-find re-groups re-matcher + re-matches re-pattern re-seq read read-line read-string reduce ref + ref-history-count ref-max-history ref-min-history ref-set refer + refer-clojure reify release-pending-sends rem remove remove-all-methods + remove-method remove-ns remove-watch repeat repeatedly replace replicate + require reset! reset-meta! resolve rest restart-agent resultset-seq + reverse reversible? rseq rsubseq satisfies? second select-keys send + send-off seq seq? seque sequence sequential? set set-error-handler! + set-error-mode! set-validator! set? short short-array shorts + shutdown-agents slurp some sort sort-by sorted-map sorted-map-by + sorted-set sorted-set-by sorted? special-form-anchor special-symbol? + split-at split-with str string? struct struct-map subs subseq subvec + supers swap! symbol symbol? sync syntax-symbol-anchor take take-last + take-nth take-while test the-ns thread-bound? time to-array to-array-2d + trampoline transient tree-seq true? type unchecked-add unchecked-dec + unchecked-divide unchecked-inc unchecked-multiply unchecked-negate + unchecked-remainder unchecked-subtract underive update-in update-proxy + use val vals var-get var-set var? vary-meta vec vector vector-of vector? + when when-first when-let when-not while with-bindings with-bindings* + with-in-str with-local-vars with-meta with-open with-out-str + with-precision xml-seq zero? zipmap + ] # :nodoc: + + PREDEFINED_CONSTANTS = %w[ + true false nil *1 *2 *3 *agent* *clojure-version* *command-line-args* + *compile-files* *compile-path* *e *err* *file* *flush-on-newline* + *in* *ns* *out* *print-dup* *print-length* *print-level* *print-meta* + *print-readably* *read-eval* *warn-on-reflection* + ] # :nodoc: + + IDENT_KIND = WordList.new(:ident). + add(SPECIAL_FORMS, :keyword). + add(CORE_FORMS, :keyword). + add(PREDEFINED_CONSTANTS, :predefined_constant) + + KEYWORD_NEXT_TOKEN_KIND = WordList.new(nil). + add(%w[ def defn defn- definline defmacro defmulti defmethod defstruct defonce declare ], :function). + add(%w[ ns ], :namespace). + add(%w[ defprotocol defrecord ], :class) + + BASIC_IDENTIFIER = /[a-zA-Z$%*\/_+!?&<>\-=]=?[a-zA-Z0-9$&*+!\/_?<>\-\#]*/ + IDENTIFIER = /(?!-\d)(?:(?:#{BASIC_IDENTIFIER}\.)*#{BASIC_IDENTIFIER}(?:\/#{BASIC_IDENTIFIER})?\.?)|\.\.?/ + SYMBOL = /::?#{IDENTIFIER}/o + DIGIT = /\d/ + DIGIT10 = DIGIT + DIGIT16 = /[0-9a-f]/i + DIGIT8 = /[0-7]/ + DIGIT2 = /[01]/ + RADIX16 = /\#x/i + RADIX8 = /\#o/i + RADIX2 = /\#b/i + RADIX10 = /\#d/i + EXACTNESS = /#i|#e/i + SIGN = /[\+-]?/ + EXP_MARK = /[esfdl]/i + EXP = /#{EXP_MARK}#{SIGN}#{DIGIT}+/ + SUFFIX = /#{EXP}?/ + PREFIX10 = /#{RADIX10}?#{EXACTNESS}?|#{EXACTNESS}?#{RADIX10}?/ + PREFIX16 = /#{RADIX16}#{EXACTNESS}?|#{EXACTNESS}?#{RADIX16}/ + PREFIX8 = /#{RADIX8}#{EXACTNESS}?|#{EXACTNESS}?#{RADIX8}/ + PREFIX2 = /#{RADIX2}#{EXACTNESS}?|#{EXACTNESS}?#{RADIX2}/ + UINT10 = /#{DIGIT10}+#*/ + UINT16 = /#{DIGIT16}+#*/ + UINT8 = /#{DIGIT8}+#*/ + UINT2 = /#{DIGIT2}+#*/ + DECIMAL = /#{DIGIT10}+#+\.#*#{SUFFIX}|#{DIGIT10}+\.#{DIGIT10}*#*#{SUFFIX}|\.#{DIGIT10}+#*#{SUFFIX}|#{UINT10}#{EXP}/ + UREAL10 = /#{UINT10}\/#{UINT10}|#{DECIMAL}|#{UINT10}/ + UREAL16 = /#{UINT16}\/#{UINT16}|#{UINT16}/ + UREAL8 = /#{UINT8}\/#{UINT8}|#{UINT8}/ + UREAL2 = /#{UINT2}\/#{UINT2}|#{UINT2}/ + REAL10 = /#{SIGN}#{UREAL10}/ + REAL16 = /#{SIGN}#{UREAL16}/ + REAL8 = /#{SIGN}#{UREAL8}/ + REAL2 = /#{SIGN}#{UREAL2}/ + IMAG10 = /i|#{UREAL10}i/ + IMAG16 = /i|#{UREAL16}i/ + IMAG8 = /i|#{UREAL8}i/ + IMAG2 = /i|#{UREAL2}i/ + COMPLEX10 = /#{REAL10}@#{REAL10}|#{REAL10}\+#{IMAG10}|#{REAL10}-#{IMAG10}|\+#{IMAG10}|-#{IMAG10}|#{REAL10}/ + COMPLEX16 = /#{REAL16}@#{REAL16}|#{REAL16}\+#{IMAG16}|#{REAL16}-#{IMAG16}|\+#{IMAG16}|-#{IMAG16}|#{REAL16}/ + COMPLEX8 = /#{REAL8}@#{REAL8}|#{REAL8}\+#{IMAG8}|#{REAL8}-#{IMAG8}|\+#{IMAG8}|-#{IMAG8}|#{REAL8}/ + COMPLEX2 = /#{REAL2}@#{REAL2}|#{REAL2}\+#{IMAG2}|#{REAL2}-#{IMAG2}|\+#{IMAG2}|-#{IMAG2}|#{REAL2}/ + NUM10 = /#{PREFIX10}?#{COMPLEX10}/ + NUM16 = /#{PREFIX16}#{COMPLEX16}/ + NUM8 = /#{PREFIX8}#{COMPLEX8}/ + NUM2 = /#{PREFIX2}#{COMPLEX2}/ + NUM = /#{NUM10}|#{NUM16}|#{NUM8}|#{NUM2}/ + + protected + + def scan_tokens encoder, options + + state = :initial + kind = nil + + until eos? + + case state + when :initial + if match = scan(/ \s+ | \\\n | , /x) + encoder.text_token match, :space + elsif match = scan(/['`\(\[\)\]\{\}]|\#[({]|~@?|[@\^]/) + encoder.text_token match, :operator + elsif match = scan(/;.*/) + encoder.text_token match, :comment # TODO: recognize (comment ...) too + elsif match = scan(/\#?\\(?:newline|space|.?)/) + encoder.text_token match, :char + elsif match = scan(/\#[ft]/) + encoder.text_token match, :predefined_constant + elsif match = scan(/#{IDENTIFIER}/o) + kind = IDENT_KIND[match] + encoder.text_token match, kind + if rest? && kind == :keyword + if kind = KEYWORD_NEXT_TOKEN_KIND[match] + encoder.text_token match, :space if match = scan(/\s+/o) + encoder.text_token match, kind if match = scan(/#{IDENTIFIER}/o) + end + end + elsif match = scan(/#{SYMBOL}/o) + encoder.text_token match, :symbol + elsif match = scan(/\./) + encoder.text_token match, :operator + elsif match = scan(/ \# \^ #{IDENTIFIER} /ox) + encoder.text_token match, :type + elsif match = scan(/ (\#)? " /x) + state = self[1] ? :regexp : :string + encoder.begin_group state + encoder.text_token match, :delimiter + elsif match = scan(/#{NUM}/o) and not matched.empty? + encoder.text_token match, match[/[.e\/]/i] ? :float : :integer + else + encoder.text_token getch, :error + end + + when :string, :regexp + if match = scan(/[^"\\]+|\\.?/) + encoder.text_token match, :content + elsif match = scan(/"/) + encoder.text_token match, :delimiter + encoder.end_group state + state = :initial + else + raise_inspect "else case \" reached; %p not handled." % peek(1), + encoder, state + end + + else + raise 'else case reached' + + end + + end + + if [:string, :regexp].include? state + encoder.end_group state + end + + encoder + + end + end + end +end \ No newline at end of file diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/cpp.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/cpp.rb new file mode 100644 index 0000000..cd4d094 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/cpp.rb @@ -0,0 +1,217 @@ +module CodeRay +module Scanners + + # Scanner for C++. + # + # Aliases: +cplusplus+, c++ + class CPlusPlus < Scanner + + register_for :cpp + file_extension 'cpp' + title 'C++' + + #-- http://www.cppreference.com/wiki/keywords/start + KEYWORDS = [ + 'and', 'and_eq', 'asm', 'bitand', 'bitor', 'break', + 'case', 'catch', 'class', 'compl', 'const_cast', + 'continue', 'default', 'delete', 'do', 'dynamic_cast', 'else', + 'enum', 'export', 'for', 'goto', 'if', 'namespace', 'new', + 'not', 'not_eq', 'or', 'or_eq', 'reinterpret_cast', 'return', + 'sizeof', 'static_assert', 'static_cast', 'struct', 'switch', + 'template', 'throw', 'try', 'typedef', 'typeid', 'typename', 'union', + 'while', 'xor', 'xor_eq', + ] # :nodoc: + + PREDEFINED_TYPES = [ + 'bool', 'char', 'char16_t', 'char32_t', 'double', 'float', + 'int', 'long', 'short', 'signed', 'unsigned', + 'wchar_t', 'string', + ] # :nodoc: + PREDEFINED_CONSTANTS = [ + 'false', 'true', + 'EOF', 'NULL', 'nullptr' + ] # :nodoc: + PREDEFINED_VARIABLES = [ + 'this', + ] # :nodoc: + DIRECTIVES = [ + 'alignas', 'alignof', 'auto', 'const', 'constexpr', 'decltype', 'explicit', + 'extern', 'final', 'friend', 'inline', 'mutable', 'noexcept', 'operator', + 'override', 'private', 'protected', 'public', 'register', 'static', + 'thread_local', 'using', 'virtual', 'void', 'volatile', + ] # :nodoc: + + IDENT_KIND = WordList.new(:ident). + add(KEYWORDS, :keyword). + add(PREDEFINED_TYPES, :predefined_type). + add(PREDEFINED_VARIABLES, :local_variable). + add(DIRECTIVES, :directive). + add(PREDEFINED_CONSTANTS, :predefined_constant) # :nodoc: + + ESCAPE = / [rbfntv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x # :nodoc: + UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x # :nodoc: + + protected + + def scan_tokens encoder, options + + state = :initial + label_expected = true + case_expected = false + label_expected_before_preproc_line = nil + in_preproc_line = false + + until eos? + + case state + + when :initial + + if match = scan(/ \s+ | \\\n /x) + if in_preproc_line && match != "\\\n" && match.index(?\n) + in_preproc_line = false + label_expected = label_expected_before_preproc_line + end + encoder.text_token match, :space + + elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx) + encoder.text_token match, :comment + + elsif match = scan(/ \# \s* if \s* 0 /x) + match << scan_until(/ ^\# (?:elif|else|endif) .*? $ | \z /xm) unless eos? + encoder.text_token match, :comment + + elsif match = scan(/ [-+*=<>?:;,!&^|()\[\]{}~%]+ | \/=? | \.(?!\d) /x) + label_expected = match =~ /[;\{\}]/ + if case_expected + label_expected = true if match == ':' + case_expected = false + end + encoder.text_token match, :operator + + elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x) + kind = IDENT_KIND[match] + if kind == :ident && label_expected && !in_preproc_line && scan(/:(?!:)/) + kind = :label + match << matched + else + label_expected = false + if kind == :keyword + case match + when 'class' + state = :class_name_expected + when 'case', 'default' + case_expected = true + end + end + end + encoder.text_token match, kind + + elsif match = scan(/\$/) + encoder.text_token match, :ident + + elsif match = scan(/L?"/) + encoder.begin_group :string + if match[0] == ?L + encoder.text_token match, 'L', :modifier + match = '"' + end + state = :string + encoder.text_token match, :delimiter + + elsif match = scan(/#[ \t]*(\w*)/) + encoder.text_token match, :preprocessor + in_preproc_line = true + label_expected_before_preproc_line = label_expected + state = :include_expected if self[1] == 'include' + + elsif match = scan(/ L?' (?: [^\'\n\\] | \\ #{ESCAPE} )? '? /ox) + label_expected = false + encoder.text_token match, :char + + elsif match = scan(/0[xX][0-9A-Fa-f]+/) + label_expected = false + encoder.text_token match, :hex + + elsif match = scan(/(?:0[0-7]+)(?![89.eEfF])/) + label_expected = false + encoder.text_token match, :octal + + elsif match = scan(/(?:\d+)(?![.eEfF])L?L?/) + label_expected = false + encoder.text_token match, :integer + + elsif match = scan(/\d[fF]?|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/) + label_expected = false + encoder.text_token match, :float + + else + encoder.text_token getch, :error + + end + + when :string + if match = scan(/[^\\"]+/) + encoder.text_token match, :content + elsif match = scan(/"/) + encoder.text_token match, :delimiter + encoder.end_group :string + state = :initial + label_expected = false + elsif match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox) + encoder.text_token match, :char + elsif match = scan(/ \\ | $ /x) + encoder.end_group :string + encoder.text_token match, :error unless match.empty? + state = :initial + label_expected = false + else + raise_inspect "else case \" reached; %p not handled." % peek(1), encoder + end + + when :include_expected + if match = scan(/<[^>\n]+>?|"[^"\n\\]*(?:\\.[^"\n\\]*)*"?/) + encoder.text_token match, :include + state = :initial + + elsif match = scan(/\s+/) + encoder.text_token match, :space + state = :initial if match.index ?\n + + else + state = :initial + + end + + when :class_name_expected + if match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x) + encoder.text_token match, :class + state = :initial + + elsif match = scan(/\s+/) + encoder.text_token match, :space + + else + encoder.text_token getch, :error + state = :initial + + end + + else + raise_inspect 'Unknown state', encoder + + end + + end + + if state == :string + encoder.end_group :string + end + + encoder + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/css.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/css.rb new file mode 100644 index 0000000..55d5239 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/css.rb @@ -0,0 +1,196 @@ +module CodeRay +module Scanners + + class CSS < Scanner + + register_for :css + + KINDS_NOT_LOC = [ + :comment, + :class, :pseudo_class, :tag, + :id, :directive, + :key, :value, :operator, :color, :float, :string, + :error, :important, :type, + ] # :nodoc: + + module RE # :nodoc: + Hex = /[0-9a-fA-F]/ + Unicode = /\\#{Hex}{1,6}\b/ # differs from standard because it allows uppercase hex too + Escape = /#{Unicode}|\\[^\n0-9a-fA-F]/ + NMChar = /[-_a-zA-Z0-9]/ + NMStart = /[_a-zA-Z]/ + String1 = /"(?:[^\n\\"]+|\\\n|#{Escape})*"?/ # TODO: buggy regexp + String2 = /'(?:[^\n\\']+|\\\n|#{Escape})*'?/ # TODO: buggy regexp + String = /#{String1}|#{String2}/ + + HexColor = /#(?:#{Hex}{6}|#{Hex}{3})/ + + Num = /-?(?:[0-9]*\.[0-9]+|[0-9]+)n?/ + Name = /#{NMChar}+/ + Ident = /-?#{NMStart}#{NMChar}*/ + AtKeyword = /@#{Ident}/ + Percentage = /#{Num}%/ + + reldimensions = %w[em ex px] + absdimensions = %w[in cm mm pt pc] + Unit = Regexp.union(*(reldimensions + absdimensions + %w[s dpi dppx deg])) + + Dimension = /#{Num}#{Unit}/ + + Function = /(?:url|alpha|attr|counters?)\((?:[^)\n]|\\\))*\)?/ + + Id = /(?!#{HexColor}\b(?!-))##{Name}/ + Class = /\.#{Name}/ + PseudoClass = /::?#{Ident}/ + AttributeSelector = /\[[^\]]*\]?/ + end + + protected + + def setup + @state = :initial + @value_expected = false + end + + def scan_tokens encoder, options + states = Array(options[:state] || @state).dup + value_expected = @value_expected + + until eos? + + if match = scan(/\s+/) + encoder.text_token match, :space + + elsif case states.last + when :initial, :media + if match = scan(/(?>#{RE::Ident})(?!\()|\*/ox) + encoder.text_token match, :tag + next + elsif match = scan(RE::Class) + encoder.text_token match, :class + next + elsif match = scan(RE::Id) + encoder.text_token match, :id + next + elsif match = scan(RE::PseudoClass) + encoder.text_token match, :pseudo_class + next + elsif match = scan(RE::AttributeSelector) + # TODO: Improve highlighting inside of attribute selectors. + encoder.text_token match[0,1], :operator + encoder.text_token match[1..-2], :attribute_name if match.size > 2 + encoder.text_token match[-1,1], :operator if match[-1] == ?] + next + elsif match = scan(/@media/) + encoder.text_token match, :directive + states.push :media_before_name + next + end + + when :block + if match = scan(/(?>#{RE::Ident})(?!\()/ox) + if value_expected + encoder.text_token match, :value + else + encoder.text_token match, :key + end + next + end + + when :media_before_name + if match = scan(RE::Ident) + encoder.text_token match, :type + states[-1] = :media_after_name + next + end + + when :media_after_name + if match = scan(/\{/) + encoder.text_token match, :operator + states[-1] = :media + next + end + + else + #:nocov: + raise_inspect 'Unknown state', encoder + #:nocov: + + end + + elsif match = scan(/\/\*(?:.*?\*\/|\z)/m) + encoder.text_token match, :comment + + elsif match = scan(/\{/) + value_expected = false + encoder.text_token match, :operator + states.push :block + + elsif match = scan(/\}/) + value_expected = false + encoder.text_token match, :operator + if states.last == :block || states.last == :media + states.pop + end + + elsif match = scan(/#{RE::String}/o) + encoder.begin_group :string + encoder.text_token match[0, 1], :delimiter + encoder.text_token match[1..-2], :content if match.size > 2 + encoder.text_token match[-1, 1], :delimiter if match.size >= 2 + encoder.end_group :string + + elsif match = scan(/#{RE::Function}/o) + encoder.begin_group :function + start = match[/^\w+\(/] + encoder.text_token start, :delimiter + if match[-1] == ?) + encoder.text_token match[start.size..-2], :content if match.size > start.size + 1 + encoder.text_token ')', :delimiter + else + encoder.text_token match[start.size..-1], :content if match.size > start.size + end + encoder.end_group :function + + elsif match = scan(/(?: #{RE::Dimension} | #{RE::Percentage} | #{RE::Num} )/ox) + encoder.text_token match, :float + + elsif match = scan(/#{RE::HexColor}/o) + encoder.text_token match, :color + + elsif match = scan(/! *important/) + encoder.text_token match, :important + + elsif match = scan(/(?:rgb|hsl)a?\([^()\n]*\)?/) + encoder.text_token match, :color + + elsif match = scan(RE::AtKeyword) + encoder.text_token match, :directive + + elsif match = scan(/ [+>~:;,.=()\/] /x) + if match == ':' + value_expected = true + elsif match == ';' + value_expected = false + end + encoder.text_token match, :operator + + else + encoder.text_token getch, :error + + end + + end + + if options[:keep_state] + @state = states + @value_expected = value_expected + end + + encoder + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/debug.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/debug.rb new file mode 100644 index 0000000..83ede9a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/debug.rb @@ -0,0 +1,75 @@ +require 'set' + +module CodeRay +module Scanners + + # = Debug Scanner + # + # Interprets the output of the Encoders::Debug encoder (basically the inverse function). + class Debug < Scanner + + register_for :debug + title 'CodeRay Token Dump Import' + + protected + + def setup + super + @known_token_kinds = TokenKinds.keys.map(&:to_s).to_set + end + + def scan_tokens encoder, options + + opened_tokens = [] + + until eos? + + if match = scan(/\s+/) + encoder.text_token match, :space + + elsif match = scan(/ (\w+) \( ( [^\)\\]* ( \\. [^\)\\]* )* ) \)? /x) + if @known_token_kinds.include? self[1] + encoder.text_token self[2].gsub(/\\(.)/m, '\1'), self[1].to_sym + else + encoder.text_token matched, :unknown + end + + elsif match = scan(/ (\w+) ([<\[]) /x) + if @known_token_kinds.include? self[1] + kind = self[1].to_sym + else + kind = :unknown + end + + opened_tokens << kind + case self[2] + when '<' + encoder.begin_group kind + when '[' + encoder.begin_line kind + else + raise 'CodeRay bug: This case should not be reached.' + end + + elsif !opened_tokens.empty? && match = scan(/ > /x) + encoder.end_group opened_tokens.pop + + elsif !opened_tokens.empty? && match = scan(/ \] /x) + encoder.end_line opened_tokens.pop + + else + encoder.text_token getch, :space + + end + + end + + encoder.end_group opened_tokens.pop until opened_tokens.empty? + + encoder + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/delphi.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/delphi.rb new file mode 100644 index 0000000..b328155 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/delphi.rb @@ -0,0 +1,144 @@ +module CodeRay +module Scanners + + # Scanner for the Delphi language (Object Pascal). + # + # Alias: +pascal+ + class Delphi < Scanner + + register_for :delphi + file_extension 'pas' + + KEYWORDS = [ + 'and', 'array', 'as', 'at', 'asm', 'at', 'begin', 'case', 'class', + 'const', 'constructor', 'destructor', 'dispinterface', 'div', 'do', + 'downto', 'else', 'end', 'except', 'exports', 'file', 'finalization', + 'finally', 'for', 'function', 'goto', 'if', 'implementation', 'in', + 'inherited', 'initialization', 'inline', 'interface', 'is', 'label', + 'library', 'mod', 'nil', 'not', 'object', 'of', 'or', 'out', 'packed', + 'procedure', 'program', 'property', 'raise', 'record', 'repeat', + 'resourcestring', 'set', 'shl', 'shr', 'string', 'then', 'threadvar', + 'to', 'try', 'type', 'unit', 'until', 'uses', 'var', 'while', 'with', + 'xor', 'on', + ] # :nodoc: + + DIRECTIVES = [ + 'absolute', 'abstract', 'assembler', 'at', 'automated', 'cdecl', + 'contains', 'deprecated', 'dispid', 'dynamic', 'export', + 'external', 'far', 'forward', 'implements', 'local', + 'near', 'nodefault', 'on', 'overload', 'override', + 'package', 'pascal', 'platform', 'private', 'protected', 'public', + 'published', 'read', 'readonly', 'register', 'reintroduce', + 'requires', 'resident', 'safecall', 'stdcall', 'stored', 'varargs', + 'virtual', 'write', 'writeonly', + ] # :nodoc: + + IDENT_KIND = WordList::CaseIgnoring.new(:ident). + add(KEYWORDS, :keyword). + add(DIRECTIVES, :directive) # :nodoc: + + NAME_FOLLOWS = WordList::CaseIgnoring.new(false). + add(%w(procedure function .)) # :nodoc: + + protected + + def scan_tokens encoder, options + + state = :initial + last_token = '' + + until eos? + + if state == :initial + + if match = scan(/ \s+ /x) + encoder.text_token match, :space + next + + elsif match = scan(%r! \{ \$ [^}]* \}? | \(\* \$ (?: .*? \*\) | .* ) !mx) + encoder.text_token match, :preprocessor + next + + elsif match = scan(%r! // [^\n]* | \{ [^}]* \}? | \(\* (?: .*? \*\) | .* ) !mx) + encoder.text_token match, :comment + next + + elsif match = scan(/ <[>=]? | >=? | :=? | [-+=*\/;,@\^|\(\)\[\]] | \.\. /x) + encoder.text_token match, :operator + + elsif match = scan(/\./) + encoder.text_token match, :operator + next if last_token == 'end' + + elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x) + encoder.text_token match, NAME_FOLLOWS[last_token] ? :ident : IDENT_KIND[match] + + elsif match = skip(/ ' ( [^\n']|'' ) (?:'|$) /x) + encoder.begin_group :char + encoder.text_token "'", :delimiter + encoder.text_token self[1], :content + encoder.text_token "'", :delimiter + encoder.end_group :char + next + + elsif match = scan(/ ' /x) + encoder.begin_group :string + encoder.text_token match, :delimiter + state = :string + + elsif match = scan(/ \# (?: \d+ | \$[0-9A-Fa-f]+ ) /x) + encoder.text_token match, :char + + elsif match = scan(/ \$ [0-9A-Fa-f]+ /x) + encoder.text_token match, :hex + + elsif match = scan(/ (?: \d+ ) (?![eE]|\.[^.]) /x) + encoder.text_token match, :integer + + elsif match = scan(/ \d+ (?: \.\d+ (?: [eE][+-]? \d+ )? | [eE][+-]? \d+ ) /x) + encoder.text_token match, :float + + else + encoder.text_token getch, :error + next + + end + + elsif state == :string + if match = scan(/[^\n']+/) + encoder.text_token match, :content + elsif match = scan(/''/) + encoder.text_token match, :char + elsif match = scan(/'/) + encoder.text_token match, :delimiter + encoder.end_group :string + state = :initial + next + elsif match = scan(/\n/) + encoder.end_group :string + encoder.text_token match, :space + state = :initial + else + raise "else case \' reached; %p not handled." % peek(1), encoder + end + + else + raise 'else-case reached', encoder + + end + + last_token = match + + end + + if state == :string + encoder.end_group state + end + + encoder + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/diff.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/diff.rb new file mode 100644 index 0000000..a2a6fcc --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/diff.rb @@ -0,0 +1,221 @@ +module CodeRay +module Scanners + + # Scanner for output of the diff command. + # + # Alias: +patch+ + class Diff < Scanner + + register_for :diff + title 'diff output' + + DEFAULT_OPTIONS = { + :highlight_code => true, + :inline_diff => true, + } + + protected + + def scan_tokens encoder, options + + line_kind = nil + state = :initial + deleted_lines_count = 0 + scanners = Hash.new do |h, lang| + h[lang] = Scanners[lang].new '', :keep_tokens => true, :keep_state => true + end + content_scanner = scanners[:plain] + content_scanner_entry_state = nil + + until eos? + + if match = scan(/\n/) + deleted_lines_count = 0 unless line_kind == :delete + if line_kind + encoder.end_line line_kind + line_kind = nil + end + encoder.text_token match, :space + next + end + + case state + + when :initial + if match = scan(/--- |\+\+\+ |=+|_+/) + encoder.begin_line line_kind = :head + encoder.text_token match, :head + if match = scan(/[^\x00\n]+?(?=$|[\t\n]| \(revision)/) + encoder.text_token match, :filename + if options[:highlight_code] && match != '/dev/null' + file_type = CodeRay::FileType.fetch(match, :text) + file_type = :text if file_type == :diff + content_scanner = scanners[file_type] + content_scanner_entry_state = nil + end + end + next unless match = scan(/.+/) + encoder.text_token match, :plain + elsif match = scan(/Index: |Property changes on: /) + encoder.begin_line line_kind = :head + encoder.text_token match, :head + next unless match = scan(/.+/) + encoder.text_token match, :plain + elsif match = scan(/Added: /) + encoder.begin_line line_kind = :head + encoder.text_token match, :head + next unless match = scan(/.+/) + encoder.text_token match, :plain + state = :added + elsif match = scan(/\\ .*/) + encoder.text_token match, :comment + elsif match = scan(/@@(?>[^@\n]+)@@/) + content_scanner.state = :initial unless match?(/\n\+/) + content_scanner_entry_state = nil + if check(/\n|$/) + encoder.begin_line line_kind = :change + else + encoder.begin_group :change + end + encoder.text_token match[0,2], :change + encoder.text_token match[2...-2], :plain + encoder.text_token match[-2,2], :change + encoder.end_group :change unless line_kind + next unless match = scan(/.+/) + if options[:highlight_code] + content_scanner.tokenize match, :tokens => encoder + else + encoder.text_token match, :plain + end + next + elsif match = scan(/\+/) + encoder.begin_line line_kind = :insert + encoder.text_token match, :insert + next unless match = scan(/.+/) + if options[:highlight_code] + content_scanner.tokenize match, :tokens => encoder + else + encoder.text_token match, :plain + end + next + elsif match = scan(/-/) + deleted_lines_count += 1 + if options[:inline_diff] && deleted_lines_count == 1 && (changed_lines_count = 1 + check(/.*(?:\n\-.*)*/).count("\n")) && changed_lines_count <= 100_000 && match?(/(?>.*(?:\n\-.*){#{changed_lines_count - 1}}(?:\n\+.*){#{changed_lines_count}})$(?!\n\+)/) + deleted_lines = Array.new(changed_lines_count) { |i| skip(/\n\-/) if i > 0; scan(/.*/) } + inserted_lines = Array.new(changed_lines_count) { |i| skip(/\n\+/) ; scan(/.*/) } + + deleted_lines_tokenized = [] + inserted_lines_tokenized = [] + for deleted_line, inserted_line in deleted_lines.zip(inserted_lines) + pre, deleted_part, inserted_part, post = diff deleted_line, inserted_line + content_scanner_entry_state = content_scanner.state + deleted_lines_tokenized << content_scanner.tokenize([pre, deleted_part, post], :tokens => Tokens.new) + content_scanner.state = content_scanner_entry_state || :initial + inserted_lines_tokenized << content_scanner.tokenize([pre, inserted_part, post], :tokens => Tokens.new) + end + + for pre, deleted_part, post in deleted_lines_tokenized + encoder.begin_line :delete + encoder.text_token '-', :delete + encoder.tokens pre + unless deleted_part.empty? + encoder.begin_group :eyecatcher + encoder.tokens deleted_part + encoder.end_group :eyecatcher + end + encoder.tokens post + encoder.end_line :delete + encoder.text_token "\n", :space + end + + for pre, inserted_part, post in inserted_lines_tokenized + encoder.begin_line :insert + encoder.text_token '+', :insert + encoder.tokens pre + unless inserted_part.empty? + encoder.begin_group :eyecatcher + encoder.tokens inserted_part + encoder.end_group :eyecatcher + end + encoder.tokens post + changed_lines_count -= 1 + if changed_lines_count > 0 + encoder.end_line :insert + encoder.text_token "\n", :space + end + end + + line_kind = :insert + + elsif match = scan(/.*/) + encoder.begin_line line_kind = :delete + encoder.text_token '-', :delete + if options[:highlight_code] + if deleted_lines_count == 1 + content_scanner_entry_state = content_scanner.state + end + content_scanner.tokenize match, :tokens => encoder unless match.empty? + if !match?(/\n-/) + if match?(/\n\+/) + content_scanner.state = content_scanner_entry_state || :initial + end + content_scanner_entry_state = nil + end + else + encoder.text_token match, :plain + end + end + next + elsif match = scan(/ .*/) + if options[:highlight_code] + content_scanner.tokenize match, :tokens => encoder + else + encoder.text_token match, :plain + end + next + elsif match = scan(/.+/) + encoder.begin_line line_kind = :comment + encoder.text_token match, :plain + else + raise_inspect 'else case rached' + end + + when :added + if match = scan(/ \+/) + encoder.begin_line line_kind = :insert + encoder.text_token match, :insert + next unless match = scan(/.+/) + encoder.text_token match, :plain + else + state = :initial + next + end + end + + end + + encoder.end_line line_kind if line_kind + + encoder + end + + private + + def diff a, b + # i will be the index of the leftmost difference from the left. + i_max = [a.size, b.size].min + i = 0 + i += 1 while i < i_max && a[i] == b[i] + # j_min will be the index of the leftmost difference from the right. + j_min = i - i_max + # j will be the index of the rightmost difference from the right which + # does not precede the leftmost one from the left. + j = -1 + j -= 1 while j >= j_min && a[j] == b[j] + return a[0...i], a[i..j], b[i..j], (j < -1) ? a[j + 1..-1] : '' + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/erb.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/erb.rb new file mode 100644 index 0000000..727a993 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/erb.rb @@ -0,0 +1,81 @@ +module CodeRay +module Scanners + + load :html + load :ruby + + # Scanner for HTML ERB templates. + class ERB < Scanner + + register_for :erb + title 'HTML ERB Template' + + KINDS_NOT_LOC = HTML::KINDS_NOT_LOC + + ERB_RUBY_BLOCK = / + (<%(?!%)[-=\#]?) + ((?> + [^\-%]* # normal* + (?> # special + (?: %(?!>) | -(?!%>) ) + [^\-%]* # normal* + )* + )) + ((?: -?%> )?) + /x # :nodoc: + + START_OF_ERB = / + <%(?!%) + /x # :nodoc: + + protected + + def setup + @ruby_scanner = CodeRay.scanner :ruby, :tokens => @tokens, :keep_tokens => true + @html_scanner = CodeRay.scanner :html, :tokens => @tokens, :keep_tokens => true, :keep_state => true + end + + def reset_instance + super + @html_scanner.reset + end + + def scan_tokens encoder, options + + until eos? + + if (match = scan_until(/(?=#{START_OF_ERB})/o) || scan_rest) and not match.empty? + @html_scanner.tokenize match, :tokens => encoder + + elsif match = scan(/#{ERB_RUBY_BLOCK}/o) + start_tag = self[1] + code = self[2] + end_tag = self[3] + + encoder.begin_group :inline + encoder.text_token start_tag, :inline_delimiter + + if start_tag == '<%#' + encoder.text_token code, :comment + else + @ruby_scanner.tokenize code, :tokens => encoder + end unless code.empty? + + encoder.text_token end_tag, :inline_delimiter unless end_tag.empty? + encoder.end_group :inline + + else + raise_inspect 'else-case reached!', encoder + + end + + end + + encoder + + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/go.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/go.rb new file mode 100644 index 0000000..99fdd63 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/go.rb @@ -0,0 +1,208 @@ +module CodeRay +module Scanners + + class Go < Scanner + + register_for :go + file_extension 'go' + + # http://golang.org/ref/spec#Keywords + KEYWORDS = [ + 'break', 'default', 'func', 'interface', 'select', + 'case', 'defer', 'go', 'map', 'struct', + 'chan', 'else', 'goto', 'package', 'switch', + 'const', 'fallthrough', 'if', 'range', 'type', + 'continue', 'for', 'import', 'return', 'var', + ] # :nodoc: + + # http://golang.org/ref/spec#Types + PREDEFINED_TYPES = [ + 'bool', + 'uint8', 'uint16', 'uint32', 'uint64', + 'int8', 'int16', 'int32', 'int64', + 'float32', 'float64', + 'complex64', 'complex128', + 'byte', 'rune', 'string', 'error', + 'uint', 'int', 'uintptr', + ] # :nodoc: + + PREDEFINED_CONSTANTS = [ + 'nil', 'iota', + 'true', 'false', + ] # :nodoc: + + PREDEFINED_FUNCTIONS = %w[ + append cap close complex copy delete imag len + make new panic print println real recover + ] # :nodoc: + + IDENT_KIND = WordList.new(:ident). + add(KEYWORDS, :keyword). + add(PREDEFINED_TYPES, :predefined_type). + add(PREDEFINED_CONSTANTS, :predefined_constant). + add(PREDEFINED_FUNCTIONS, :predefined) # :nodoc: + + ESCAPE = / [rbfntv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x # :nodoc: + UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x # :nodoc: + + protected + + def scan_tokens encoder, options + + state = :initial + label_expected = true + case_expected = false + label_expected_before_preproc_line = nil + in_preproc_line = false + + until eos? + + case state + + when :initial + + if match = scan(/ \s+ | \\\n /x) + if in_preproc_line && match != "\\\n" && match.index(?\n) + in_preproc_line = false + case_expected = false + label_expected = label_expected_before_preproc_line + end + encoder.text_token match, :space + + elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx) + encoder.text_token match, :comment + + elsif match = scan(/ ?:;,!&^|()\[\]{}~%]+ | \/=? | \.(?!\d) /x) + if case_expected + label_expected = true if match == ':' + case_expected = false + end + encoder.text_token match, :operator + + elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x) + kind = IDENT_KIND[match] + if kind == :ident && label_expected && !in_preproc_line && scan(/:(?!:)/) + kind = :label + label_expected = false + match << matched + else + label_expected = false + if kind == :keyword + case match + when 'case', 'default' + case_expected = true + end + end + end + encoder.text_token match, kind + + elsif match = scan(/L?"/) + encoder.begin_group :string + if match[0] == ?L + encoder.text_token 'L', :modifier + match = '"' + end + encoder.text_token match, :delimiter + state = :string + + elsif match = scan(/ ` ([^`]+)? (`)? /x) + encoder.begin_group :shell + encoder.text_token '`', :delimiter + encoder.text_token self[1], :content if self[1] + encoder.text_token self[2], :delimiter if self[2] + encoder.end_group :shell + + elsif match = scan(/ \# \s* if \s* 0 /x) + match << scan_until(/ ^\# (?:elif|else|endif) .*? $ | \z /xm) unless eos? + encoder.text_token match, :comment + + elsif match = scan(/#[ \t]*(\w*)/) + encoder.text_token match, :preprocessor + in_preproc_line = true + label_expected_before_preproc_line = label_expected + state = :include_expected if self[1] == 'include' + + elsif match = scan(/ L?' (?: [^\'\n\\] | \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) )? '? /ox) + label_expected = false + encoder.text_token match, :char + + elsif match = scan(/\$/) + encoder.text_token match, :ident + + elsif match = scan(/-?\d*(\.\d*)?([eE][+-]?\d+)?i/) + label_expected = false + encoder.text_token match, :imaginary + + elsif match = scan(/-?0[xX][0-9A-Fa-f]+/) + label_expected = false + encoder.text_token match, :hex + + elsif match = scan(/-?(?:0[0-7]+)(?![89.eEfF])/) + label_expected = false + encoder.text_token match, :octal + + elsif match = scan(/-?(?:\d*\.\d+|\d+\.)(?:[eE][+-]?\d+)?|\d+[eE][+-]?\d+/) + label_expected = false + encoder.text_token match, :float + + elsif match = scan(/-?(?:\d+)(?![.eEfF])L?L?/) + label_expected = false + encoder.text_token match, :integer + + else + encoder.text_token getch, :error + + end + + when :string + if match = scan(/[^\\\n"]+/) + encoder.text_token match, :content + elsif match = scan(/"/) + encoder.text_token match, :delimiter + encoder.end_group :string + state = :initial + label_expected = false + elsif match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox) + encoder.text_token match, :char + elsif match = scan(/ \\ /x) + encoder.text_token match, :error + elsif match = scan(/$/) + encoder.end_group :string + state = :initial + label_expected = false + else + raise_inspect "else case \" reached; %p not handled." % peek(1), encoder + end + + when :include_expected + if match = scan(/<[^>\n]+>?|"[^"\n\\]*(?:\\.[^"\n\\]*)*"?/) + encoder.text_token match, :include + state = :initial + + elsif match = scan(/\s+/) + encoder.text_token match, :space + state = :initial if match.index ?\n + + else + state = :initial + + end + + else + raise_inspect 'Unknown state', encoder + + end + + end + + if state == :string + encoder.end_group :string + end + + encoder + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/groovy.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/groovy.rb new file mode 100644 index 0000000..c52ce8d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/groovy.rb @@ -0,0 +1,268 @@ +module CodeRay +module Scanners + + load :java + + # Scanner for Groovy. + class Groovy < Java + + register_for :groovy + + # TODO: check list of keywords + GROOVY_KEYWORDS = %w[ + as assert def in + ] # :nodoc: + KEYWORDS_EXPECTING_VALUE = WordList.new.add %w[ + case instanceof new return throw typeof while as assert in + ] # :nodoc: + GROOVY_MAGIC_VARIABLES = %w[ it ] # :nodoc: + + IDENT_KIND = Java::IDENT_KIND.dup. + add(GROOVY_KEYWORDS, :keyword). + add(GROOVY_MAGIC_VARIABLES, :local_variable) # :nodoc: + + ESCAPE = / [bfnrtv$\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x # :nodoc: + UNICODE_ESCAPE = / u[a-fA-F0-9]{4} /x # :nodoc: no 4-byte unicode chars? U[a-fA-F0-9]{8} + REGEXP_ESCAPE = / [bfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} | \d | [bBdDsSwW\/] /x # :nodoc: + + # TODO: interpretation inside ', ", / + STRING_CONTENT_PATTERN = { + "'" => /(?>\\[^\\'\n]+|[^\\'\n]+)+/, + '"' => /[^\\$"\n]+/, + "'''" => /(?>[^\\']+|'(?!''))+/, + '"""' => /(?>[^\\$"]+|"(?!""))+/, + '/' => /[^\\$\/\n]+/, + } # :nodoc: + + protected + + def setup + @state = :initial + end + + def scan_tokens encoder, options + state = options[:state] || @state + inline_block_stack = [] + inline_block_paren_depth = nil + string_delimiter = nil + import_clause = class_name_follows = last_token = after_def = false + value_expected = true + + until eos? + + case state + + when :initial + + if match = scan(/ \s+ | \\\n /x) + encoder.text_token match, :space + if match.index ?\n + import_clause = after_def = false + value_expected = true unless value_expected + end + next + + elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx) + value_expected = true + after_def = false + encoder.text_token match, :comment + + elsif bol? && match = scan(/ \#!.* /x) + encoder.text_token match, :doctype + + elsif import_clause && match = scan(/ (?!as) #{IDENT} (?: \. #{IDENT} )* (?: \.\* )? /ox) + after_def = value_expected = false + encoder.text_token match, :include + + elsif match = scan(/ #{IDENT} | \[\] /ox) + kind = IDENT_KIND[match] + value_expected = (kind == :keyword) && KEYWORDS_EXPECTING_VALUE[match] + if last_token == '.' + kind = :ident + elsif class_name_follows + kind = :class + class_name_follows = false + elsif after_def && check(/\s*[({]/) + kind = :method + after_def = false + elsif kind == :ident && last_token != '?' && check(/:/) + kind = :key + else + class_name_follows = true if match == 'class' || (import_clause && match == 'as') + import_clause = match == 'import' + after_def = true if match == 'def' + end + encoder.text_token match, kind + + elsif match = scan(/;/) + import_clause = after_def = false + value_expected = true + encoder.text_token match, :operator + + elsif match = scan(/\{/) + class_name_follows = after_def = false + value_expected = true + encoder.text_token match, :operator + if !inline_block_stack.empty? + inline_block_paren_depth += 1 + end + + # TODO: ~'...', ~"..." and ~/.../ style regexps + elsif match = scan(/ \.\.] | \+\+ | + && | \|\| | \*\*=? | ==?~ | <=?>? | [-+*%^~&|>=!]=? | <<>>?=? /x) + value_expected = true + value_expected = :regexp if match == '~' + after_def = false + encoder.text_token match, :operator + + elsif match = scan(/ [)\]}] /x) + value_expected = after_def = false + if !inline_block_stack.empty? && match == '}' + inline_block_paren_depth -= 1 + if inline_block_paren_depth == 0 # closing brace of inline block reached + encoder.text_token match, :inline_delimiter + encoder.end_group :inline + state, string_delimiter, inline_block_paren_depth = inline_block_stack.pop + next + end + end + encoder.text_token match, :operator + + elsif check(/[\d.]/) + after_def = value_expected = false + if match = scan(/0[xX][0-9A-Fa-f]+/) + encoder.text_token match, :hex + elsif match = scan(/(?>0[0-7]+)(?![89.eEfF])/) + encoder.text_token match, :octal + elsif match = scan(/\d+[fFdD]|\d*\.\d+(?:[eE][+-]?\d+)?[fFdD]?|\d+[eE][+-]?\d+[fFdD]?/) + encoder.text_token match, :float + elsif match = scan(/\d+[lLgG]?/) + encoder.text_token match, :integer + end + + elsif match = scan(/'''|"""/) + after_def = value_expected = false + state = :multiline_string + encoder.begin_group :string + string_delimiter = match + encoder.text_token match, :delimiter + + # TODO: record.'name' syntax + elsif match = scan(/["']/) + after_def = value_expected = false + state = match == '/' ? :regexp : :string + encoder.begin_group state + string_delimiter = match + encoder.text_token match, :delimiter + + elsif value_expected && match = scan(/\//) + after_def = value_expected = false + encoder.begin_group :regexp + state = :regexp + string_delimiter = '/' + encoder.text_token match, :delimiter + + elsif match = scan(/ @ #{IDENT} /ox) + after_def = value_expected = false + encoder.text_token match, :annotation + + elsif match = scan(/\//) + after_def = false + value_expected = true + encoder.text_token match, :operator + + else + encoder.text_token getch, :error + + end + + when :string, :regexp, :multiline_string + if match = scan(STRING_CONTENT_PATTERN[string_delimiter]) + encoder.text_token match, :content + + elsif match = scan(state == :multiline_string ? /'''|"""/ : /["'\/]/) + encoder.text_token match, :delimiter + if state == :regexp + # TODO: regexp modifiers? s, m, x, i? + modifiers = scan(/[ix]+/) + encoder.text_token modifiers, :modifier if modifiers && !modifiers.empty? + end + state = :string if state == :multiline_string + encoder.end_group state + string_delimiter = nil + after_def = value_expected = false + state = :initial + next + + elsif (state == :string || state == :multiline_string) && + (match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)) + if string_delimiter[0] == ?' && !(match == "\\\\" || match == "\\'") + encoder.text_token match, :content + else + encoder.text_token match, :char + end + elsif state == :regexp && match = scan(/ \\ (?: #{REGEXP_ESCAPE} | #{UNICODE_ESCAPE} ) /mox) + encoder.text_token match, :char + + elsif match = scan(/ \$ #{IDENT} /mox) + encoder.begin_group :inline + encoder.text_token '$', :inline_delimiter + match = match[1..-1] + encoder.text_token match, IDENT_KIND[match] + encoder.end_group :inline + next + elsif match = scan(/ \$ \{ /x) + encoder.begin_group :inline + encoder.text_token match, :inline_delimiter + inline_block_stack << [state, string_delimiter, inline_block_paren_depth] + inline_block_paren_depth = 1 + state = :initial + next + + elsif match = scan(/ \$ /mx) + encoder.text_token match, :content + + elsif match = scan(/ \\. /mx) + encoder.text_token match, :content # TODO: Shouldn't this be :error? + + elsif match = scan(/ \\ | \n /x) + encoder.end_group state == :regexp ? :regexp : :string + encoder.text_token match, :error + after_def = value_expected = false + state = :initial + + else + raise_inspect "else case \" reached; %p not handled." % peek(1), encoder + + end + + else + raise_inspect 'Unknown state', encoder + + end + + last_token = match unless [:space, :comment, :doctype].include? kind + + end + + if [:multiline_string, :string, :regexp].include? state + encoder.end_group state == :regexp ? :regexp : :string + end + + if options[:keep_state] + @state = state + end + + until inline_block_stack.empty? + state, = *inline_block_stack.pop + encoder.end_group :inline + encoder.end_group state == :regexp ? :regexp : :string + end + + encoder + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/haml.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/haml.rb new file mode 100644 index 0000000..d516ba9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/haml.rb @@ -0,0 +1,168 @@ +module CodeRay +module Scanners + + load :ruby + load :html + load :java_script + + class HAML < Scanner + + register_for :haml + title 'HAML Template' + + KINDS_NOT_LOC = HTML::KINDS_NOT_LOC + + protected + + def setup + super + @ruby_scanner = CodeRay.scanner :ruby, :tokens => @tokens, :keep_tokens => true + @embedded_ruby_scanner = CodeRay.scanner :ruby, :tokens => @tokens, :keep_tokens => true, :state => @ruby_scanner.interpreted_string_state + @html_scanner = CodeRay.scanner :html, :tokens => @tokens, :keep_tokens => true + end + + def scan_tokens encoder, options + + match = nil + code = '' + + until eos? + + if bol? + if match = scan(/!!!.*/) + encoder.text_token match, :doctype + next + end + + if match = scan(/(?>( *)(\/(?!\[if)|-\#|:javascript|:ruby|:\w+) *)(?=\n)/) + encoder.text_token match, :comment + + code = self[2] + if match = scan(/(?:\n+#{self[1]} .*)+/) + case code + when '/', '-#' + encoder.text_token match, :comment + when ':javascript' + # TODO: recognize #{...} snippets inside JavaScript + @java_script_scanner ||= CodeRay.scanner :java_script, :tokens => @tokens, :keep_tokens => true + @java_script_scanner.tokenize match, :tokens => encoder + when ':ruby' + @ruby_scanner.tokenize match, :tokens => encoder + when /:\w+/ + encoder.text_token match, :comment + else + raise 'else-case reached: %p' % [code] + end + end + end + + if match = scan(/ +/) + encoder.text_token match, :space + end + + if match = scan(/\/.*/) + encoder.text_token match, :comment + next + end + + if match = scan(/\\/) + encoder.text_token match, :plain + if match = scan(/.+/) + @html_scanner.tokenize match, :tokens => encoder + end + next + end + + tag = false + + if match = scan(/%[-\w:]+\/?/) + encoder.text_token match, :tag + # if match = scan(/( +)(.+)/) + # encoder.text_token self[1], :space + # @embedded_ruby_scanner.tokenize self[2], :tokens => encoder + # end + tag = true + end + + while match = scan(/([.#])[-\w]*\w/) + encoder.text_token match, self[1] == '#' ? :constant : :class + tag = true + end + + if tag && match = scan(/(\()([^)]+)?(\))?/) + # TODO: recognize title=@title, class="widget_#{@widget.number}" + encoder.text_token self[1], :plain + @html_scanner.tokenize self[2], :tokens => encoder, :state => :attribute if self[2] + encoder.text_token self[3], :plain if self[3] + end + + if tag && match = scan(/\{/) + encoder.text_token match, :plain + + code = '' + level = 1 + while true + code << scan(/([^\{\},\n]|, *\n?)*/) + case match = getch + when '{' + level += 1 + code << match + when '}' + level -= 1 + if level > 0 + code << match + else + break + end + when "\n", ",", nil + break + end + end + @ruby_scanner.tokenize code, :tokens => encoder unless code.empty? + + encoder.text_token match, :plain if match + end + + if tag && match = scan(/(\[)([^\]\n]+)?(\])?/) + encoder.text_token self[1], :plain + @ruby_scanner.tokenize self[2], :tokens => encoder if self[2] + encoder.text_token self[3], :plain if self[3] + end + + if tag && match = scan(/\//) + encoder.text_token match, :tag + end + + if scan(/(>? encoder + else + @ruby_scanner.tokenize self[4], :tokens => encoder + end + end + elsif match = scan(/((?:<|> encoder if self[2] + end + + elsif match = scan(/.+/) + @html_scanner.tokenize match, :tokens => encoder + + end + + if match = scan(/\n/) + encoder.text_token match, :space + end + end + + encoder + + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/html.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/html.rb new file mode 100644 index 0000000..ebe7b01 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/html.rb @@ -0,0 +1,275 @@ +module CodeRay +module Scanners + + # HTML Scanner + # + # Alias: +xhtml+ + # + # See also: Scanners::XML + class HTML < Scanner + + register_for :html + + KINDS_NOT_LOC = [ + :comment, :doctype, :preprocessor, + :tag, :attribute_name, :operator, + :attribute_value, :string, + :plain, :entity, :error, + ] # :nodoc: + + EVENT_ATTRIBUTES = %w( + onabort onafterprint onbeforeprint onbeforeunload onblur oncanplay + oncanplaythrough onchange onclick oncontextmenu oncuechange ondblclick + ondrag ondragdrop ondragend ondragenter ondragleave ondragover + ondragstart ondrop ondurationchange onemptied onended onerror onfocus + onformchange onforminput onhashchange oninput oninvalid onkeydown + onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart + onmessage onmousedown onmousemove onmouseout onmouseover onmouseup + onmousewheel onmove onoffline ononline onpagehide onpageshow onpause + onplay onplaying onpopstate onprogress onratechange onreadystatechange + onredo onreset onresize onscroll onseeked onseeking onselect onshow + onstalled onstorage onsubmit onsuspend ontimeupdate onundo onunload + onvolumechange onwaiting + ) + + IN_ATTRIBUTE = WordList::CaseIgnoring.new(nil). + add(EVENT_ATTRIBUTES, :script). + add(['style'], :style) + + ATTR_NAME = /[\w.:-]+/ # :nodoc: + TAG_END = /\/?>/ # :nodoc: + HEX = /[0-9a-fA-F]/ # :nodoc: + ENTITY = / + & + (?: + \w+ + | + \# + (?: + \d+ + | + x#{HEX}+ + ) + ) + ; + /ox # :nodoc: + + PLAIN_STRING_CONTENT = { + "'" => /[^&'>\n]+/, + '"' => /[^&">\n]+/, + } # :nodoc: + + def reset + super + @state = :initial + @plain_string_content = nil + end + + protected + + def setup + @state = :initial + @plain_string_content = nil + @in_tag = nil + end + + def scan_java_script encoder, code + if code && !code.empty? + @java_script_scanner ||= Scanners::JavaScript.new '', :keep_tokens => true + @java_script_scanner.tokenize code, :tokens => encoder + end + end + + def scan_css encoder, code, state = [:initial] + if code && !code.empty? + @css_scanner ||= Scanners::CSS.new '', :keep_tokens => true + @css_scanner.tokenize code, :tokens => encoder, :state => state + end + end + + def scan_tokens encoder, options + state = options[:state] || @state + plain_string_content = @plain_string_content + in_tag = @in_tag + in_attribute = nil + + encoder.begin_group :string if state == :attribute_value_string + + until eos? + + if state != :in_special_tag && match = scan(/\s+/m) + encoder.text_token match, :space + + else + + case state + + when :initial + if match = scan(//m) + encoder.text_token match[0..-4], :plain + encoder.text_token ']]>', :inline_delimiter + elsif match = scan(/.+/) + encoder.text_token match, :error + end + elsif match = scan(/|.*)/m) + encoder.text_token match, :comment + elsif match = scan(/|.*)|\]>/m) + encoder.text_token match, :doctype + elsif match = scan(/<\?xml(?:.*?\?>|.*)/m) + encoder.text_token match, :preprocessor + elsif match = scan(/<\?(?:.*?\?>|.*)/m) + encoder.text_token match, :comment + elsif match = scan(/<\/[-\w.:]*>?/m) + in_tag = nil + encoder.text_token match, :tag + elsif match = scan(/<(?:(script|style)|[-\w.:]+)(>)?/m) + encoder.text_token match, :tag + in_tag = self[1] + if self[2] + state = :in_special_tag if in_tag + else + state = :attribute + end + elsif match = scan(/[^<>&]+/) + encoder.text_token match, :plain + elsif match = scan(/#{ENTITY}/ox) + encoder.text_token match, :entity + elsif match = scan(/[<>&]/) + in_tag = nil + encoder.text_token match, :error + else + raise_inspect '[BUG] else-case reached with state %p' % [state], encoder + end + + when :attribute + if match = scan(/#{TAG_END}/o) + encoder.text_token match, :tag + in_attribute = nil + if in_tag + state = :in_special_tag + else + state = :initial + end + elsif match = scan(/#{ATTR_NAME}/o) + in_attribute = IN_ATTRIBUTE[match] + encoder.text_token match, :attribute_name + state = :attribute_equal + else + in_tag = nil + encoder.text_token getch, :error + end + + when :attribute_equal + if match = scan(/=/) #/ + encoder.text_token match, :operator + state = :attribute_value + else + state = :attribute + next + end + + when :attribute_value + if match = scan(/#{ATTR_NAME}/o) + encoder.text_token match, :attribute_value + state = :attribute + elsif match = scan(/["']/) + if in_attribute == :script || in_attribute == :style + encoder.begin_group :string + encoder.text_token match, :delimiter + if scan(/javascript:[ \t]*/) + encoder.text_token matched, :comment + end + code = scan_until(match == '"' ? /(?="|\z)/ : /(?='|\z)/) + if in_attribute == :script + scan_java_script encoder, code + else + scan_css encoder, code, [:block] + end + match = scan(/["']/) + encoder.text_token match, :delimiter if match + encoder.end_group :string + state = :attribute + in_attribute = nil + else + encoder.begin_group :string + state = :attribute_value_string + plain_string_content = PLAIN_STRING_CONTENT[match] + encoder.text_token match, :delimiter + end + elsif match = scan(/#{TAG_END}/o) + encoder.text_token match, :tag + state = :initial + else + encoder.text_token getch, :error + end + + when :attribute_value_string + if match = scan(plain_string_content) + encoder.text_token match, :content + elsif match = scan(/['"]/) + encoder.text_token match, :delimiter + encoder.end_group :string + state = :attribute + elsif match = scan(/#{ENTITY}/ox) + encoder.text_token match, :entity + elsif match = scan(/&/) + encoder.text_token match, :content + elsif match = scan(/[\n>]/) + encoder.end_group :string + state = :initial + encoder.text_token match, :error + end + + when :in_special_tag + case in_tag + when 'script', 'style' + encoder.text_token match, :space if match = scan(/[ \t]*\n/) + if scan(/(\s*)|(.*))/m) + code = self[2] || self[4] + closing = self[3] + encoder.text_token self[1], :comment + else + code = scan_until(/(?=(?:\n\s*)?<\/#{in_tag}>)|\z/) + closing = false + end + unless code.empty? + encoder.begin_group :inline + if in_tag == 'script' + scan_java_script encoder, code + else + scan_css encoder, code + end + encoder.end_group :inline + end + encoder.text_token closing, :comment if closing + state = :initial + else + raise 'unknown special tag: %p' % [in_tag] + end + + else + raise_inspect 'Unknown state: %p' % [state], encoder + + end + + end + + end + + if options[:keep_state] + @state = state + @plain_string_content = plain_string_content + @in_tag = in_tag + end + + encoder.end_group :string if state == :attribute_value_string + + encoder + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/java.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/java.rb new file mode 100644 index 0000000..7dd1919 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/java.rb @@ -0,0 +1,174 @@ +module CodeRay +module Scanners + + # Scanner for Java. + class Java < Scanner + + register_for :java + + autoload :BuiltinTypes, CodeRay.coderay_path('scanners', 'java', 'builtin_types') + + # http://java.sun.com/docs/books/tutorial/java/nutsandbolts/_keywords.html + KEYWORDS = %w[ + assert break case catch continue default do else + finally for if instanceof import new package + return switch throw try typeof while + debugger export + ] # :nodoc: + RESERVED = %w[ const goto ] # :nodoc: + CONSTANTS = %w[ false null true ] # :nodoc: + MAGIC_VARIABLES = %w[ this super ] # :nodoc: + TYPES = %w[ + boolean byte char class double enum float int interface long + short void var + ] << '[]' # :nodoc: because int[] should be highlighted as a type + DIRECTIVES = %w[ + abstract extends final implements native private protected public + static strictfp synchronized throws transient volatile + ] # :nodoc: + + IDENT_KIND = WordList.new(:ident). + add(KEYWORDS, :keyword). + add(RESERVED, :reserved). + add(CONSTANTS, :predefined_constant). + add(MAGIC_VARIABLES, :local_variable). + add(TYPES, :type). + add(BuiltinTypes::List, :predefined_type). + add(BuiltinTypes::List.select { |builtin| builtin[/(Error|Exception)$/] }, :exception). + add(DIRECTIVES, :directive) # :nodoc: + + ESCAPE = / [bfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x # :nodoc: + UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x # :nodoc: + STRING_CONTENT_PATTERN = { + "'" => /[^\\']+/, + '"' => /[^\\"]+/, + '/' => /[^\\\/]+/, + } # :nodoc: + IDENT = RUBY_VERSION < '1.9' ? /[a-zA-Z_][A-Za-z_0-9]*/ : Regexp.new('[[[:alpha:]]_][[[:alnum:]]_]*') # :nodoc: + + protected + + def scan_tokens encoder, options + + state = :initial + string_delimiter = nil + package_name_expected = false + class_name_follows = false + last_token_dot = false + + until eos? + + case state + + when :initial + + if match = scan(/ \s+ | \\\n /x) + encoder.text_token match, :space + next + + elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx) + encoder.text_token match, :comment + next + + elsif package_name_expected && match = scan(/ #{IDENT} (?: \. #{IDENT} )* /ox) + encoder.text_token match, package_name_expected + + elsif match = scan(/ #{IDENT} | \[\] /ox) + kind = IDENT_KIND[match] + if last_token_dot + kind = :ident + elsif class_name_follows + kind = :class + class_name_follows = false + else + case match + when 'import' + package_name_expected = :include + when 'package' + package_name_expected = :namespace + when 'class', 'interface' + class_name_follows = true + end + end + encoder.text_token match, kind + + elsif match = scan(/ \.(?!\d) | [,?:()\[\]}] | -- | \+\+ | && | \|\| | \*\*=? | [-+*\/%^~&|<>=!]=? | <<>>?=? /x) + encoder.text_token match, :operator + + elsif match = scan(/;/) + package_name_expected = false + encoder.text_token match, :operator + + elsif match = scan(/\{/) + class_name_follows = false + encoder.text_token match, :operator + + elsif check(/[\d.]/) + if match = scan(/0[xX][0-9A-Fa-f]+/) + encoder.text_token match, :hex + elsif match = scan(/(?>0[0-7]+)(?![89.eEfF])/) + encoder.text_token match, :octal + elsif match = scan(/\d+[fFdD]|\d*\.\d+(?:[eE][+-]?\d+)?[fFdD]?|\d+[eE][+-]?\d+[fFdD]?/) + encoder.text_token match, :float + elsif match = scan(/\d+[lL]?/) + encoder.text_token match, :integer + end + + elsif match = scan(/["']/) + state = :string + encoder.begin_group state + string_delimiter = match + encoder.text_token match, :delimiter + + elsif match = scan(/ @ #{IDENT} /ox) + encoder.text_token match, :annotation + + else + encoder.text_token getch, :error + + end + + when :string + if match = scan(STRING_CONTENT_PATTERN[string_delimiter]) + encoder.text_token match, :content + elsif match = scan(/["'\/]/) + encoder.text_token match, :delimiter + encoder.end_group state + state = :initial + string_delimiter = nil + elsif state == :string && (match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)) + if string_delimiter == "'" && !(match == "\\\\" || match == "\\'") + encoder.text_token match, :content + else + encoder.text_token match, :char + end + elsif match = scan(/\\./m) + encoder.text_token match, :content + elsif match = scan(/ \\ | $ /x) + encoder.end_group state + state = :initial + encoder.text_token match, :error unless match.empty? + else + raise_inspect "else case \" reached; %p not handled." % peek(1), encoder + end + + else + raise_inspect 'Unknown state', encoder + + end + + last_token_dot = match == '.' + + end + + if state == :string + encoder.end_group state + end + + encoder + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/java/builtin_types.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/java/builtin_types.rb new file mode 100644 index 0000000..d1b8b73 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/java/builtin_types.rb @@ -0,0 +1,421 @@ +module CodeRay +module Scanners + + module Java::BuiltinTypes # :nodoc: + + #:nocov: + List = %w[ + AbstractAction AbstractBorder AbstractButton AbstractCellEditor AbstractCollection + AbstractColorChooserPanel AbstractDocument AbstractExecutorService AbstractInterruptibleChannel + AbstractLayoutCache AbstractList AbstractListModel AbstractMap AbstractMethodError AbstractPreferences + AbstractQueue AbstractQueuedSynchronizer AbstractSelectableChannel AbstractSelectionKey AbstractSelector + AbstractSequentialList AbstractSet AbstractSpinnerModel AbstractTableModel AbstractUndoableEdit + AbstractWriter AccessControlContext AccessControlException AccessController AccessException Accessible + AccessibleAction AccessibleAttributeSequence AccessibleBundle AccessibleComponent AccessibleContext + AccessibleEditableText AccessibleExtendedComponent AccessibleExtendedTable AccessibleExtendedText + AccessibleHyperlink AccessibleHypertext AccessibleIcon AccessibleKeyBinding AccessibleObject + AccessibleRelation AccessibleRelationSet AccessibleResourceBundle AccessibleRole AccessibleSelection + AccessibleState AccessibleStateSet AccessibleStreamable AccessibleTable AccessibleTableModelChange + AccessibleText AccessibleTextSequence AccessibleValue AccountException AccountExpiredException + AccountLockedException AccountNotFoundException Acl AclEntry AclNotFoundException Action ActionEvent + ActionListener ActionMap ActionMapUIResource Activatable ActivateFailedException ActivationDesc + ActivationException ActivationGroup ActivationGroupDesc ActivationGroupID ActivationGroup_Stub + ActivationID ActivationInstantiator ActivationMonitor ActivationSystem Activator ActiveEvent + ActivityCompletedException ActivityRequiredException Adjustable AdjustmentEvent AdjustmentListener + Adler32 AffineTransform AffineTransformOp AlgorithmParameterGenerator AlgorithmParameterGeneratorSpi + AlgorithmParameters AlgorithmParameterSpec AlgorithmParametersSpi AllPermission AlphaComposite + AlreadyBoundException AlreadyConnectedException AncestorEvent AncestorListener AnnotatedElement + Annotation AnnotationFormatError AnnotationTypeMismatchException AppConfigurationEntry Appendable Applet + AppletContext AppletInitializer AppletStub Arc2D Area AreaAveragingScaleFilter ArithmeticException Array + ArrayBlockingQueue ArrayIndexOutOfBoundsException ArrayList Arrays ArrayStoreException ArrayType + AssertionError AsyncBoxView AsynchronousCloseException AtomicBoolean AtomicInteger AtomicIntegerArray + AtomicIntegerFieldUpdater AtomicLong AtomicLongArray AtomicLongFieldUpdater AtomicMarkableReference + AtomicReference AtomicReferenceArray AtomicReferenceFieldUpdater AtomicStampedReference Attribute + AttributeChangeNotification AttributeChangeNotificationFilter AttributedCharacterIterator + AttributedString AttributeException AttributeInUseException AttributeList AttributeModificationException + AttributeNotFoundException Attributes AttributeSet AttributeSetUtilities AttributeValueExp AudioClip + AudioFileFormat AudioFileReader AudioFileWriter AudioFormat AudioInputStream AudioPermission AudioSystem + AuthenticationException AuthenticationNotSupportedException Authenticator AuthorizeCallback + AuthPermission AuthProvider Autoscroll AWTError AWTEvent AWTEventListener AWTEventListenerProxy + AWTEventMulticaster AWTException AWTKeyStroke AWTPermission BackingStoreException + BadAttributeValueExpException BadBinaryOpValueExpException BadLocationException BadPaddingException + BadStringOperationException BandCombineOp BandedSampleModel BaseRowSet BasicArrowButton BasicAttribute + BasicAttributes BasicBorders BasicButtonListener BasicButtonUI BasicCheckBoxMenuItemUI BasicCheckBoxUI + BasicColorChooserUI BasicComboBoxEditor BasicComboBoxRenderer BasicComboBoxUI BasicComboPopup + BasicControl BasicDesktopIconUI BasicDesktopPaneUI BasicDirectoryModel BasicEditorPaneUI + BasicFileChooserUI BasicFormattedTextFieldUI BasicGraphicsUtils BasicHTML BasicIconFactory + BasicInternalFrameTitlePane BasicInternalFrameUI BasicLabelUI BasicListUI BasicLookAndFeel + BasicMenuBarUI BasicMenuItemUI BasicMenuUI BasicOptionPaneUI BasicPanelUI BasicPasswordFieldUI + BasicPermission BasicPopupMenuSeparatorUI BasicPopupMenuUI BasicProgressBarUI BasicRadioButtonMenuItemUI + BasicRadioButtonUI BasicRootPaneUI BasicScrollBarUI BasicScrollPaneUI BasicSeparatorUI BasicSliderUI + BasicSpinnerUI BasicSplitPaneDivider BasicSplitPaneUI BasicStroke BasicTabbedPaneUI BasicTableHeaderUI + BasicTableUI BasicTextAreaUI BasicTextFieldUI BasicTextPaneUI BasicTextUI BasicToggleButtonUI + BasicToolBarSeparatorUI BasicToolBarUI BasicToolTipUI BasicTreeUI BasicViewportUI BatchUpdateException + BeanContext BeanContextChild BeanContextChildComponentProxy BeanContextChildSupport + BeanContextContainerProxy BeanContextEvent BeanContextMembershipEvent BeanContextMembershipListener + BeanContextProxy BeanContextServiceAvailableEvent BeanContextServiceProvider + BeanContextServiceProviderBeanInfo BeanContextServiceRevokedEvent BeanContextServiceRevokedListener + BeanContextServices BeanContextServicesListener BeanContextServicesSupport BeanContextSupport + BeanDescriptor BeanInfo Beans BevelBorder Bidi BigDecimal BigInteger BinaryRefAddr BindException Binding + BitSet Blob BlockingQueue BlockView BMPImageWriteParam Book Boolean BooleanControl Border BorderFactory + BorderLayout BorderUIResource BoundedRangeModel Box BoxLayout BoxView BreakIterator + BrokenBarrierException Buffer BufferCapabilities BufferedImage BufferedImageFilter BufferedImageOp + BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter BufferOverflowException + BufferStrategy BufferUnderflowException Button ButtonGroup ButtonModel ButtonUI Byte + ByteArrayInputStream ByteArrayOutputStream ByteBuffer ByteChannel ByteLookupTable ByteOrder CachedRowSet + CacheRequest CacheResponse Calendar Callable CallableStatement Callback CallbackHandler + CancelablePrintJob CancellationException CancelledKeyException CannotProceedException + CannotRedoException CannotUndoException Canvas CardLayout Caret CaretEvent CaretListener CellEditor + CellEditorListener CellRendererPane Certificate CertificateEncodingException CertificateException + CertificateExpiredException CertificateFactory CertificateFactorySpi CertificateNotYetValidException + CertificateParsingException CertPath CertPathBuilder CertPathBuilderException CertPathBuilderResult + CertPathBuilderSpi CertPathParameters CertPathTrustManagerParameters CertPathValidator + CertPathValidatorException CertPathValidatorResult CertPathValidatorSpi CertSelector CertStore + CertStoreException CertStoreParameters CertStoreSpi ChangedCharSetException ChangeEvent ChangeListener + Channel Channels Character CharacterCodingException CharacterIterator CharArrayReader CharArrayWriter + CharBuffer CharConversionException CharSequence Charset CharsetDecoder CharsetEncoder CharsetProvider + Checkbox CheckboxGroup CheckboxMenuItem CheckedInputStream CheckedOutputStream Checksum Choice + ChoiceCallback ChoiceFormat Chromaticity Cipher CipherInputStream CipherOutputStream CipherSpi Class + ClassCastException ClassCircularityError ClassDefinition ClassDesc ClassFileTransformer ClassFormatError + ClassLoader ClassLoaderRepository ClassLoadingMXBean ClassNotFoundException Clip Clipboard + ClipboardOwner Clob Cloneable CloneNotSupportedException Closeable ClosedByInterruptException + ClosedChannelException ClosedSelectorException CMMException CoderMalfunctionError CoderResult CodeSigner + CodeSource CodingErrorAction CollationElementIterator CollationKey Collator Collection + CollectionCertStoreParameters Collections Color ColorChooserComponentFactory ColorChooserUI + ColorConvertOp ColorModel ColorSelectionModel ColorSpace ColorSupported ColorType ColorUIResource + ComboBoxEditor ComboBoxModel ComboBoxUI ComboPopup CommunicationException Comparable Comparator + CompilationMXBean Compiler CompletionService Component ComponentAdapter ComponentColorModel + ComponentEvent ComponentInputMap ComponentInputMapUIResource ComponentListener ComponentOrientation + ComponentSampleModel ComponentUI ComponentView Composite CompositeContext CompositeData + CompositeDataSupport CompositeName CompositeType CompositeView CompoundBorder CompoundControl + CompoundEdit CompoundName Compression ConcurrentHashMap ConcurrentLinkedQueue ConcurrentMap + ConcurrentModificationException Condition Configuration ConfigurationException ConfirmationCallback + ConnectException ConnectIOException Connection ConnectionEvent ConnectionEventListener + ConnectionPendingException ConnectionPoolDataSource ConsoleHandler Constructor Container + ContainerAdapter ContainerEvent ContainerListener ContainerOrderFocusTraversalPolicy ContentHandler + ContentHandlerFactory ContentModel Context ContextNotEmptyException ContextualRenderedImageFactory + Control ControlFactory ControllerEventListener ConvolveOp CookieHandler Copies CopiesSupported + CopyOnWriteArrayList CopyOnWriteArraySet CountDownLatch CounterMonitor CounterMonitorMBean CRC32 + CredentialException CredentialExpiredException CredentialNotFoundException CRL CRLException CRLSelector + CropImageFilter CSS CubicCurve2D Currency Cursor Customizer CyclicBarrier DatabaseMetaData DataBuffer + DataBufferByte DataBufferDouble DataBufferFloat DataBufferInt DataBufferShort DataBufferUShort + DataFlavor DataFormatException DatagramChannel DatagramPacket DatagramSocket DatagramSocketImpl + DatagramSocketImplFactory DataInput DataInputStream DataLine DataOutput DataOutputStream DataSource + DataTruncation DatatypeConfigurationException DatatypeConstants DatatypeFactory Date DateFormat + DateFormatSymbols DateFormatter DateTimeAtCompleted DateTimeAtCreation DateTimeAtProcessing + DateTimeSyntax DebugGraphics DecimalFormat DecimalFormatSymbols DefaultBoundedRangeModel + DefaultButtonModel DefaultCaret DefaultCellEditor DefaultColorSelectionModel DefaultComboBoxModel + DefaultDesktopManager DefaultEditorKit DefaultFocusManager DefaultFocusTraversalPolicy DefaultFormatter + DefaultFormatterFactory DefaultHighlighter DefaultKeyboardFocusManager DefaultListCellRenderer + DefaultListModel DefaultListSelectionModel DefaultLoaderRepository DefaultMenuLayout DefaultMetalTheme + DefaultMutableTreeNode DefaultPersistenceDelegate DefaultSingleSelectionModel DefaultStyledDocument + DefaultTableCellRenderer DefaultTableColumnModel DefaultTableModel DefaultTextUI DefaultTreeCellEditor + DefaultTreeCellRenderer DefaultTreeModel DefaultTreeSelectionModel Deflater DeflaterOutputStream Delayed + DelayQueue DelegationPermission Deprecated Descriptor DescriptorAccess DescriptorSupport DESedeKeySpec + DesignMode DESKeySpec DesktopIconUI DesktopManager DesktopPaneUI Destination Destroyable + DestroyFailedException DGC DHGenParameterSpec DHKey DHParameterSpec DHPrivateKey DHPrivateKeySpec + DHPublicKey DHPublicKeySpec Dialog Dictionary DigestException DigestInputStream DigestOutputStream + Dimension Dimension2D DimensionUIResource DirContext DirectColorModel DirectoryManager DirObjectFactory + DirStateFactory DisplayMode DnDConstants Doc DocAttribute DocAttributeSet DocFlavor DocPrintJob Document + DocumentBuilder DocumentBuilderFactory Documented DocumentEvent DocumentFilter DocumentListener + DocumentName DocumentParser DomainCombiner DOMLocator DOMResult DOMSource Double DoubleBuffer + DragGestureEvent DragGestureListener DragGestureRecognizer DragSource DragSourceAdapter + DragSourceContext DragSourceDragEvent DragSourceDropEvent DragSourceEvent DragSourceListener + DragSourceMotionListener Driver DriverManager DriverPropertyInfo DropTarget DropTargetAdapter + DropTargetContext DropTargetDragEvent DropTargetDropEvent DropTargetEvent DropTargetListener DSAKey + DSAKeyPairGenerator DSAParameterSpec DSAParams DSAPrivateKey DSAPrivateKeySpec DSAPublicKey + DSAPublicKeySpec DTD DTDConstants DuplicateFormatFlagsException Duration DynamicMBean ECField ECFieldF2m + ECFieldFp ECGenParameterSpec ECKey ECParameterSpec ECPoint ECPrivateKey ECPrivateKeySpec ECPublicKey + ECPublicKeySpec EditorKit Element ElementIterator ElementType Ellipse2D EllipticCurve EmptyBorder + EmptyStackException EncodedKeySpec Encoder EncryptedPrivateKeyInfo Entity Enum + EnumConstantNotPresentException EnumControl Enumeration EnumMap EnumSet EnumSyntax EOFException Error + ErrorListener ErrorManager EtchedBorder Event EventContext EventDirContext EventHandler EventListener + EventListenerList EventListenerProxy EventObject EventQueue EventSetDescriptor Exception + ExceptionInInitializerError ExceptionListener Exchanger ExecutionException Executor + ExecutorCompletionService Executors ExecutorService ExemptionMechanism ExemptionMechanismException + ExemptionMechanismSpi ExpandVetoException ExportException Expression ExtendedRequest ExtendedResponse + Externalizable FactoryConfigurationError FailedLoginException FeatureDescriptor Fidelity Field + FieldPosition FieldView File FileCacheImageInputStream FileCacheImageOutputStream FileChannel + FileChooserUI FileDescriptor FileDialog FileFilter FileHandler FileImageInputStream + FileImageOutputStream FileInputStream FileLock FileLockInterruptionException FilenameFilter FileNameMap + FileNotFoundException FileOutputStream FilePermission FileReader FileSystemView FileView FileWriter + Filter FilteredImageSource FilteredRowSet FilterInputStream FilterOutputStream FilterReader FilterWriter + Finishings FixedHeightLayoutCache FlatteningPathIterator FlavorEvent FlavorException FlavorListener + FlavorMap FlavorTable Float FloatBuffer FloatControl FlowLayout FlowView Flushable FocusAdapter + FocusEvent FocusListener FocusManager FocusTraversalPolicy Font FontFormatException FontMetrics + FontRenderContext FontUIResource Format FormatConversionProvider FormatFlagsConversionMismatchException + Formattable FormattableFlags Formatter FormatterClosedException FormSubmitEvent FormView Frame Future + FutureTask GapContent GarbageCollectorMXBean GatheringByteChannel GaugeMonitor GaugeMonitorMBean + GeneralPath GeneralSecurityException GenericArrayType GenericDeclaration GenericSignatureFormatError + GlyphJustificationInfo GlyphMetrics GlyphVector GlyphView GradientPaint GraphicAttribute Graphics + Graphics2D GraphicsConfigTemplate GraphicsConfiguration GraphicsDevice GraphicsEnvironment GrayFilter + GregorianCalendar GridBagConstraints GridBagLayout GridLayout Group Guard GuardedObject GZIPInputStream + GZIPOutputStream Handler HandshakeCompletedEvent HandshakeCompletedListener HasControls HashAttributeSet + HashDocAttributeSet HashMap HashPrintJobAttributeSet HashPrintRequestAttributeSet + HashPrintServiceAttributeSet HashSet Hashtable HeadlessException HierarchyBoundsAdapter + HierarchyBoundsListener HierarchyEvent HierarchyListener Highlighter HostnameVerifier HTML HTMLDocument + HTMLEditorKit HTMLFrameHyperlinkEvent HTMLWriter HttpRetryException HttpsURLConnection HttpURLConnection + HyperlinkEvent HyperlinkListener ICC_ColorSpace ICC_Profile ICC_ProfileGray ICC_ProfileRGB Icon + IconUIResource IconView Identity IdentityHashMap IdentityScope IIOByteBuffer IIOException IIOImage + IIOInvalidTreeException IIOMetadata IIOMetadataController IIOMetadataFormat IIOMetadataFormatImpl + IIOMetadataNode IIOParam IIOParamController IIOReadProgressListener IIOReadUpdateListener + IIOReadWarningListener IIORegistry IIOServiceProvider IIOWriteProgressListener IIOWriteWarningListener + IllegalAccessError IllegalAccessException IllegalArgumentException IllegalBlockingModeException + IllegalBlockSizeException IllegalCharsetNameException IllegalClassFormatException + IllegalComponentStateException IllegalFormatCodePointException IllegalFormatConversionException + IllegalFormatException IllegalFormatFlagsException IllegalFormatPrecisionException + IllegalFormatWidthException IllegalMonitorStateException IllegalPathStateException + IllegalSelectorException IllegalStateException IllegalThreadStateException Image ImageCapabilities + ImageConsumer ImageFilter ImageGraphicAttribute ImageIcon ImageInputStream ImageInputStreamImpl + ImageInputStreamSpi ImageIO ImageObserver ImageOutputStream ImageOutputStreamImpl ImageOutputStreamSpi + ImageProducer ImageReader ImageReaderSpi ImageReaderWriterSpi ImageReadParam ImageTranscoder + ImageTranscoderSpi ImageTypeSpecifier ImageView ImageWriteParam ImageWriter ImageWriterSpi + ImagingOpException IncompatibleClassChangeError IncompleteAnnotationException IndexColorModel + IndexedPropertyChangeEvent IndexedPropertyDescriptor IndexOutOfBoundsException Inet4Address Inet6Address + InetAddress InetSocketAddress Inflater InflaterInputStream InheritableThreadLocal Inherited + InitialContext InitialContextFactory InitialContextFactoryBuilder InitialDirContext InitialLdapContext + InlineView InputContext InputEvent InputMap InputMapUIResource InputMethod InputMethodContext + InputMethodDescriptor InputMethodEvent InputMethodHighlight InputMethodListener InputMethodRequests + InputMismatchException InputStream InputStreamReader InputSubset InputVerifier Insets InsetsUIResource + InstanceAlreadyExistsException InstanceNotFoundException InstantiationError InstantiationException + Instrument Instrumentation InsufficientResourcesException IntBuffer Integer IntegerSyntax InternalError + InternalFrameAdapter InternalFrameEvent InternalFrameFocusTraversalPolicy InternalFrameListener + InternalFrameUI InternationalFormatter InterruptedException InterruptedIOException + InterruptedNamingException InterruptibleChannel IntrospectionException Introspector + InvalidActivityException InvalidAlgorithmParameterException InvalidApplicationException + InvalidAttributeIdentifierException InvalidAttributesException InvalidAttributeValueException + InvalidClassException InvalidDnDOperationException InvalidKeyException InvalidKeySpecException + InvalidMarkException InvalidMidiDataException InvalidNameException InvalidObjectException + InvalidOpenTypeException InvalidParameterException InvalidParameterSpecException + InvalidPreferencesFormatException InvalidPropertiesFormatException InvalidRelationIdException + InvalidRelationServiceException InvalidRelationTypeException InvalidRoleInfoException + InvalidRoleValueException InvalidSearchControlsException InvalidSearchFilterException + InvalidTargetObjectTypeException InvalidTransactionException InvocationEvent InvocationHandler + InvocationTargetException IOException ItemEvent ItemListener ItemSelectable Iterable Iterator + IvParameterSpec JApplet JarEntry JarException JarFile JarInputStream JarOutputStream JarURLConnection + JButton JCheckBox JCheckBoxMenuItem JColorChooser JComboBox JComponent JdbcRowSet JDesktopPane JDialog + JEditorPane JFileChooser JFormattedTextField JFrame JInternalFrame JLabel JLayeredPane JList JMenu + JMenuBar JMenuItem JMException JMRuntimeException JMXAuthenticator JMXConnectionNotification + JMXConnector JMXConnectorFactory JMXConnectorProvider JMXConnectorServer JMXConnectorServerFactory + JMXConnectorServerMBean JMXConnectorServerProvider JMXPrincipal JMXProviderException + JMXServerErrorException JMXServiceURL JobAttributes JobHoldUntil JobImpressions JobImpressionsCompleted + JobImpressionsSupported JobKOctets JobKOctetsProcessed JobKOctetsSupported JobMediaSheets + JobMediaSheetsCompleted JobMediaSheetsSupported JobMessageFromOperator JobName JobOriginatingUserName + JobPriority JobPrioritySupported JobSheets JobState JobStateReason JobStateReasons Joinable JoinRowSet + JOptionPane JPanel JPasswordField JPEGHuffmanTable JPEGImageReadParam JPEGImageWriteParam JPEGQTable + JPopupMenu JProgressBar JRadioButton JRadioButtonMenuItem JRootPane JScrollBar JScrollPane JSeparator + JSlider JSpinner JSplitPane JTabbedPane JTable JTableHeader JTextArea JTextComponent JTextField + JTextPane JToggleButton JToolBar JToolTip JTree JViewport JWindow KerberosKey KerberosPrincipal + KerberosTicket Kernel Key KeyAdapter KeyAgreement KeyAgreementSpi KeyAlreadyExistsException + KeyboardFocusManager KeyEvent KeyEventDispatcher KeyEventPostProcessor KeyException KeyFactory + KeyFactorySpi KeyGenerator KeyGeneratorSpi KeyListener KeyManagementException KeyManager + KeyManagerFactory KeyManagerFactorySpi Keymap KeyPair KeyPairGenerator KeyPairGeneratorSpi KeyRep + KeySpec KeyStore KeyStoreBuilderParameters KeyStoreException KeyStoreSpi KeyStroke Label LabelUI + LabelView LanguageCallback LastOwnerException LayeredHighlighter LayoutFocusTraversalPolicy + LayoutManager LayoutManager2 LayoutQueue LDAPCertStoreParameters LdapContext LdapName + LdapReferralException Lease Level LimitExceededException Line Line2D LineBorder LineBreakMeasurer + LineEvent LineListener LineMetrics LineNumberInputStream LineNumberReader LineUnavailableException + LinkageError LinkedBlockingQueue LinkedHashMap LinkedHashSet LinkedList LinkException LinkLoopException + LinkRef List ListCellRenderer ListDataEvent ListDataListener ListenerNotFoundException ListIterator + ListModel ListResourceBundle ListSelectionEvent ListSelectionListener ListSelectionModel ListUI ListView + LoaderHandler Locale LocateRegistry Lock LockSupport Logger LoggingMXBean LoggingPermission LoginContext + LoginException LoginModule LogManager LogRecord LogStream Long LongBuffer LookAndFeel LookupOp + LookupTable Mac MacSpi MalformedInputException MalformedLinkException MalformedObjectNameException + MalformedParameterizedTypeException MalformedURLException ManagementFactory ManagementPermission + ManageReferralControl ManagerFactoryParameters Manifest Map MappedByteBuffer MarshalException + MarshalledObject MaskFormatter Matcher MatchResult Math MathContext MatteBorder MBeanAttributeInfo + MBeanConstructorInfo MBeanException MBeanFeatureInfo MBeanInfo MBeanNotificationInfo MBeanOperationInfo + MBeanParameterInfo MBeanPermission MBeanRegistration MBeanRegistrationException MBeanServer + MBeanServerBuilder MBeanServerConnection MBeanServerDelegate MBeanServerDelegateMBean MBeanServerFactory + MBeanServerForwarder MBeanServerInvocationHandler MBeanServerNotification MBeanServerNotificationFilter + MBeanServerPermission MBeanTrustPermission Media MediaName MediaPrintableArea MediaSize MediaSizeName + MediaTracker MediaTray Member MemoryCacheImageInputStream MemoryCacheImageOutputStream MemoryHandler + MemoryImageSource MemoryManagerMXBean MemoryMXBean MemoryNotificationInfo MemoryPoolMXBean MemoryType + MemoryUsage Menu MenuBar MenuBarUI MenuComponent MenuContainer MenuDragMouseEvent MenuDragMouseListener + MenuElement MenuEvent MenuItem MenuItemUI MenuKeyEvent MenuKeyListener MenuListener MenuSelectionManager + MenuShortcut MessageDigest MessageDigestSpi MessageFormat MetaEventListener MetalBorders MetalButtonUI + MetalCheckBoxIcon MetalCheckBoxUI MetalComboBoxButton MetalComboBoxEditor MetalComboBoxIcon + MetalComboBoxUI MetalDesktopIconUI MetalFileChooserUI MetalIconFactory MetalInternalFrameTitlePane + MetalInternalFrameUI MetalLabelUI MetalLookAndFeel MetalMenuBarUI MetalPopupMenuSeparatorUI + MetalProgressBarUI MetalRadioButtonUI MetalRootPaneUI MetalScrollBarUI MetalScrollButton + MetalScrollPaneUI MetalSeparatorUI MetalSliderUI MetalSplitPaneUI MetalTabbedPaneUI MetalTextFieldUI + MetalTheme MetalToggleButtonUI MetalToolBarUI MetalToolTipUI MetalTreeUI MetaMessage Method + MethodDescriptor MGF1ParameterSpec MidiChannel MidiDevice MidiDeviceProvider MidiEvent MidiFileFormat + MidiFileReader MidiFileWriter MidiMessage MidiSystem MidiUnavailableException MimeTypeParseException + MinimalHTMLWriter MissingFormatArgumentException MissingFormatWidthException MissingResourceException + Mixer MixerProvider MLet MLetMBean ModelMBean ModelMBeanAttributeInfo ModelMBeanConstructorInfo + ModelMBeanInfo ModelMBeanInfoSupport ModelMBeanNotificationBroadcaster ModelMBeanNotificationInfo + ModelMBeanOperationInfo ModificationItem Modifier Monitor MonitorMBean MonitorNotification + MonitorSettingException MouseAdapter MouseDragGestureRecognizer MouseEvent MouseInfo MouseInputAdapter + MouseInputListener MouseListener MouseMotionAdapter MouseMotionListener MouseWheelEvent + MouseWheelListener MultiButtonUI MulticastSocket MultiColorChooserUI MultiComboBoxUI MultiDesktopIconUI + MultiDesktopPaneUI MultiDoc MultiDocPrintJob MultiDocPrintService MultiFileChooserUI + MultiInternalFrameUI MultiLabelUI MultiListUI MultiLookAndFeel MultiMenuBarUI MultiMenuItemUI + MultiOptionPaneUI MultiPanelUI MultiPixelPackedSampleModel MultipleDocumentHandling MultipleMaster + MultiPopupMenuUI MultiProgressBarUI MultiRootPaneUI MultiScrollBarUI MultiScrollPaneUI MultiSeparatorUI + MultiSliderUI MultiSpinnerUI MultiSplitPaneUI MultiTabbedPaneUI MultiTableHeaderUI MultiTableUI + MultiTextUI MultiToolBarUI MultiToolTipUI MultiTreeUI MultiViewportUI MutableAttributeSet + MutableComboBoxModel MutableTreeNode Name NameAlreadyBoundException NameCallback NameClassPair + NameNotFoundException NameParser NamespaceChangeListener NamespaceContext Naming NamingEnumeration + NamingEvent NamingException NamingExceptionEvent NamingListener NamingManager NamingSecurityException + NavigationFilter NegativeArraySizeException NetPermission NetworkInterface NoClassDefFoundError + NoConnectionPendingException NodeChangeEvent NodeChangeListener NoInitialContextException + NoninvertibleTransformException NonReadableChannelException NonWritableChannelException + NoPermissionException NoRouteToHostException NoSuchAlgorithmException NoSuchAttributeException + NoSuchElementException NoSuchFieldError NoSuchFieldException NoSuchMethodError NoSuchMethodException + NoSuchObjectException NoSuchPaddingException NoSuchProviderException NotActiveException + NotBoundException NotCompliantMBeanException NotContextException Notification NotificationBroadcaster + NotificationBroadcasterSupport NotificationEmitter NotificationFilter NotificationFilterSupport + NotificationListener NotificationResult NotOwnerException NotSerializableException NotYetBoundException + NotYetConnectedException NullCipher NullPointerException Number NumberFormat NumberFormatException + NumberFormatter NumberOfDocuments NumberOfInterveningJobs NumberUp NumberUpSupported NumericShaper + OAEPParameterSpec Object ObjectChangeListener ObjectFactory ObjectFactoryBuilder ObjectInput + ObjectInputStream ObjectInputValidation ObjectInstance ObjectName ObjectOutput ObjectOutputStream + ObjectStreamClass ObjectStreamConstants ObjectStreamException ObjectStreamField ObjectView ObjID + Observable Observer OceanTheme OpenDataException OpenMBeanAttributeInfo OpenMBeanAttributeInfoSupport + OpenMBeanConstructorInfo OpenMBeanConstructorInfoSupport OpenMBeanInfo OpenMBeanInfoSupport + OpenMBeanOperationInfo OpenMBeanOperationInfoSupport OpenMBeanParameterInfo + OpenMBeanParameterInfoSupport OpenType OperatingSystemMXBean Operation OperationNotSupportedException + OperationsException Option OptionalDataException OptionPaneUI OrientationRequested OutOfMemoryError + OutputDeviceAssigned OutputKeys OutputStream OutputStreamWriter OverlappingFileLockException + OverlayLayout Override Owner Pack200 Package PackedColorModel Pageable PageAttributes + PagedResultsControl PagedResultsResponseControl PageFormat PageRanges PagesPerMinute PagesPerMinuteColor + Paint PaintContext PaintEvent Panel PanelUI Paper ParagraphView ParameterBlock ParameterDescriptor + ParameterizedType ParameterMetaData ParseException ParsePosition Parser ParserConfigurationException + ParserDelegator PartialResultException PasswordAuthentication PasswordCallback PasswordView Patch + PathIterator Pattern PatternSyntaxException PBEKey PBEKeySpec PBEParameterSpec PDLOverrideSupported + Permission PermissionCollection Permissions PersistenceDelegate PersistentMBean PhantomReference Pipe + PipedInputStream PipedOutputStream PipedReader PipedWriter PixelGrabber PixelInterleavedSampleModel + PKCS8EncodedKeySpec PKIXBuilderParameters PKIXCertPathBuilderResult PKIXCertPathChecker + PKIXCertPathValidatorResult PKIXParameters PlainDocument PlainView Point Point2D PointerInfo Policy + PolicyNode PolicyQualifierInfo Polygon PooledConnection Popup PopupFactory PopupMenu PopupMenuEvent + PopupMenuListener PopupMenuUI Port PortableRemoteObject PortableRemoteObjectDelegate + PortUnreachableException Position Predicate PreferenceChangeEvent PreferenceChangeListener Preferences + PreferencesFactory PreparedStatement PresentationDirection Principal Printable PrinterAbortException + PrinterException PrinterGraphics PrinterInfo PrinterIOException PrinterIsAcceptingJobs PrinterJob + PrinterLocation PrinterMakeAndModel PrinterMessageFromOperator PrinterMoreInfo + PrinterMoreInfoManufacturer PrinterName PrinterResolution PrinterState PrinterStateReason + PrinterStateReasons PrinterURI PrintEvent PrintException PrintGraphics PrintJob PrintJobAdapter + PrintJobAttribute PrintJobAttributeEvent PrintJobAttributeListener PrintJobAttributeSet PrintJobEvent + PrintJobListener PrintQuality PrintRequestAttribute PrintRequestAttributeSet PrintService + PrintServiceAttribute PrintServiceAttributeEvent PrintServiceAttributeListener PrintServiceAttributeSet + PrintServiceLookup PrintStream PrintWriter PriorityBlockingQueue PriorityQueue PrivateClassLoader + PrivateCredentialPermission PrivateKey PrivateMLet PrivilegedAction PrivilegedActionException + PrivilegedExceptionAction Process ProcessBuilder ProfileDataException ProgressBarUI ProgressMonitor + ProgressMonitorInputStream Properties PropertyChangeEvent PropertyChangeListener + PropertyChangeListenerProxy PropertyChangeSupport PropertyDescriptor PropertyEditor + PropertyEditorManager PropertyEditorSupport PropertyPermission PropertyResourceBundle + PropertyVetoException ProtectionDomain ProtocolException Provider ProviderException Proxy ProxySelector + PSource PSSParameterSpec PublicKey PushbackInputStream PushbackReader QName QuadCurve2D Query QueryEval + QueryExp Queue QueuedJobCount Random RandomAccess RandomAccessFile Raster RasterFormatException RasterOp + RC2ParameterSpec RC5ParameterSpec Rdn Readable ReadableByteChannel Reader ReadOnlyBufferException + ReadWriteLock RealmCallback RealmChoiceCallback Receiver Rectangle Rectangle2D RectangularShape + ReentrantLock ReentrantReadWriteLock Ref RefAddr Reference Referenceable ReferenceQueue + ReferenceUriSchemesSupported ReferralException ReflectionException ReflectPermission Refreshable + RefreshFailedException Region RegisterableService Registry RegistryHandler RejectedExecutionException + RejectedExecutionHandler Relation RelationException RelationNotFoundException RelationNotification + RelationService RelationServiceMBean RelationServiceNotRegisteredException RelationSupport + RelationSupportMBean RelationType RelationTypeNotFoundException RelationTypeSupport Remote RemoteCall + RemoteException RemoteObject RemoteObjectInvocationHandler RemoteRef RemoteServer RemoteStub + RenderableImage RenderableImageOp RenderableImageProducer RenderContext RenderedImage + RenderedImageFactory Renderer RenderingHints RepaintManager ReplicateScaleFilter RequestingUserName + RequiredModelMBean RescaleOp ResolutionSyntax Resolver ResolveResult ResourceBundle ResponseCache Result + ResultSet ResultSetMetaData Retention RetentionPolicy ReverbType RGBImageFilter RMIClassLoader + RMIClassLoaderSpi RMIClientSocketFactory RMIConnection RMIConnectionImpl RMIConnectionImpl_Stub + RMIConnector RMIConnectorServer RMIFailureHandler RMIIIOPServerImpl RMIJRMPServerImpl + RMISecurityException RMISecurityManager RMIServer RMIServerImpl RMIServerImpl_Stub + RMIServerSocketFactory RMISocketFactory Robot Role RoleInfo RoleInfoNotFoundException RoleList + RoleNotFoundException RoleResult RoleStatus RoleUnresolved RoleUnresolvedList RootPaneContainer + RootPaneUI RoundingMode RoundRectangle2D RowMapper RowSet RowSetEvent RowSetInternal RowSetListener + RowSetMetaData RowSetMetaDataImpl RowSetReader RowSetWarning RowSetWriter RSAKey RSAKeyGenParameterSpec + RSAMultiPrimePrivateCrtKey RSAMultiPrimePrivateCrtKeySpec RSAOtherPrimeInfo RSAPrivateCrtKey + RSAPrivateCrtKeySpec RSAPrivateKey RSAPrivateKeySpec RSAPublicKey RSAPublicKeySpec RTFEditorKit + RuleBasedCollator Runnable Runtime RuntimeErrorException RuntimeException RuntimeMBeanException + RuntimeMXBean RuntimeOperationsException RuntimePermission SampleModel Sasl SaslClient SaslClientFactory + SaslException SaslServer SaslServerFactory Savepoint SAXParser SAXParserFactory SAXResult SAXSource + SAXTransformerFactory Scanner ScatteringByteChannel ScheduledExecutorService ScheduledFuture + ScheduledThreadPoolExecutor Schema SchemaFactory SchemaFactoryLoader SchemaViolationException Scrollable + Scrollbar ScrollBarUI ScrollPane ScrollPaneAdjustable ScrollPaneConstants ScrollPaneLayout ScrollPaneUI + SealedObject SearchControls SearchResult SecretKey SecretKeyFactory SecretKeyFactorySpi SecretKeySpec + SecureCacheResponse SecureClassLoader SecureRandom SecureRandomSpi Security SecurityException + SecurityManager SecurityPermission Segment SelectableChannel SelectionKey Selector SelectorProvider + Semaphore SeparatorUI Sequence SequenceInputStream Sequencer SerialArray SerialBlob SerialClob + SerialDatalink SerialException Serializable SerializablePermission SerialJavaObject SerialRef + SerialStruct ServerCloneException ServerError ServerException ServerNotActiveException ServerRef + ServerRuntimeException ServerSocket ServerSocketChannel ServerSocketFactory ServiceNotFoundException + ServicePermission ServiceRegistry ServiceUI ServiceUIFactory ServiceUnavailableException Set + SetOfIntegerSyntax Severity Shape ShapeGraphicAttribute SheetCollate Short ShortBuffer + ShortBufferException ShortLookupTable ShortMessage Sides Signature SignatureException SignatureSpi + SignedObject Signer SimpleAttributeSet SimpleBeanInfo SimpleDateFormat SimpleDoc SimpleFormatter + SimpleTimeZone SimpleType SinglePixelPackedSampleModel SingleSelectionModel Size2DSyntax + SizeLimitExceededException SizeRequirements SizeSequence Skeleton SkeletonMismatchException + SkeletonNotFoundException SliderUI Socket SocketAddress SocketChannel SocketException SocketFactory + SocketHandler SocketImpl SocketImplFactory SocketOptions SocketPermission SocketSecurityException + SocketTimeoutException SoftBevelBorder SoftReference SortControl SortedMap SortedSet + SortingFocusTraversalPolicy SortKey SortResponseControl Soundbank SoundbankReader SoundbankResource + Source SourceDataLine SourceLocator SpinnerDateModel SpinnerListModel SpinnerModel SpinnerNumberModel + SpinnerUI SplitPaneUI Spring SpringLayout SQLData SQLException SQLInput SQLInputImpl SQLOutput + SQLOutputImpl SQLPermission SQLWarning SSLContext SSLContextSpi SSLEngine SSLEngineResult SSLException + SSLHandshakeException SSLKeyException SSLPeerUnverifiedException SSLPermission SSLProtocolException + SslRMIClientSocketFactory SslRMIServerSocketFactory SSLServerSocket SSLServerSocketFactory SSLSession + SSLSessionBindingEvent SSLSessionBindingListener SSLSessionContext SSLSocket SSLSocketFactory Stack + StackOverflowError StackTraceElement StandardMBean StartTlsRequest StartTlsResponse StateEdit + StateEditable StateFactory Statement StreamCorruptedException StreamHandler StreamPrintService + StreamPrintServiceFactory StreamResult StreamSource StreamTokenizer StrictMath String StringBuffer + StringBufferInputStream StringBuilder StringCharacterIterator StringContent + StringIndexOutOfBoundsException StringMonitor StringMonitorMBean StringReader StringRefAddr + StringSelection StringTokenizer StringValueExp StringWriter Stroke Struct Stub StubDelegate + StubNotFoundException Style StyleConstants StyleContext StyledDocument StyledEditorKit StyleSheet + Subject SubjectDelegationPermission SubjectDomainCombiner SupportedValuesAttribute SuppressWarnings + SwingConstants SwingPropertyChangeSupport SwingUtilities SyncFactory SyncFactoryException + SyncFailedException SynchronousQueue SyncProvider SyncProviderException SyncResolver SynthConstants + SynthContext Synthesizer SynthGraphicsUtils SynthLookAndFeel SynthPainter SynthStyle SynthStyleFactory + SysexMessage System SystemColor SystemFlavorMap TabableView TabbedPaneUI TabExpander TableCellEditor + TableCellRenderer TableColumn TableColumnModel TableColumnModelEvent TableColumnModelListener + TableHeaderUI TableModel TableModelEvent TableModelListener TableUI TableView TabSet TabStop TabularData + TabularDataSupport TabularType TagElement Target TargetDataLine TargetedNotification Templates + TemplatesHandler TextAction TextArea TextAttribute TextComponent TextEvent TextField TextHitInfo + TextInputCallback TextLayout TextListener TextMeasurer TextOutputCallback TextSyntax TextUI TexturePaint + Thread ThreadDeath ThreadFactory ThreadGroup ThreadInfo ThreadLocal ThreadMXBean ThreadPoolExecutor + Throwable Tie TileObserver Time TimeLimitExceededException TimeoutException Timer + TimerAlarmClockNotification TimerMBean TimerNotification TimerTask Timestamp TimeUnit TimeZone + TitledBorder ToolBarUI Toolkit ToolTipManager ToolTipUI TooManyListenersException Track + TransactionalWriter TransactionRequiredException TransactionRolledbackException Transferable + TransferHandler TransformAttribute Transformer TransformerConfigurationException TransformerException + TransformerFactory TransformerFactoryConfigurationError TransformerHandler Transmitter Transparency + TreeCellEditor TreeCellRenderer TreeExpansionEvent TreeExpansionListener TreeMap TreeModel + TreeModelEvent TreeModelListener TreeNode TreePath TreeSelectionEvent TreeSelectionListener + TreeSelectionModel TreeSet TreeUI TreeWillExpandListener TrustAnchor TrustManager TrustManagerFactory + TrustManagerFactorySpi Type TypeInfoProvider TypeNotPresentException Types TypeVariable UID UIDefaults + UIManager UIResource UndeclaredThrowableException UndoableEdit UndoableEditEvent UndoableEditListener + UndoableEditSupport UndoManager UnexpectedException UnicastRemoteObject UnknownError + UnknownFormatConversionException UnknownFormatFlagsException UnknownGroupException UnknownHostException + UnknownObjectException UnknownServiceException UnmappableCharacterException UnmarshalException + UnmodifiableClassException UnmodifiableSetException UnrecoverableEntryException + UnrecoverableKeyException Unreferenced UnresolvedAddressException UnresolvedPermission + UnsatisfiedLinkError UnsolicitedNotification UnsolicitedNotificationEvent + UnsolicitedNotificationListener UnsupportedAddressTypeException UnsupportedAudioFileException + UnsupportedCallbackException UnsupportedCharsetException UnsupportedClassVersionError + UnsupportedEncodingException UnsupportedFlavorException UnsupportedLookAndFeelException + UnsupportedOperationException URI URIException URIResolver URISyntax URISyntaxException URL + URLClassLoader URLConnection URLDecoder URLEncoder URLStreamHandler URLStreamHandlerFactory + UTFDataFormatException Util UtilDelegate Utilities UUID Validator ValidatorHandler ValueExp ValueHandler + ValueHandlerMultiFormat VariableHeightLayoutCache Vector VerifyError VetoableChangeListener + VetoableChangeListenerProxy VetoableChangeSupport View ViewFactory ViewportLayout ViewportUI + VirtualMachineError Visibility VMID VoiceStatus Void VolatileImage WeakHashMap WeakReference WebRowSet + WildcardType Window WindowAdapter WindowConstants WindowEvent WindowFocusListener WindowListener + WindowStateListener WrappedPlainView WritableByteChannel WritableRaster WritableRenderedImage + WriteAbortedException Writer X500Principal X500PrivateCredential X509Certificate X509CertSelector + X509CRL X509CRLEntry X509CRLSelector X509EncodedKeySpec X509ExtendedKeyManager X509Extension + X509KeyManager X509TrustManager XAConnection XADataSource XAException XAResource Xid XMLConstants + XMLDecoder XMLEncoder XMLFormatter XMLGregorianCalendar XMLParseException XmlReader XmlWriter XPath + XPathConstants XPathException XPathExpression XPathExpressionException XPathFactory + XPathFactoryConfigurationException XPathFunction XPathFunctionException XPathFunctionResolver + XPathVariableResolver ZipEntry ZipException ZipFile ZipInputStream ZipOutputStream ZoneView + ] + #:nocov: + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/java_script.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/java_script.rb new file mode 100644 index 0000000..8c13d4f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/java_script.rb @@ -0,0 +1,236 @@ +module CodeRay +module Scanners + + # Scanner for JavaScript. + # + # Aliases: +ecmascript+, +ecma_script+, +javascript+ + class JavaScript < Scanner + + register_for :java_script + file_extension 'js' + + # The actual JavaScript keywords. + KEYWORDS = %w[ + break case catch continue default delete do else + finally for function if in instanceof new + return switch throw try typeof var void while with + ] # :nodoc: + PREDEFINED_CONSTANTS = %w[ + false null true undefined NaN Infinity + ] # :nodoc: + + MAGIC_VARIABLES = %w[ this arguments ] # :nodoc: arguments was introduced in JavaScript 1.4 + + KEYWORDS_EXPECTING_VALUE = WordList.new.add %w[ + case delete in instanceof new return throw typeof with + ] # :nodoc: + + # Reserved for future use. + RESERVED_WORDS = %w[ + abstract boolean byte char class debugger double enum export extends + final float goto implements import int interface long native package + private protected public short static super synchronized throws transient + volatile + ] # :nodoc: + + IDENT_KIND = WordList.new(:ident). + add(RESERVED_WORDS, :reserved). + add(PREDEFINED_CONSTANTS, :predefined_constant). + add(MAGIC_VARIABLES, :local_variable). + add(KEYWORDS, :keyword) # :nodoc: + + ESCAPE = / [bfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x # :nodoc: + UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x # :nodoc: + REGEXP_ESCAPE = / [bBdDsSwW] /x # :nodoc: + STRING_CONTENT_PATTERN = { + "'" => /[^\\']+/, + '"' => /[^\\"]+/, + '/' => /[^\\\/]+/, + } # :nodoc: + KEY_CHECK_PATTERN = { + "'" => / (?> [^\\']* (?: \\. [^\\']* )* ) ' \s* : /mx, + '"' => / (?> [^\\"]* (?: \\. [^\\"]* )* ) " \s* : /mx, + } # :nodoc: + + protected + + def setup + @state = :initial + end + + def scan_tokens encoder, options + + state, string_delimiter = options[:state] || @state + if string_delimiter + encoder.begin_group state + end + + value_expected = true + key_expected = false + function_expected = false + + until eos? + + case state + + when :initial + + if match = scan(/ \s+ | \\\n /x) + value_expected = true if !value_expected && match.index(?\n) + encoder.text_token match, :space + + elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .*() ) !mx) + value_expected = true + encoder.text_token match, :comment + state = :open_multi_line_comment if self[1] + + elsif check(/\.?\d/) + key_expected = value_expected = false + if match = scan(/0[xX][0-9A-Fa-f]+/) + encoder.text_token match, :hex + elsif match = scan(/(?>0[0-7]+)(?![89.eEfF])/) + encoder.text_token match, :octal + elsif match = scan(/\d+[fF]|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/) + encoder.text_token match, :float + elsif match = scan(/\d+/) + encoder.text_token match, :integer + end + + elsif value_expected && match = scan(/<([[:alpha:]]\w*) (?: [^\/>]*\/> | .*?<\/\1>)/xim) + # TODO: scan over nested tags + xml_scanner.tokenize match, :tokens => encoder + value_expected = false + + elsif match = scan(/ [-+*=<>?:;,!&^|(\[{~%]+ | \.(?!\d) /x) + value_expected = true + last_operator = match[-1] + key_expected = (last_operator == ?{) || (last_operator == ?,) + function_expected = false + encoder.text_token match, :operator + + elsif match = scan(/ [)\]}]+ /x) + function_expected = key_expected = value_expected = false + encoder.text_token match, :operator + + elsif match = scan(/ [$a-zA-Z_][A-Za-z_0-9$]* /x) + kind = IDENT_KIND[match] + value_expected = (kind == :keyword) && KEYWORDS_EXPECTING_VALUE[match] + # TODO: labels + if kind == :ident + if match.index(?$) # $ allowed inside an identifier + kind = :predefined + elsif function_expected + kind = :function + elsif check(/\s*[=:]\s*function\b/) + kind = :function + elsif key_expected && check(/\s*:/) + kind = :key + end + end + function_expected = (kind == :keyword) && (match == 'function') + key_expected = false + encoder.text_token match, kind + + elsif match = scan(/["']/) + if key_expected && check(KEY_CHECK_PATTERN[match]) + state = :key + else + state = :string + end + encoder.begin_group state + string_delimiter = match + encoder.text_token match, :delimiter + + elsif value_expected && (match = scan(/\//)) + encoder.begin_group :regexp + state = :regexp + string_delimiter = '/' + encoder.text_token match, :delimiter + + elsif match = scan(/ \/ /x) + value_expected = true + key_expected = false + encoder.text_token match, :operator + + else + encoder.text_token getch, :error + + end + + when :string, :regexp, :key + if match = scan(STRING_CONTENT_PATTERN[string_delimiter]) + encoder.text_token match, :content + elsif match = scan(/["'\/]/) + encoder.text_token match, :delimiter + if state == :regexp + modifiers = scan(/[gim]+/) + encoder.text_token modifiers, :modifier if modifiers && !modifiers.empty? + end + encoder.end_group state + string_delimiter = nil + key_expected = value_expected = false + state = :initial + elsif state != :regexp && (match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)) + if string_delimiter == "'" && !(match == "\\\\" || match == "\\'") + encoder.text_token match, :content + else + encoder.text_token match, :char + end + elsif state == :regexp && match = scan(/ \\ (?: #{ESCAPE} | #{REGEXP_ESCAPE} | #{UNICODE_ESCAPE} ) /mox) + encoder.text_token match, :char + elsif match = scan(/\\./m) + encoder.text_token match, :content + elsif match = scan(/ \\ | $ /x) + encoder.end_group state + encoder.text_token match, :error unless match.empty? + string_delimiter = nil + key_expected = value_expected = false + state = :initial + else + raise_inspect "else case #{string_delimiter} reached; %p not handled." % peek(1), encoder + end + + when :open_multi_line_comment + if match = scan(%r! .*? \*/ !mx) + state = :initial + else + match = scan(%r! .+ !mx) + end + value_expected = true + encoder.text_token match, :comment if match + + else + #:nocov: + raise_inspect 'Unknown state: %p' % [state], encoder + #:nocov: + + end + + end + + if options[:keep_state] + @state = state, string_delimiter + end + + if [:string, :regexp].include? state + encoder.end_group state + end + + encoder + end + + protected + + def reset_instance + super + @xml_scanner.reset if defined? @xml_scanner + end + + def xml_scanner + @xml_scanner ||= CodeRay.scanner :xml, :tokens => @tokens, :keep_tokens => true, :keep_state => false + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/json.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/json.rb new file mode 100644 index 0000000..b09970c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/json.rb @@ -0,0 +1,98 @@ +module CodeRay +module Scanners + + # Scanner for JSON (JavaScript Object Notation). + class JSON < Scanner + + register_for :json + file_extension 'json' + + KINDS_NOT_LOC = [ + :float, :char, :content, :delimiter, + :error, :integer, :operator, :value, + ] # :nodoc: + + ESCAPE = / [bfnrt\\"\/] /x # :nodoc: + UNICODE_ESCAPE = / u[a-fA-F0-9]{4} /x # :nodoc: + KEY = / (?> (?: [^\\"]+ | \\. )* ) " \s* : /x + + protected + + def setup + @state = :initial + end + + # See http://json.org/ for a definition of the JSON lexic/grammar. + def scan_tokens encoder, options + state = options[:state] || @state + + if [:string, :key].include? state + encoder.begin_group state + end + + until eos? + + case state + + when :initial + if match = scan(/ \s+ /x) + encoder.text_token match, :space + elsif match = scan(/"/) + state = check(/#{KEY}/o) ? :key : :string + encoder.begin_group state + encoder.text_token match, :delimiter + elsif match = scan(/ [:,\[{\]}] /x) + encoder.text_token match, :operator + elsif match = scan(/ true | false | null /x) + encoder.text_token match, :value + elsif match = scan(/ -? (?: 0 | [1-9]\d* ) /x) + if scan(/ \.\d+ (?:[eE][-+]?\d+)? | [eE][-+]? \d+ /x) + match << matched + encoder.text_token match, :float + else + encoder.text_token match, :integer + end + else + encoder.text_token getch, :error + end + + when :string, :key + if match = scan(/[^\\"]+/) + encoder.text_token match, :content + elsif match = scan(/"/) + encoder.text_token match, :delimiter + encoder.end_group state + state = :initial + elsif match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox) + encoder.text_token match, :char + elsif match = scan(/\\./m) + encoder.text_token match, :content + elsif match = scan(/ \\ | $ /x) + encoder.end_group state + encoder.text_token match, :error unless match.empty? + state = :initial + else + raise_inspect "else case \" reached; %p not handled." % peek(1), encoder + end + + else + raise_inspect 'Unknown state: %p' % [state], encoder + + end + end + + if options[:keep_state] + @state = state + end + + if [:string, :key].include? state + encoder.end_group state + end + + encoder + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/lua.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/lua.rb new file mode 100644 index 0000000..81d7dae --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/lua.rb @@ -0,0 +1,280 @@ +# encoding: utf-8 + +module CodeRay +module Scanners + + # Scanner for the Lua[http://lua.org] programming lanuage. + # + # The language’s complete syntax is defined in + # {the Lua manual}[http://www.lua.org/manual/5.2/manual.html], + # which is what this scanner tries to conform to. + class Lua < Scanner + + register_for :lua + file_extension 'lua' + title 'Lua' + + # Keywords used in Lua. + KEYWORDS = %w[and break do else elseif end + for function goto if in + local not or repeat return + then until while + ] + + # Constants set by the Lua core. + PREDEFINED_CONSTANTS = %w[false true nil] + + # The expressions contained in this array are parts of Lua’s `basic' + # library. Although it’s not entirely necessary to load that library, + # it is highly recommended and one would have to provide own implementations + # of some of these expressions if one does not do so. They however aren’t + # keywords, neither are they constants, but nearly predefined, so they + # get tagged as `predefined' rather than anything else. + # + # This list excludes values of form `_UPPERCASE' because the Lua manual + # requires such identifiers to be reserved by Lua anyway and they are + # highlighted directly accordingly, without the need for specific + # identifiers to be listed here. + PREDEFINED_EXPRESSIONS = %w[ + assert collectgarbage dofile error getmetatable + ipairs load loadfile next pairs pcall print + rawequal rawget rawlen rawset select setmetatable + tonumber tostring type xpcall + ] + + # Automatic token kind selection for normal words. + IDENT_KIND = CodeRay::WordList.new(:ident). + add(KEYWORDS, :keyword). + add(PREDEFINED_CONSTANTS, :predefined_constant). + add(PREDEFINED_EXPRESSIONS, :predefined) + + protected + + # Scanner initialization. + def setup + @state = :initial + @brace_depth = 0 + end + + # CodeRay entry hook. Starts parsing. + def scan_tokens(encoder, options) + state = options[:state] || @state + brace_depth = @brace_depth + num_equals = nil + + until eos? + case state + + when :initial + if match = scan(/\-\-\[\=*\[/) #--[[ long (possibly multiline) comment ]] + num_equals = match.count("=") # Number must match for comment end + encoder.begin_group(:comment) + encoder.text_token(match, :delimiter) + state = :long_comment + + elsif match = scan(/--.*$/) # --Lua comment + encoder.text_token(match, :comment) + + elsif match = scan(/\[=*\[/) # [[ long (possibly multiline) string ]] + num_equals = match.count("=") # Number must match for string end + encoder.begin_group(:string) + encoder.text_token(match, :delimiter) + state = :long_string + + elsif match = scan(/::\s*[a-zA-Z_][a-zA-Z0-9_]+\s*::/) # ::goto_label:: + encoder.text_token(match, :label) + + elsif match = scan(/_[A-Z]+/) # _UPPERCASE are names reserved for Lua + encoder.text_token(match, :predefined) + + elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/) # Normal letters (or letters followed by digits) + kind = IDENT_KIND[match] + + # Extra highlighting for entities following certain keywords + if kind == :keyword and match == "function" + state = :function_expected + elsif kind == :keyword and match == "goto" + state = :goto_label_expected + elsif kind == :keyword and match == "local" + state = :local_var_expected + end + + encoder.text_token(match, kind) + + elsif match = scan(/\{/) # Opening table brace { + encoder.begin_group(:map) + encoder.text_token(match, brace_depth >= 1 ? :inline_delimiter : :delimiter) + brace_depth += 1 + state = :map + + elsif match = scan(/\}/) # Closing table brace } + if brace_depth == 1 + brace_depth = 0 + encoder.text_token(match, :delimiter) + encoder.end_group(:map) + elsif brace_depth == 0 # Mismatched brace + encoder.text_token(match, :error) + else + brace_depth -= 1 + encoder.text_token(match, :inline_delimiter) + encoder.end_group(:map) + state = :map + end + + elsif match = scan(/["']/) # String delimiters " and ' + encoder.begin_group(:string) + encoder.text_token(match, :delimiter) + start_delim = match + state = :string + + # ↓Prefix hex number ←|→ decimal number + elsif match = scan(/-? (?:0x\h* \. \h+ (?:p[+\-]?\d+)? | \d*\.\d+ (?:e[+\-]?\d+)?)/ix) # hexadecimal constants have no E power, decimal ones no P power + encoder.text_token(match, :float) + + # ↓Prefix hex number ←|→ decimal number + elsif match = scan(/-? (?:0x\h+ (?:p[+\-]?\d+)? | \d+ (?:e[+\-]?\d+)?)/ix) # hexadecimal constants have no E power, decimal ones no P power + encoder.text_token(match, :integer) + + elsif match = scan(/[\+\-\*\/%^\#=~<>\(\)\[\]:;,] | \.(?!\d)/x) # Operators + encoder.text_token(match, :operator) + + elsif match = scan(/\s+/) # Space + encoder.text_token(match, :space) + + else # Invalid stuff. Note that Lua doesn’t accept multibyte chars outside of strings, hence these are also errors. + encoder.text_token(getch, :error) + end + + # It may be that we’re scanning a full-blown subexpression of a table + # (tables can contain full expressions in parts). + # If this is the case, return to :map scanning state. + state = :map if state == :initial && brace_depth >= 1 + + when :function_expected + if match = scan(/\(.*?\)/m) # x = function() # "Anonymous" function without explicit name + encoder.text_token(match, :operator) + state = :initial + elsif match = scan(/[a-zA-Z_] (?:[a-zA-Z0-9_\.] (?!\.\d))* [\.\:]/x) # function tbl.subtbl.foo() | function tbl:foo() # Colon only allowed as last separator + encoder.text_token(match, :ident) + elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/) # function foo() + encoder.text_token(match, :function) + state = :initial + elsif match = scan(/\s+/) # Between the `function' keyword and the ident may be any amount of whitespace + encoder.text_token(match, :space) + else + encoder.text_token(getch, :error) + state = :initial + end + + when :goto_label_expected + if match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/) + encoder.text_token(match, :label) + state = :initial + elsif match = scan(/\s+/) # Between the `goto' keyword and the label may be any amount of whitespace + encoder.text_token(match, :space) + else + encoder.text_token(getch, :error) + end + + when :local_var_expected + if match = scan(/function/) # local function ... + encoder.text_token(match, :keyword) + state = :function_expected + elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/) + encoder.text_token(match, :local_variable) + elsif match = scan(/,/) + encoder.text_token(match, :operator) + elsif match = scan(/\=/) + encoder.text_token(match, :operator) + # After encountering the equal sign, arbitrary expressions are + # allowed again, so just return to the main state for further + # parsing. + state = :initial + elsif match = scan(/\n/) + encoder.text_token(match, :space) + state = :initial + elsif match = scan(/\s+/) + encoder.text_token(match, :space) + else + encoder.text_token(getch, :error) + end + + when :long_comment + if match = scan(/.*?(?=\]={#{num_equals}}\])/m) + encoder.text_token(match, :content) + + delim = scan(/\]={#{num_equals}}\]/) + encoder.text_token(delim, :delimiter) + else # No terminator found till EOF + encoder.text_token(rest, :error) + terminate + end + encoder.end_group(:comment) + state = :initial + + when :long_string + if match = scan(/.*?(?=\]={#{num_equals}}\])/m) # Long strings do not interpret any escape sequences + encoder.text_token(match, :content) + + delim = scan(/\]={#{num_equals}}\]/) + encoder.text_token(delim, :delimiter) + else # No terminator found till EOF + encoder.text_token(rest, :error) + terminate + end + encoder.end_group(:string) + state = :initial + + when :string + if match = scan(/[^\\#{start_delim}\n]+/) # Everything except \ and the start delimiter character is string content (newlines are only allowed if preceeded by \ or \z) + encoder.text_token(match, :content) + elsif match = scan(/\\(?:['"abfnrtv\\]|z\s*|x\h\h|\d{1,3}|\n)/m) + encoder.text_token(match, :char) + elsif match = scan(Regexp.compile(start_delim)) + encoder.text_token(match, :delimiter) + encoder.end_group(:string) + state = :initial + elsif match = scan(/\n/) # Lua forbids unescaped newlines in normal non-long strings + encoder.text_token("\\n\n", :error) # Visually appealing error indicator--otherwise users may wonder whether the highlighter cannot highlight multine strings + encoder.end_group(:string) + state = :initial + else + encoder.text_token(getch, :error) + end + + when :map + if match = scan(/[,;]/) + encoder.text_token(match, :operator) + elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]* (?=\s*=)/x) + encoder.text_token(match, :key) + encoder.text_token(scan(/\s+/), :space) if check(/\s+/) + encoder.text_token(scan(/\=/), :operator) + state = :initial + elsif match = scan(/\s+/m) + encoder.text_token(match, :space) + else + # Note this clause doesn’t advance the scan pointer, it’s a kind of + # "retry with other options" (the :initial state then of course + # advances the pointer). + state = :initial + end + else + raise + end + + end + + if options[:keep_state] + @state = state + end + + encoder.end_group :string if [:string].include? state + brace_depth.times { encoder.end_group :map } + + encoder + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/php.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/php.rb new file mode 100644 index 0000000..7a8d75d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/php.rb @@ -0,0 +1,527 @@ +# encoding: utf-8 +module CodeRay +module Scanners + + load :html + + # Scanner for PHP. + # + # Original by Stefan Walk. + class PHP < Scanner + + register_for :php + file_extension 'php' + + KINDS_NOT_LOC = HTML::KINDS_NOT_LOC + + protected + + def setup + @html_scanner = CodeRay.scanner :html, :tokens => @tokens, :keep_tokens => true, :keep_state => true + end + + def reset_instance + super + @html_scanner.reset + end + + module Words # :nodoc: + + # according to http://www.php.net/manual/en/reserved.keywords.php + KEYWORDS = %w[ + abstract and array as break case catch class clone const continue declare default do else elseif + enddeclare endfor endforeach endif endswitch endwhile extends final for foreach function global + goto if implements interface instanceof namespace new or private protected public static switch + throw try use var while xor + cfunction old_function + ] + + TYPES = %w[ int integer float double bool boolean string array object resource ] + + LANGUAGE_CONSTRUCTS = %w[ + die echo empty exit eval include include_once isset list + require require_once return print unset + ] + + CLASSES = %w[ Directory stdClass __PHP_Incomplete_Class exception php_user_filter Closure ] + + # according to http://php.net/quickref.php on 2009-04-21; + # all functions with _ excluded (module functions) and selected additional functions + BUILTIN_FUNCTIONS = %w[ + abs acos acosh addcslashes addslashes aggregate array arsort ascii2ebcdic asin asinh asort assert atan atan2 + atanh basename bcadd bccomp bcdiv bcmod bcmul bcpow bcpowmod bcscale bcsqrt bcsub bin2hex bindec + bindtextdomain bzclose bzcompress bzdecompress bzerrno bzerror bzerrstr bzflush bzopen bzread bzwrite + calculhmac ceil chdir checkdate checkdnsrr chgrp chmod chop chown chr chroot clearstatcache closedir closelog + compact constant copy cos cosh count crc32 crypt current date dcgettext dcngettext deaggregate decbin dechex + decoct define defined deg2rad delete dgettext die dirname diskfreespace dl dngettext doubleval each + ebcdic2ascii echo empty end ereg eregi escapeshellarg escapeshellcmd eval exec exit exp explode expm1 extract + fclose feof fflush fgetc fgetcsv fgets fgetss file fileatime filectime filegroup fileinode filemtime fileowner + fileperms filepro filesize filetype floatval flock floor flush fmod fnmatch fopen fpassthru fprintf fputcsv + fputs fread frenchtojd fscanf fseek fsockopen fstat ftell ftok ftruncate fwrite getallheaders getcwd getdate + getenv gethostbyaddr gethostbyname gethostbynamel getimagesize getlastmod getmxrr getmygid getmyinode getmypid + getmyuid getopt getprotobyname getprotobynumber getrandmax getrusage getservbyname getservbyport gettext + gettimeofday gettype glob gmdate gmmktime gmstrftime gregoriantojd gzclose gzcompress gzdecode gzdeflate + gzencode gzeof gzfile gzgetc gzgets gzgetss gzinflate gzopen gzpassthru gzputs gzread gzrewind gzseek gztell + gzuncompress gzwrite hash header hebrev hebrevc hexdec htmlentities htmlspecialchars hypot iconv idate + implode include intval ip2long iptcembed iptcparse isset + jddayofweek jdmonthname jdtofrench jdtogregorian jdtojewish jdtojulian jdtounix jewishtojd join jpeg2wbmp + juliantojd key krsort ksort lcfirst lchgrp lchown levenshtein link linkinfo list localeconv localtime log + log10 log1p long2ip lstat ltrim mail main max md5 metaphone mhash microtime min mkdir mktime msql natcasesort + natsort next ngettext nl2br nthmac octdec opendir openlog + ord overload pack passthru pathinfo pclose pfsockopen phpcredits phpinfo phpversion pi png2wbmp popen pos pow + prev print printf putenv quotemeta rad2deg rand range rawurldecode rawurlencode readdir readfile readgzfile + readline readlink realpath recode rename require reset rewind rewinddir rmdir round rsort rtrim scandir + serialize setcookie setlocale setrawcookie settype sha1 shuffle signeurlpaiement sin sinh sizeof sleep snmpget + snmpgetnext snmprealwalk snmpset snmpwalk snmpwalkoid sort soundex split spliti sprintf sqrt srand sscanf stat + strcasecmp strchr strcmp strcoll strcspn strftime stripcslashes stripos stripslashes stristr strlen + strnatcasecmp strnatcmp strncasecmp strncmp strpbrk strpos strptime strrchr strrev strripos strrpos strspn + strstr strtok strtolower strtotime strtoupper strtr strval substr symlink syslog system tan tanh tempnam + textdomain time tmpfile touch trim uasort ucfirst ucwords uksort umask uniqid unixtojd unlink unpack + unserialize unset urldecode urlencode usleep usort vfprintf virtual vprintf vsprintf wordwrap + array_change_key_case array_chunk array_combine array_count_values array_diff array_diff_assoc + array_diff_key array_diff_uassoc array_diff_ukey array_fill array_fill_keys array_filter array_flip + array_intersect array_intersect_assoc array_intersect_key array_intersect_uassoc array_intersect_ukey + array_key_exists array_keys array_map array_merge array_merge_recursive array_multisort array_pad + array_pop array_product array_push array_rand array_reduce array_reverse array_search array_shift + array_slice array_splice array_sum array_udiff array_udiff_assoc array_udiff_uassoc array_uintersect + array_uintersect_assoc array_uintersect_uassoc array_unique array_unshift array_values array_walk + array_walk_recursive + assert_options base_convert base64_decode base64_encode + chunk_split class_exists class_implements class_parents + count_chars debug_backtrace debug_print_backtrace debug_zval_dump + error_get_last error_log error_reporting extension_loaded + file_exists file_get_contents file_put_contents load_file + func_get_arg func_get_args func_num_args function_exists + get_browser get_called_class get_cfg_var get_class get_class_methods get_class_vars + get_current_user get_declared_classes get_declared_interfaces get_defined_constants + get_defined_functions get_defined_vars get_extension_funcs get_headers get_html_translation_table + get_include_path get_included_files get_loaded_extensions get_magic_quotes_gpc get_magic_quotes_runtime + get_meta_tags get_object_vars get_parent_class get_required_filesget_resource_type + gc_collect_cycles gc_disable gc_enable gc_enabled + halt_compiler headers_list headers_sent highlight_file highlight_string + html_entity_decode htmlspecialchars_decode + in_array include_once inclued_get_data + is_a is_array is_binary is_bool is_buffer is_callable is_dir is_double is_executable is_file is_finite + is_float is_infinite is_int is_integer is_link is_long is_nan is_null is_numeric is_object is_readable + is_real is_resource is_scalar is_soap_fault is_string is_subclass_of is_unicode is_uploaded_file + is_writable is_writeable + locale_get_default locale_set_default + number_format override_function parse_str parse_url + php_check_syntax php_ini_loaded_file php_ini_scanned_files php_logo_guid php_sapi_name + php_strip_whitespace php_uname + preg_filter preg_grep preg_last_error preg_match preg_match_all preg_quote preg_replace + preg_replace_callback preg_split print_r + require_once register_shutdown_function register_tick_function + set_error_handler set_exception_handler set_file_buffer set_include_path + set_magic_quotes_runtime set_time_limit shell_exec + str_getcsv str_ireplace str_pad str_repeat str_replace str_rot13 str_shuffle str_split str_word_count + strip_tags substr_compare substr_count substr_replace + time_nanosleep time_sleep_until + token_get_all token_name trigger_error + unregister_tick_function use_soap_error_handler user_error + utf8_decode utf8_encode var_dump var_export + version_compare + zend_logo_guid zend_thread_id zend_version + create_function call_user_func_array + posix_access posix_ctermid posix_get_last_error posix_getcwd posix_getegid + posix_geteuid posix_getgid posix_getgrgid posix_getgrnam posix_getgroups + posix_getlogin posix_getpgid posix_getpgrp posix_getpid posix_getppid + posix_getpwnam posix_getpwuid posix_getrlimit posix_getsid posix_getuid + posix_initgroups posix_isatty posix_kill posix_mkfifo posix_mknod + posix_setegid posix_seteuid posix_setgid posix_setpgid posix_setsid + posix_setuid posix_strerror posix_times posix_ttyname posix_uname + pcntl_alarm pcntl_exec pcntl_fork pcntl_getpriority pcntl_setpriority + pcntl_signal pcntl_signal_dispatch pcntl_sigprocmask pcntl_sigtimedwait + pcntl_sigwaitinfo pcntl_wait pcntl_waitpid pcntl_wexitstatus pcntl_wifexited + pcntl_wifsignaled pcntl_wifstopped pcntl_wstopsig pcntl_wtermsig + ] + # TODO: more built-in PHP functions? + + EXCEPTIONS = %w[ + E_ERROR E_WARNING E_PARSE E_NOTICE E_CORE_ERROR E_CORE_WARNING E_COMPILE_ERROR E_COMPILE_WARNING + E_USER_ERROR E_USER_WARNING E_USER_NOTICE E_DEPRECATED E_USER_DEPRECATED E_ALL E_STRICT + ] + + CONSTANTS = %w[ + null true false self parent + __LINE__ __DIR__ __FILE__ __LINE__ + __CLASS__ __NAMESPACE__ __METHOD__ __FUNCTION__ + PHP_VERSION PHP_MAJOR_VERSION PHP_MINOR_VERSION PHP_RELEASE_VERSION PHP_VERSION_ID PHP_EXTRA_VERSION PHP_ZTS + PHP_DEBUG PHP_MAXPATHLEN PHP_OS PHP_SAPI PHP_EOL PHP_INT_MAX PHP_INT_SIZE DEFAULT_INCLUDE_PATH + PEAR_INSTALL_DIR PEAR_EXTENSION_DIR PHP_EXTENSION_DIR PHP_PREFIX PHP_BINDIR PHP_LIBDIR PHP_DATADIR + PHP_SYSCONFDIR PHP_LOCALSTATEDIR PHP_CONFIG_FILE_PATH PHP_CONFIG_FILE_SCAN_DIR PHP_SHLIB_SUFFIX + PHP_OUTPUT_HANDLER_START PHP_OUTPUT_HANDLER_CONT PHP_OUTPUT_HANDLER_END + __COMPILER_HALT_OFFSET__ + EXTR_OVERWRITE EXTR_SKIP EXTR_PREFIX_SAME EXTR_PREFIX_ALL EXTR_PREFIX_INVALID EXTR_PREFIX_IF_EXISTS + EXTR_IF_EXISTS SORT_ASC SORT_DESC SORT_REGULAR SORT_NUMERIC SORT_STRING CASE_LOWER CASE_UPPER COUNT_NORMAL + COUNT_RECURSIVE ASSERT_ACTIVE ASSERT_CALLBACK ASSERT_BAIL ASSERT_WARNING ASSERT_QUIET_EVAL CONNECTION_ABORTED + CONNECTION_NORMAL CONNECTION_TIMEOUT INI_USER INI_PERDIR INI_SYSTEM INI_ALL M_E M_LOG2E M_LOG10E M_LN2 M_LN10 + M_PI M_PI_2 M_PI_4 M_1_PI M_2_PI M_2_SQRTPI M_SQRT2 M_SQRT1_2 CRYPT_SALT_LENGTH CRYPT_STD_DES CRYPT_EXT_DES + CRYPT_MD5 CRYPT_BLOWFISH DIRECTORY_SEPARATOR SEEK_SET SEEK_CUR SEEK_END LOCK_SH LOCK_EX LOCK_UN LOCK_NB + HTML_SPECIALCHARS HTML_ENTITIES ENT_COMPAT ENT_QUOTES ENT_NOQUOTES INFO_GENERAL INFO_CREDITS + INFO_CONFIGURATION INFO_MODULES INFO_ENVIRONMENT INFO_VARIABLES INFO_LICENSE INFO_ALL CREDITS_GROUP + CREDITS_GENERAL CREDITS_SAPI CREDITS_MODULES CREDITS_DOCS CREDITS_FULLPAGE CREDITS_QA CREDITS_ALL STR_PAD_LEFT + STR_PAD_RIGHT STR_PAD_BOTH PATHINFO_DIRNAME PATHINFO_BASENAME PATHINFO_EXTENSION PATH_SEPARATOR CHAR_MAX + LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_ALL LC_MESSAGES ABDAY_1 ABDAY_2 ABDAY_3 ABDAY_4 ABDAY_5 + ABDAY_6 ABDAY_7 DAY_1 DAY_2 DAY_3 DAY_4 DAY_5 DAY_6 DAY_7 ABMON_1 ABMON_2 ABMON_3 ABMON_4 ABMON_5 ABMON_6 + ABMON_7 ABMON_8 ABMON_9 ABMON_10 ABMON_11 ABMON_12 MON_1 MON_2 MON_3 MON_4 MON_5 MON_6 MON_7 MON_8 MON_9 + MON_10 MON_11 MON_12 AM_STR PM_STR D_T_FMT D_FMT T_FMT T_FMT_AMPM ERA ERA_YEAR ERA_D_T_FMT ERA_D_FMT ERA_T_FMT + ALT_DIGITS INT_CURR_SYMBOL CURRENCY_SYMBOL CRNCYSTR MON_DECIMAL_POINT MON_THOUSANDS_SEP MON_GROUPING + POSITIVE_SIGN NEGATIVE_SIGN INT_FRAC_DIGITS FRAC_DIGITS P_CS_PRECEDES P_SEP_BY_SPACE N_CS_PRECEDES + N_SEP_BY_SPACE P_SIGN_POSN N_SIGN_POSN DECIMAL_POINT RADIXCHAR THOUSANDS_SEP THOUSEP GROUPING YESEXPR NOEXPR + YESSTR NOSTR CODESET LOG_EMERG LOG_ALERT LOG_CRIT LOG_ERR LOG_WARNING LOG_NOTICE LOG_INFO LOG_DEBUG LOG_KERN + LOG_USER LOG_MAIL LOG_DAEMON LOG_AUTH LOG_SYSLOG LOG_LPR LOG_NEWS LOG_UUCP LOG_CRON LOG_AUTHPRIV LOG_LOCAL0 + LOG_LOCAL1 LOG_LOCAL2 LOG_LOCAL3 LOG_LOCAL4 LOG_LOCAL5 LOG_LOCAL6 LOG_LOCAL7 LOG_PID LOG_CONS LOG_ODELAY + LOG_NDELAY LOG_NOWAIT LOG_PERROR + ] + + PREDEFINED = %w[ + $GLOBALS $_SERVER $_GET $_POST $_FILES $_REQUEST $_SESSION $_ENV + $_COOKIE $php_errormsg $HTTP_RAW_POST_DATA $http_response_header + $argc $argv + ] + + IDENT_KIND = WordList::CaseIgnoring.new(:ident). + add(KEYWORDS, :keyword). + add(TYPES, :predefined_type). + add(LANGUAGE_CONSTRUCTS, :keyword). + add(BUILTIN_FUNCTIONS, :predefined). + add(CLASSES, :predefined_constant). + add(EXCEPTIONS, :exception). + add(CONSTANTS, :predefined_constant) + + VARIABLE_KIND = WordList.new(:local_variable). + add(PREDEFINED, :predefined) + end + + module RE # :nodoc: + + PHP_START = / + ]*?language\s*=\s*"php"[^>]*?> | + ]*?language\s*=\s*'php'[^>]*?> | + <\?php\d? | + <\?(?!xml) + /xi + + PHP_END = %r! + | + \?> + !xi + + HTML_INDICATOR = / ]/i + + IDENTIFIER = 'ä'[/[[:alpha:]]/] == 'ä' ? Regexp.new('[[:alpha:]_[^\0-\177]][[:alnum:]_[^\0-\177]]*') : Regexp.new('[a-z_\x7f-\xFF][a-z0-9_\x7f-\xFF]*', true) + VARIABLE = /\$#{IDENTIFIER}/ + + OPERATOR = / + \.(?!\d)=? | # dot that is not decimal point, string concatenation + && | \|\| | # logic + :: | -> | => | # scope, member, dictionary + \\(?!\n) | # namespace + \+\+ | -- | # increment, decrement + [,;?:()\[\]{}] | # simple delimiters + [-+*\/%&|^]=? | # ordinary math, binary logic, assignment shortcuts + [~$] | # whatever + =& | # reference assignment + [=!]=?=? | <> | # comparison and assignment + <<=? | >>=? | [<>]=? # comparison and shift + /x + + end + + protected + + def scan_tokens encoder, options + + if check(RE::PHP_START) || # starts with #{RE::IDENTIFIER}/o) + encoder.begin_group :inline + encoder.text_token match, :local_variable + encoder.text_token scan(/->/), :operator + encoder.text_token scan(/#{RE::IDENTIFIER}/o), :ident + encoder.end_group :inline + elsif check(/->/) + match << scan(/->/) + encoder.text_token match, :error + else + encoder.text_token match, :local_variable + end + elsif match = scan(/\{/) + if check(/\$/) + encoder.begin_group :inline + states[-1] = [states.last, delimiter] + delimiter = nil + states.push :php_inline + encoder.text_token match, :delimiter + else + encoder.text_token match, :content + end + elsif match = scan(/\$\{#{RE::IDENTIFIER}\}/o) + encoder.text_token match, :local_variable + elsif match = scan(/\$/) + encoder.text_token match, :content + else + encoder.end_group :string + states.pop + end + + when :class_expected + if match = scan(/\s+/) + encoder.text_token match, :space + elsif match = scan(/#{RE::IDENTIFIER}/o) + encoder.text_token match, :class + states.pop + else + states.pop + end + + when :function_expected + if match = scan(/\s+/) + encoder.text_token match, :space + elsif match = scan(/&/) + encoder.text_token match, :operator + elsif match = scan(/#{RE::IDENTIFIER}/o) + encoder.text_token match, :function + states.pop + else + states.pop + end + + else + raise_inspect 'Unknown state!', encoder, states + end + + end + + while state = states.pop + encoder.end_group :string if [:sqstring, :dqstring].include? state + if state.is_a? Array + encoder.end_group :inline + encoder.end_group :string if [:sqstring, :dqstring].include? state.first + end + end + + encoder + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/python.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/python.rb new file mode 100644 index 0000000..5da553a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/python.rb @@ -0,0 +1,287 @@ +module CodeRay +module Scanners + + # Scanner for Python. Supports Python 3. + # + # Based on pygments' PythonLexer, see + # http://dev.pocoo.org/projects/pygments/browser/pygments/lexers/agile.py. + class Python < Scanner + + register_for :python + file_extension 'py' + + KEYWORDS = [ + 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', + 'del', 'elif', 'else', 'except', 'finally', 'for', + 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'not', + 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield', + 'nonlocal', # new in Python 3 + ] # :nodoc: + + OLD_KEYWORDS = [ + 'exec', 'print', # gone in Python 3 + ] # :nodoc: + + PREDEFINED_METHODS_AND_TYPES = %w[ + __import__ abs all any apply basestring bin bool buffer + bytearray bytes callable chr classmethod cmp coerce compile + complex delattr dict dir divmod enumerate eval execfile exit + file filter float frozenset getattr globals hasattr hash hex id + input int intern isinstance issubclass iter len list locals + long map max min next object oct open ord pow property range + raw_input reduce reload repr reversed round set setattr slice + sorted staticmethod str sum super tuple type unichr unicode + vars xrange zip + ] # :nodoc: + + PREDEFINED_EXCEPTIONS = %w[ + ArithmeticError AssertionError AttributeError + BaseException DeprecationWarning EOFError EnvironmentError + Exception FloatingPointError FutureWarning GeneratorExit IOError + ImportError ImportWarning IndentationError IndexError KeyError + KeyboardInterrupt LookupError MemoryError NameError + NotImplemented NotImplementedError OSError OverflowError + OverflowWarning PendingDeprecationWarning ReferenceError + RuntimeError RuntimeWarning StandardError StopIteration + SyntaxError SyntaxWarning SystemError SystemExit TabError + TypeError UnboundLocalError UnicodeDecodeError + UnicodeEncodeError UnicodeError UnicodeTranslateError + UnicodeWarning UserWarning ValueError Warning ZeroDivisionError + ] # :nodoc: + + PREDEFINED_VARIABLES_AND_CONSTANTS = [ + 'False', 'True', 'None', # "keywords" since Python 3 + 'self', 'Ellipsis', 'NotImplemented', + ] # :nodoc: + + IDENT_KIND = WordList.new(:ident). + add(KEYWORDS, :keyword). + add(OLD_KEYWORDS, :old_keyword). + add(PREDEFINED_METHODS_AND_TYPES, :predefined). + add(PREDEFINED_VARIABLES_AND_CONSTANTS, :predefined_constant). + add(PREDEFINED_EXCEPTIONS, :exception) # :nodoc: + + NAME = / [[:alpha:]_] \w* /x # :nodoc: + ESCAPE = / [abfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x # :nodoc: + UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} | N\{[-\w ]+\} /x # :nodoc: + + OPERATOR = / + \.\.\. | # ellipsis + \.(?!\d) | # dot but not decimal point + [,;:()\[\]{}] | # simple delimiters + \/\/=? | \*\*=? | # special math + [-+*\/%&|^]=? | # ordinary math and binary logic + [~`] | # binary complement and inspection + <<=? | >>=? | [<>=]=? | != # comparison and assignment + /x # :nodoc: + + STRING_DELIMITER_REGEXP = Hash.new { |h, delimiter| + h[delimiter] = Regexp.union delimiter # :nodoc: + } + + STRING_CONTENT_REGEXP = Hash.new { |h, delimiter| + h[delimiter] = / [^\\\n]+? (?= \\ | $ | #{Regexp.escape(delimiter)} ) /x # :nodoc: + } + + DEF_NEW_STATE = WordList.new(:initial). + add(%w(def), :def_expected). + add(%w(import from), :include_expected). + add(%w(class), :class_expected) # :nodoc: + + DESCRIPTOR = / + #{NAME} + (?: \. #{NAME} )* + | \* + /x # :nodoc: + + DOCSTRING_COMING = / + [ \t]* u?r? ("""|''') + /x # :nodoc: + + protected + + def scan_tokens encoder, options + + state = :initial + string_delimiter = nil + string_raw = false + string_type = nil + docstring_coming = match?(/#{DOCSTRING_COMING}/o) + last_token_dot = false + unicode = string.respond_to?(:encoding) && string.encoding.name == 'UTF-8' + from_import_state = [] + + until eos? + + if state == :string + if match = scan(STRING_DELIMITER_REGEXP[string_delimiter]) + encoder.text_token match, :delimiter + encoder.end_group string_type + string_type = nil + state = :initial + next + elsif string_delimiter.size == 3 && match = scan(/\n/) + encoder.text_token match, :content + elsif match = scan(STRING_CONTENT_REGEXP[string_delimiter]) + encoder.text_token match, :content + elsif !string_raw && match = scan(/ \\ #{ESCAPE} /ox) + encoder.text_token match, :char + elsif match = scan(/ \\ #{UNICODE_ESCAPE} /ox) + encoder.text_token match, :char + elsif match = scan(/ \\ . /x) + encoder.text_token match, :content + elsif match = scan(/ \\ | $ /x) + encoder.end_group string_type + string_type = nil + encoder.text_token match, :error unless match.empty? + state = :initial + else + raise_inspect "else case \" reached; %p not handled." % peek(1), encoder, state + end + + elsif match = scan(/ [ \t]+ | \\?\n /x) + encoder.text_token match, :space + if match == "\n" + state = :initial if state == :include_expected + docstring_coming = true if match?(/#{DOCSTRING_COMING}/o) + end + next + + elsif match = scan(/ \# [^\n]* /mx) + encoder.text_token match, :comment + next + + elsif state == :initial + + if match = scan(/#{OPERATOR}/o) + encoder.text_token match, :operator + + elsif match = scan(/(u?r?|b)?("""|"|'''|')/i) + modifiers = self[1] + string_delimiter = self[2] + string_type = docstring_coming ? :docstring : (modifiers == 'b' ? :binary : :string) + docstring_coming = false if docstring_coming + encoder.begin_group string_type + string_raw = false + unless modifiers.empty? + string_raw = !!modifiers.index(?r) + encoder.text_token modifiers, :modifier + match = string_delimiter + end + state = :string + encoder.text_token match, :delimiter + + # TODO: backticks + + elsif match = scan(unicode ? /#{NAME}/uo : /#{NAME}/o) + kind = IDENT_KIND[match] + # TODO: keyword arguments + kind = :ident if last_token_dot + if kind == :old_keyword + kind = check(/\(/) ? :ident : :keyword + elsif kind == :predefined && check(/ *=/) + kind = :ident + elsif kind == :keyword + state = DEF_NEW_STATE[match] + from_import_state << match.to_sym if state == :include_expected + end + encoder.text_token match, kind + + elsif match = scan(/@[a-zA-Z0-9_.]+[lL]?/) + encoder.text_token match, :decorator + + elsif match = scan(/0[xX][0-9A-Fa-f]+[lL]?/) + encoder.text_token match, :hex + + elsif match = scan(/0[bB][01]+[lL]?/) + encoder.text_token match, :binary + + elsif match = scan(/(?:\d*\.\d+|\d+\.\d*)(?:[eE][+-]?\d+)?|\d+[eE][+-]?\d+/) + if scan(/[jJ]/) + match << matched + encoder.text_token match, :imaginary + else + encoder.text_token match, :float + end + + elsif match = scan(/0[oO][0-7]+|0[0-7]+(?![89.eE])[lL]?/) + encoder.text_token match, :octal + + elsif match = scan(/\d+([lL])?/) + if self[1] == nil && scan(/[jJ]/) + match << matched + encoder.text_token match, :imaginary + else + encoder.text_token match, :integer + end + + else + encoder.text_token getch, :error + + end + + elsif state == :def_expected + state = :initial + if match = scan(unicode ? /#{NAME}/uo : /#{NAME}/o) + encoder.text_token match, :method + else + next + end + + elsif state == :class_expected + state = :initial + if match = scan(unicode ? /#{NAME}/uo : /#{NAME}/o) + encoder.text_token match, :class + else + next + end + + elsif state == :include_expected + if match = scan(unicode ? /#{DESCRIPTOR}/uo : /#{DESCRIPTOR}/o) + if match == 'as' + encoder.text_token match, :keyword + from_import_state << :as + elsif from_import_state.first == :from && match == 'import' + encoder.text_token match, :keyword + from_import_state << :import + elsif from_import_state.last == :as + # encoder.text_token match, match[0,1][unicode ? /[[:upper:]]/u : /[[:upper:]]/] ? :class : :method + encoder.text_token match, :ident + from_import_state.pop + elsif IDENT_KIND[match] == :keyword + unscan + match = nil + state = :initial + next + else + encoder.text_token match, :include + end + elsif match = scan(/,/) + from_import_state.pop if from_import_state.last == :as + encoder.text_token match, :operator + else + from_import_state = [] + state = :initial + next + end + + else + raise_inspect 'Unknown state', encoder, state + + end + + last_token_dot = match == '.' + + end + + if state == :string + encoder.end_group string_type + end + + encoder + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/raydebug.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/raydebug.rb new file mode 100644 index 0000000..1effdc8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/raydebug.rb @@ -0,0 +1,75 @@ +require 'set' + +module CodeRay +module Scanners + + # = Raydebug Scanner + # + # Highlights the output of the Encoders::Debug encoder. + class Raydebug < Scanner + + register_for :raydebug + file_extension 'raydebug' + title 'CodeRay Token Dump' + + protected + + def setup + super + @known_token_kinds = TokenKinds.keys.map(&:to_s).to_set + end + + def scan_tokens encoder, options + + opened_tokens = [] + + until eos? + + if match = scan(/\s+/) + encoder.text_token match, :space + + elsif match = scan(/ (\w+) \( ( [^\)\\]* ( \\. [^\)\\]* )* ) /x) + kind = self[1] + encoder.text_token kind, :class + encoder.text_token '(', :operator + match = self[2] + unless match.empty? + if @known_token_kinds.include? kind + encoder.text_token match, kind.to_sym + else + encoder.text_token match, :plain + end + end + encoder.text_token match, :operator if match = scan(/\)/) + + elsif match = scan(/ (\w+) ([<\[]) /x) + encoder.text_token self[1], :class + if @known_token_kinds.include? self[1] + kind = self[1].to_sym + else + kind = :unknown + end + opened_tokens << kind + encoder.begin_group kind + encoder.text_token self[2], :operator + + elsif !opened_tokens.empty? && match = scan(/ [>\]] /x) + encoder.text_token match, :operator + encoder.end_group opened_tokens.pop + + else + encoder.text_token getch, :space + + end + + end + + encoder.end_group opened_tokens.pop until opened_tokens.empty? + + encoder + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/ruby.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/ruby.rb new file mode 100644 index 0000000..5b8de42 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/ruby.rb @@ -0,0 +1,477 @@ +module CodeRay +module Scanners + + # This scanner is really complex, since Ruby _is_ a complex language! + # + # It tries to highlight 100% of all common code, + # and 90% of strange codes. + # + # It is optimized for HTML highlighting, and is not very useful for + # parsing or pretty printing. + class Ruby < Scanner + + register_for :ruby + file_extension 'rb' + + autoload :Patterns, CodeRay.coderay_path('scanners', 'ruby', 'patterns') + autoload :StringState, CodeRay.coderay_path('scanners', 'ruby', 'string_state') + + def interpreted_string_state + StringState.new :string, true, '"' + end + + protected + + def setup + @state = :initial + end + + def scan_tokens encoder, options + state, heredocs = options[:state] || @state + heredocs = heredocs.dup if heredocs.is_a?(Array) + + if state && state.instance_of?(StringState) + encoder.begin_group state.type + end + + last_state = nil + + method_call_expected = false + value_expected = true + + inline_block_stack = nil + inline_block_curly_depth = 0 + + if heredocs + state = heredocs.shift + encoder.begin_group state.type + heredocs = nil if heredocs.empty? + end + + # def_object_stack = nil + # def_object_paren_depth = 0 + + patterns = Patterns # avoid constant lookup + + unicode = string.respond_to?(:encoding) && string.encoding.name == 'UTF-8' + + until eos? + + if state.instance_of? ::Symbol + + if match = scan(/[ \t\f\v]+/) + encoder.text_token match, :space + + elsif match = scan(/\n/) + if heredocs + unscan # heredoc scanning needs \n at start + state = heredocs.shift + encoder.begin_group state.type + heredocs = nil if heredocs.empty? + else + state = :initial if state == :undef_comma_expected + encoder.text_token match, :space + value_expected = true + end + + elsif match = scan(bol? ? / \#(!)?.* | #{patterns::RUBYDOC_OR_DATA} /ox : /\#.*/) + encoder.text_token match, self[1] ? :doctype : :comment + + elsif match = scan(/\\\n/) + if heredocs + unscan # heredoc scanning needs \n at start + encoder.text_token scan(/\\/), :space + state = heredocs.shift + encoder.begin_group state.type + heredocs = nil if heredocs.empty? + else + encoder.text_token match, :space + end + + elsif state == :initial + + # IDENTS # + if !method_call_expected && + match = scan(unicode ? /#{patterns::METHOD_NAME}/uo : + /#{patterns::METHOD_NAME}/o) + + kind = patterns::IDENT_KIND[match] + if value_expected != :colon_expected && scan(/:(?!:)/) + value_expected = true + encoder.text_token match, :key + encoder.text_token ':', :operator + else + value_expected = false + if kind == :ident + if match[/\A[A-Z]/] && !(match[/[!?]$/] || match?(/\(/)) + kind = :constant + end + elsif kind == :keyword + state = patterns::KEYWORD_NEW_STATE[match] + if patterns::KEYWORDS_EXPECTING_VALUE[match] + value_expected = match == 'when' ? :colon_expected : true + end + end + value_expected = true if !value_expected && check(/#{patterns::VALUE_FOLLOWS}/o) + encoder.text_token match, kind + end + + elsif method_call_expected && + match = scan(unicode ? /#{patterns::METHOD_AFTER_DOT}/uo : + /#{patterns::METHOD_AFTER_DOT}/o) + if method_call_expected == '::' && match[/\A[A-Z]/] && !match?(/\(/) + encoder.text_token match, :constant + else + encoder.text_token match, :ident + end + method_call_expected = false + value_expected = check(/#{patterns::VALUE_FOLLOWS}/o) + + # OPERATORS # + elsif !method_call_expected && match = scan(/ (\.(?!\.)|::) | ( \.\.\.? | ==?=? | [,\(\[\{] ) | [\)\]\}] /x) + method_call_expected = self[1] + value_expected = !method_call_expected && !!self[2] + if inline_block_stack + case match + when '{' + inline_block_curly_depth += 1 + when '}' + inline_block_curly_depth -= 1 + if inline_block_curly_depth == 0 # closing brace of inline block reached + state, inline_block_curly_depth, heredocs = inline_block_stack.pop + inline_block_stack = nil if inline_block_stack.empty? + heredocs = nil if heredocs && heredocs.empty? + encoder.text_token match, :inline_delimiter + encoder.end_group :inline + next + end + end + end + encoder.text_token match, :operator + + elsif match = scan(unicode ? /#{patterns::SYMBOL}/uo : + /#{patterns::SYMBOL}/o) + case delim = match[1] + when ?', ?" + encoder.begin_group :symbol + encoder.text_token ':', :symbol + match = delim.chr + encoder.text_token match, :delimiter + state = self.class::StringState.new :symbol, delim == ?", match + else + encoder.text_token match, :symbol + value_expected = false + end + + elsif match = scan(/ ' (?:(?>[^'\\]*) ')? | " (?:(?>[^"\\\#]*) ")? /mx) + if match.size == 1 + kind = check(self.class::StringState.simple_key_pattern(match)) ? :key : :string + encoder.begin_group kind + encoder.text_token match, :delimiter + state = self.class::StringState.new kind, match == '"', match # important for streaming + else + kind = value_expected == true && scan(/:/) ? :key : :string + encoder.begin_group kind + encoder.text_token match[0,1], :delimiter + encoder.text_token match[1..-2], :content if match.size > 2 + encoder.text_token match[-1,1], :delimiter + encoder.end_group kind + encoder.text_token ':', :operator if kind == :key + value_expected = false + end + + elsif match = scan(unicode ? /#{patterns::INSTANCE_VARIABLE}/uo : + /#{patterns::INSTANCE_VARIABLE}/o) + value_expected = false + encoder.text_token match, :instance_variable + + elsif value_expected && match = scan(/\//) + encoder.begin_group :regexp + encoder.text_token match, :delimiter + state = self.class::StringState.new :regexp, true, '/' + + elsif match = scan(value_expected ? /[-+]?#{patterns::NUMERIC}/o : /#{patterns::NUMERIC}/o) + if method_call_expected + encoder.text_token match, :error + method_call_expected = false + else + kind = self[1] ? :float : :integer # TODO: send :hex/:octal/:binary + match << 'r' if match !~ /e/i && scan(/r/) + match << 'i' if scan(/i/) + encoder.text_token match, kind + end + value_expected = false + + elsif match = scan(/ [-+!~^\/]=? | [:;] | &\. | [*|&]{1,2}=? | >>? /x) + value_expected = true + encoder.text_token match, :operator + + elsif value_expected && match = scan(/#{patterns::HEREDOC_OPEN}/o) + quote = self[3] + delim = self[quote ? 4 : 2] + kind = patterns::QUOTE_TO_TYPE[quote] + encoder.begin_group kind + encoder.text_token match, :delimiter + encoder.end_group kind + heredocs ||= [] # create heredocs if empty + heredocs << self.class::StringState.new(kind, quote != "'", delim, + self[1] ? :indented : :linestart) + value_expected = false + + elsif value_expected && match = scan(/#{patterns::FANCY_STRING_START}/o) + kind = patterns::FANCY_STRING_KIND[self[1]] + encoder.begin_group kind + state = self.class::StringState.new kind, patterns::FANCY_STRING_INTERPRETED[self[1]], self[2] + encoder.text_token match, :delimiter + + elsif value_expected && match = scan(/#{patterns::CHARACTER}/o) + value_expected = false + encoder.text_token match, :integer + + elsif match = scan(/ %=? | <(?:<|=>?)? | \? /x) + value_expected = match == '?' ? :colon_expected : true + encoder.text_token match, :operator + + elsif match = scan(/`/) + encoder.begin_group :shell + encoder.text_token match, :delimiter + state = self.class::StringState.new :shell, true, match + + elsif match = scan(unicode ? /#{patterns::GLOBAL_VARIABLE}/uo : + /#{patterns::GLOBAL_VARIABLE}/o) + encoder.text_token match, :global_variable + value_expected = false + + elsif match = scan(unicode ? /#{patterns::CLASS_VARIABLE}/uo : + /#{patterns::CLASS_VARIABLE}/o) + encoder.text_token match, :class_variable + value_expected = false + + elsif match = scan(/\\\z/) + encoder.text_token match, :space + + else + if method_call_expected + method_call_expected = false + next + end + unless unicode + # check for unicode + $DEBUG_BEFORE, $DEBUG = $DEBUG, false + begin + if check(/./mu).size > 1 + # seems like we should try again with unicode + unicode = true + end + rescue + # bad unicode char; use getch + ensure + $DEBUG = $DEBUG_BEFORE + end + next if unicode + end + + encoder.text_token getch, :error + + end + + if last_state + state = last_state unless state.is_a?(StringState) # otherwise, a simple 'def"' results in unclosed tokens + last_state = nil + end + + elsif state == :def_expected + if match = scan(unicode ? /(?>#{patterns::METHOD_NAME_EX})(?!\.|::)/uo : + /(?>#{patterns::METHOD_NAME_EX})(?!\.|::)/o) + encoder.text_token match, :method + state = :initial + else + last_state = :dot_expected + state = :initial + end + + elsif state == :dot_expected + if match = scan(/\.|::/) + # invalid definition + state = :def_expected + encoder.text_token match, :operator + else + state = :initial + end + + elsif state == :module_expected + if match = scan(/<#{patterns::METHOD_NAME_EX})(?!\.|::)/uo : + /(?>#{patterns::METHOD_NAME_EX})(?!\.|::)/o) + encoder.text_token match, :method + elsif match = scan(/#{patterns::SYMBOL}/o) + case delim = match[1] + when ?', ?" + encoder.begin_group :symbol + encoder.text_token ':', :symbol + match = delim.chr + encoder.text_token match, :delimiter + state = self.class::StringState.new :symbol, delim == ?", match + state.next_state = :undef_comma_expected + else + encoder.text_token match, :symbol + end + else + state = :initial + end + + elsif state == :undef_comma_expected + if match = scan(/,/) + encoder.text_token match, :operator + state = :undef_expected + else + state = :initial + end + + elsif state == :alias_expected + match = scan(unicode ? /(#{patterns::METHOD_NAME_OR_SYMBOL})([ \t]+)(#{patterns::METHOD_NAME_OR_SYMBOL})/uo : + /(#{patterns::METHOD_NAME_OR_SYMBOL})([ \t]+)(#{patterns::METHOD_NAME_OR_SYMBOL})/o) + + if match + encoder.text_token self[1], (self[1][0] == ?: ? :symbol : :method) + encoder.text_token self[2], :space + encoder.text_token self[3], (self[3][0] == ?: ? :symbol : :method) + end + state = :initial + + else + #:nocov: + raise_inspect 'Unknown state: %p' % [state], encoder + #:nocov: + end + + else # StringState + + match = scan_until(state.pattern) || scan_rest + unless match.empty? + encoder.text_token match, :content + break if eos? + end + + if state.heredoc && self[1] # end of heredoc + match = getch + match << scan_until(/$/) unless eos? + encoder.text_token match, :delimiter unless match.empty? + encoder.end_group state.type + state = state.next_state + next + end + + case match = getch + + when state.delim + if state.paren_depth + state.paren_depth -= 1 + if state.paren_depth > 0 + encoder.text_token match, :content + next + end + end + encoder.text_token match, :delimiter + if state.type == :regexp && !eos? + match = scan(/#{patterns::REGEXP_MODIFIERS}/o) + encoder.text_token match, :modifier unless match.empty? + end + encoder.end_group state.type + value_expected = false + state = state.next_state + + when '\\' + if state.interpreted + if esc = scan(/#{patterns::ESCAPE}/o) + encoder.text_token match + esc, :char + else + encoder.text_token match, :error + end + else + case esc = getch + when nil + encoder.text_token match, :content + when state.delim, '\\' + encoder.text_token match + esc, :char + else + encoder.text_token match + esc, :content + end + end + + when '#' + case peek(1) + when '{' + inline_block_stack ||= [] + inline_block_stack << [state, inline_block_curly_depth, heredocs] + value_expected = true + state = :initial + inline_block_curly_depth = 1 + encoder.begin_group :inline + encoder.text_token match + getch, :inline_delimiter + when '$', '@' + encoder.text_token match, :escape + last_state = state + state = :initial + else + #:nocov: + raise_inspect 'else-case # reached; #%p not handled' % [peek(1)], encoder + #:nocov: + end + + when state.opening_paren + state.paren_depth += 1 + encoder.text_token match, :content + + else + #:nocov + raise_inspect 'else-case " reached; %p not handled, state = %p' % [match, state], encoder + #:nocov: + + end + + end + + end + + # cleaning up + if state.is_a? StringState + encoder.end_group state.type + end + + if options[:keep_state] + if state.is_a?(StringState) && state.heredoc + (heredocs ||= []).unshift state + state = :initial + elsif heredocs && heredocs.empty? + heredocs = nil + end + @state = state, heredocs + end + + if inline_block_stack + until inline_block_stack.empty? + state, = *inline_block_stack.pop + encoder.end_group :inline + encoder.end_group state.type + end + end + + encoder + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/ruby/patterns.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/ruby/patterns.rb new file mode 100644 index 0000000..cd942d0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/ruby/patterns.rb @@ -0,0 +1,178 @@ +# encoding: utf-8 +module CodeRay +module Scanners + + module Ruby::Patterns # :nodoc: all + + KEYWORDS = %w[ + and def end in or unless begin + defined? ensure module redo super until + BEGIN break do next rescue then + when END case else for retry + while alias class elsif if not return + undef yield + ] + + # See http://murfy.de/ruby-constants. + PREDEFINED_CONSTANTS = %w[ + nil true false self + DATA ARGV ARGF ENV + FALSE TRUE NIL + STDERR STDIN STDOUT + TOPLEVEL_BINDING + RUBY_COPYRIGHT RUBY_DESCRIPTION RUBY_ENGINE RUBY_PATCHLEVEL + RUBY_PLATFORM RUBY_RELEASE_DATE RUBY_REVISION RUBY_VERSION + __FILE__ __LINE__ __ENCODING__ + ] + + IDENT_KIND = WordList.new(:ident). + add(KEYWORDS, :keyword). + add(PREDEFINED_CONSTANTS, :predefined_constant) + + KEYWORD_NEW_STATE = WordList.new(:initial). + add(%w[ def ], :def_expected). + add(%w[ undef ], :undef_expected). + add(%w[ alias ], :alias_expected). + add(%w[ class module ], :module_expected) + + IDENT = 'ä'[/[[:alpha:]]/] == 'ä' ? Regexp.new('[[:alpha:]_[^\0-\177]][[:alnum:]_[^\0-\177]]*') : /[^\W\d]\w*/ + + METHOD_NAME = / #{IDENT} [?!]? /ox + METHOD_NAME_OPERATOR = / + \*\*? # multiplication and power + | [-+~]@? # plus, minus, tilde with and without at sign + | [\/%&|^`] # division, modulo or format strings, and, or, xor, system + | \[\]=? # array getter and setter + | << | >> # append or shift left, shift right + | <=?>? | >=? # comparison, rocket operator + | ===? | =~ # simple equality, case equality, match + | ![~=@]? # negation with and without at sign, not-equal and not-match + /ox + METHOD_SUFFIX = / (?: [?!] | = (?![~>]|=(?!>)) ) /x + METHOD_NAME_EX = / #{IDENT} #{METHOD_SUFFIX}? | #{METHOD_NAME_OPERATOR} /ox + METHOD_AFTER_DOT = / #{IDENT} [?!]? | #{METHOD_NAME_OPERATOR} /ox + INSTANCE_VARIABLE = / @ #{IDENT} /ox + CLASS_VARIABLE = / @@ #{IDENT} /ox + OBJECT_VARIABLE = / @@? #{IDENT} /ox + GLOBAL_VARIABLE = / \$ (?: #{IDENT} | [1-9]\d* | 0\w* | [~&+`'=\/,;_.<>!@$?*":\\] | -[a-zA-Z_0-9] ) /ox + PREFIX_VARIABLE = / #{GLOBAL_VARIABLE} | #{OBJECT_VARIABLE} /ox + VARIABLE = / @?@? #{IDENT} | #{GLOBAL_VARIABLE} /ox + + QUOTE_TO_TYPE = { + '`' => :shell, + '/' => :regexp, + } + QUOTE_TO_TYPE.default = :string + + REGEXP_MODIFIERS = /[mousenix]*/ + + DECIMAL = /\d+(?:_\d+)*/ + OCTAL = /0_?[0-7]+(?:_[0-7]+)*/ + HEXADECIMAL = /0x[0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*/ + BINARY = /0b[01]+(?:_[01]+)*/ + + EXPONENT = / [eE] [+-]? #{DECIMAL} /ox + FLOAT_SUFFIX = / #{EXPONENT} | \. #{DECIMAL} #{EXPONENT}? /ox + FLOAT_OR_INT = / #{DECIMAL} (?: #{FLOAT_SUFFIX} () )? /ox + NUMERIC = / (?: (?=0) (?: #{OCTAL} | #{HEXADECIMAL} | #{BINARY} ) | #{FLOAT_OR_INT} ) /ox + + SYMBOL = / + : + (?: + #{METHOD_NAME_EX} + | #{PREFIX_VARIABLE} + | ['"] + ) + /ox + METHOD_NAME_OR_SYMBOL = / #{METHOD_NAME_EX} | #{SYMBOL} /ox + + SIMPLE_ESCAPE = / + [abefnrstv] + | [0-7]{1,3} + | x[0-9A-Fa-f]{1,2} + | . + /mx + + CONTROL_META_ESCAPE = / + (?: M-|C-|c ) + (?: \\ (?: M-|C-|c ) )* + (?: [^\\] | \\ #{SIMPLE_ESCAPE} )? + /mox + + ESCAPE = / + #{CONTROL_META_ESCAPE} | #{SIMPLE_ESCAPE} + /mox + + CHARACTER = / + \? + (?: + [^\s\\] + | \\ #{ESCAPE} + ) + /mox + + # NOTE: This is not completely correct, but + # nobody needs heredoc delimiters ending with \n. + HEREDOC_OPEN = / + << ([-~])? # $1 = float + (?: + ( [A-Za-z_0-9]+ ) # $2 = delim + | + ( ["'`\/] ) # $3 = quote, type + ( [^\n]*? ) \3 # $4 = delim + ) + /mx + + RUBYDOC = / + =begin (?!\S) + .*? + (?: \Z | ^=end (?!\S) [^\n]* ) + /mx + + DATA = / + __END__$ + .*? + (?: \Z | (?=^\#CODE) ) + /mx + + RUBYDOC_OR_DATA = / #{RUBYDOC} | #{DATA} /xo + + # Checks for a valid value to follow. This enables + # value_expected in method calls without parentheses. + VALUE_FOLLOWS = / + (?>[ \t\f\v]+) + (?: + [%\/][^\s=] + | <<-?\S + | [-+] \d + | #{CHARACTER} + ) + /ox + KEYWORDS_EXPECTING_VALUE = WordList.new.add(%w[ + and end in or unless begin + defined? ensure redo super until + break do next rescue then + when case else for retry + while elsif if not return + yield + ]) + + FANCY_STRING_START = / % ( [iIqQrswWx] | (?![a-zA-Z0-9]) ) ([^a-zA-Z0-9]) /x + FANCY_STRING_KIND = Hash.new(:string).merge({ + 'i' => :symbol, + 'I' => :symbol, + 'r' => :regexp, + 's' => :symbol, + 'x' => :shell, + }) + FANCY_STRING_INTERPRETED = Hash.new(true).merge({ + 'i' => false, + 'q' => false, + 's' => false, + 'w' => false, + }) + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/ruby/string_state.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/ruby/string_state.rb new file mode 100644 index 0000000..95f1e83 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/ruby/string_state.rb @@ -0,0 +1,79 @@ +# encoding: utf-8 +module CodeRay +module Scanners + + class Ruby + + class StringState < Struct.new :type, :interpreted, :delim, :heredoc, + :opening_paren, :paren_depth, :pattern, :next_state # :nodoc: all + + CLOSING_PAREN = Hash[ *%w[ + ( ) + [ ] + < > + { } + ] ].each { |k,v| k.freeze; v.freeze } # debug, if I try to change it with << + + STRING_PATTERN = Hash.new do |h, k| + delim, interpreted = *k + delim_pattern = Regexp.escape(delim) + if closing_paren = CLOSING_PAREN[delim] + delim_pattern << Regexp.escape(closing_paren) + end + delim_pattern << '\\\\' unless delim == '\\' + + # special_escapes = + # case interpreted + # when :regexp_symbols + # '| [|?*+(){}\[\].^$]' + # end + + if interpreted && delim != '#' + / (?= [#{delim_pattern}] | \# [{$@] ) /mx + else + / (?= [#{delim_pattern}] ) /mx + end.tap do |pattern| + h[k] = pattern if (delim.respond_to?(:ord) ? delim.ord : delim[0]) < 256 + end + end + + def self.simple_key_pattern delim + if delim == "'" + / (?> (?: [^\\']+ | \\. )* ) ' : /mx + else + / (?> (?: [^\\"\#]+ | \\. | \#\$[\\"] | \#\{[^\{\}]+\} | \#(?!\{) )* ) " : /mx + end + end + + def initialize kind, interpreted, delim, heredoc = false + if heredoc + pattern = heredoc_pattern delim, interpreted, heredoc == :indented + delim = nil + else + pattern = STRING_PATTERN[ [delim, interpreted] ] + if closing_paren = CLOSING_PAREN[delim] + opening_paren = delim + delim = closing_paren + paren_depth = 1 + end + end + super kind, interpreted, delim, heredoc, opening_paren, paren_depth, pattern, :initial + end + + def heredoc_pattern delim, interpreted, indented + # delim = delim.dup # workaround for old Ruby + delim_pattern = Regexp.escape(delim) + delim_pattern = / (?:\A|\n) #{ '(?>[ \t]*)' if indented } #{ Regexp.new delim_pattern } $ /x + if interpreted + / (?= #{delim_pattern}() | \\ | \# [{$@] ) /mx # $1 set == end of heredoc + else + / (?= #{delim_pattern}() | \\ ) /mx + end + end + + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/sass.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/sass.rb new file mode 100644 index 0000000..e3296b9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/sass.rb @@ -0,0 +1,232 @@ +module CodeRay +module Scanners + + # A scanner for Sass. + class Sass < CSS + + register_for :sass + file_extension 'sass' + + protected + + def setup + @state = :initial + end + + def scan_tokens encoder, options + states = Array(options[:state] || @state).dup + + encoder.begin_group :string if states.last == :sqstring || states.last == :dqstring + + until eos? + + if bol? && (match = scan(/(?>( +)?(\/[\*\/])(.+)?)(?=\n)/)) + encoder.text_token self[1], :space if self[1] + encoder.begin_group :comment + encoder.text_token self[2], :delimiter + encoder.text_token self[3], :content if self[3] + if match = scan(/(?:\n+#{self[1]} .*)+/) + encoder.text_token match, :content + end + encoder.end_group :comment + elsif match = scan(/\n|[^\n\S]+\n?/) + encoder.text_token match, :space + if match.index(/\n/) + value_expected = false + states.pop if states.last == :include + end + + elsif states.last == :sass_inline && (match = scan(/\}/)) + encoder.text_token match, :inline_delimiter + encoder.end_group :inline + states.pop + + elsif case states.last + when :initial, :media, :sass_inline + if match = scan(/(?>#{RE::Ident})(?!\()/ox) + encoder.text_token match, value_expected ? :value : (check(/.*:(?![a-z])/) ? :key : :tag) + next + elsif !value_expected && (match = scan(/\*/)) + encoder.text_token match, :tag + next + elsif match = scan(RE::Class) + encoder.text_token match, :class + next + elsif match = scan(RE::Id) + encoder.text_token match, :id + next + elsif match = scan(RE::PseudoClass) + encoder.text_token match, :pseudo_class + next + elsif match = scan(RE::AttributeSelector) + # TODO: Improve highlighting inside of attribute selectors. + encoder.text_token match[0,1], :operator + encoder.text_token match[1..-2], :attribute_name if match.size > 2 + encoder.text_token match[-1,1], :operator if match[-1] == ?] + next + elsif match = scan(/(\=|@mixin +)#{RE::Ident}/o) + encoder.text_token match, :function + next + elsif match = scan(/@import\b/) + encoder.text_token match, :directive + states << :include + next + elsif match = scan(/@media\b/) + encoder.text_token match, :directive + # states.push :media_before_name + next + end + + when :block + if match = scan(/(?>#{RE::Ident})(?!\()/ox) + if value_expected + encoder.text_token match, :value + else + encoder.text_token match, :key + end + next + end + + when :sqstring, :dqstring + if match = scan(states.last == :sqstring ? /(?:[^\n\'\#]+|\\\n|#{RE::Escape}|#(?!\{))+/o : /(?:[^\n\"\#]+|\\\n|#{RE::Escape}|#(?!\{))+/o) + encoder.text_token match, :content + elsif match = scan(/['"]/) + encoder.text_token match, :delimiter + encoder.end_group :string + states.pop + elsif match = scan(/#\{/) + encoder.begin_group :inline + encoder.text_token match, :inline_delimiter + states.push :sass_inline + elsif match = scan(/ \\ | $ /x) + encoder.end_group states.last + encoder.text_token match, :error unless match.empty? + states.pop + else + raise_inspect "else case #{states.last} reached; %p not handled." % peek(1), encoder + end + + when :include + if match = scan(/[^\s'",]+/) + encoder.text_token match, :include + next + end + + else + #:nocov: + raise_inspect 'Unknown state: %p' % [states.last], encoder + #:nocov: + + end + + elsif match = scan(/\$#{RE::Ident}/o) + encoder.text_token match, :variable + next + + elsif match = scan(/&/) + encoder.text_token match, :local_variable + + elsif match = scan(/\+#{RE::Ident}/o) + encoder.text_token match, :include + value_expected = true + + elsif match = scan(/\/\*(?:.*?\*\/|.*)|\/\/.*/) + encoder.text_token match, :comment + + elsif match = scan(/#\{/) + encoder.begin_group :inline + encoder.text_token match, :inline_delimiter + states.push :sass_inline + + elsif match = scan(/\{/) + value_expected = false + encoder.text_token match, :operator + states.push :block + + elsif match = scan(/\}/) + value_expected = false + encoder.text_token match, :operator + if states.last == :block || states.last == :media + states.pop + end + + elsif match = scan(/['"]/) + encoder.begin_group :string + encoder.text_token match, :delimiter + if states.include? :sass_inline + # no nesting, just scan the string until delimiter + content = scan_until(/(?=#{match}|\}|\z)/) + encoder.text_token content, :content unless content.empty? + encoder.text_token match, :delimiter if scan(/#{match}/) + encoder.end_group :string + else + states.push match == "'" ? :sqstring : :dqstring + end + + elsif match = scan(/#{RE::Function}/o) + encoder.begin_group :function + start = match[/^[-\w]+\(/] + encoder.text_token start, :delimiter + if match[-1] == ?) + encoder.text_token match[start.size..-2], :content + encoder.text_token ')', :delimiter + else + encoder.text_token match[start.size..-1], :content if start.size < match.size + end + encoder.end_group :function + + elsif match = scan(/[a-z][-a-z_]*(?=\()/o) + encoder.text_token match, :predefined + + elsif match = scan(/(?: #{RE::Dimension} | #{RE::Percentage} | #{RE::Num} )/ox) + encoder.text_token match, :float + + elsif match = scan(/#{RE::HexColor}/o) + encoder.text_token match, :color + + elsif match = scan(/! *(?:important|optional)/) + encoder.text_token match, :important + + elsif match = scan(/(?:rgb|hsl)a?\([^()\n]*\)?/) + encoder.text_token match, :color + + elsif match = scan(/@else if\b|#{RE::AtKeyword}/o) + encoder.text_token match, :directive + value_expected = true + + elsif match = scan(/ == | != | [-+*\/>~:;,.=()] /x) + if match == ':' + value_expected = true + elsif match == ';' + value_expected = false + end + encoder.text_token match, :operator + + else + encoder.text_token getch, :error + + end + + end + + states.pop if states.last == :include + + if options[:keep_state] + @state = states.dup + end + + while state = states.pop + if state == :sass_inline + encoder.end_group :inline + elsif state == :sqstring || state == :dqstring + encoder.end_group :string + end + end + + encoder + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/scanner.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/scanner.rb new file mode 100644 index 0000000..efa710d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/scanner.rb @@ -0,0 +1,337 @@ +# encoding: utf-8 + +module CodeRay + module Scanners + + # = Scanner + # + # The base class for all Scanners. + # + # It is a subclass of Ruby's great +StringScanner+, which + # makes it easy to access the scanning methods inside. + # + # It is also +Enumerable+, so you can use it like an Array of + # Tokens: + # + # require 'coderay' + # + # c_scanner = CodeRay::Scanners[:c].new "if (*p == '{') nest++;" + # + # for text, kind in c_scanner + # puts text if kind == :operator + # end + # + # # prints: (*==)++; + # + # OK, this is a very simple example :) + # You can also use +map+, +any?+, +find+ and even +sort_by+, + # if you want. + class Scanner < StringScanner + + extend Plugin + plugin_host Scanners + + # Raised if a Scanner fails while scanning + ScanError = Class.new StandardError + + # The default options for all scanner classes. + # + # Define @default_options for subclasses. + DEFAULT_OPTIONS = { } + + KINDS_NOT_LOC = [:comment, :doctype, :docstring] + + attr_accessor :state + + class << self + + # Normalizes the given code into a string with UNIX newlines, in the + # scanner's internal encoding, with invalid and undefined charachters + # replaced by placeholders. Always returns a new object. + def normalize code + # original = code + code = code.to_s unless code.is_a? ::String + return code if code.empty? + + if code.respond_to? :encoding + code = encode_with_encoding code, self.encoding + else + code = to_unix code + end + # code = code.dup if code.eql? original + code + end + + # The typical filename suffix for this scanner's language. + def file_extension extension = lang + @file_extension ||= extension.to_s + end + + # The encoding used internally by this scanner. + def encoding name = 'UTF-8' + @encoding ||= defined?(Encoding.find) && Encoding.find(name) + end + + # The lang of this Scanner class, which is equal to its Plugin ID. + def lang + @plugin_id + end + + protected + + def encode_with_encoding code, target_encoding + if code.encoding == target_encoding + if code.valid_encoding? + return to_unix(code) + else + source_encoding = guess_encoding code + end + else + source_encoding = code.encoding + end + # print "encode_with_encoding from #{source_encoding} to #{target_encoding}" + code.encode target_encoding, source_encoding, :universal_newline => true, :undef => :replace, :invalid => :replace + end + + def to_unix code + code.index(?\r) ? code.gsub(/\r\n?/, "\n") : code + end + + def guess_encoding s + #:nocov: + IO.popen("file -b --mime -", "w+") do |file| + file.write s[0, 1024] + file.close_write + begin + Encoding.find file.gets[/charset=([-\w]+)/, 1] + rescue ArgumentError + Encoding::BINARY + end + end + #:nocov: + end + + end + + # Create a new Scanner. + # + # * +code+ is the input String and is handled by the superclass + # StringScanner. + # * +options+ is a Hash with Symbols as keys. + # It is merged with the default options of the class (you can + # overwrite default options here.) + # + # Else, a Tokens object is used. + def initialize code = '', options = {} + if self.class == Scanner + raise NotImplementedError, "I am only the basic Scanner class. I can't scan anything. :( Use my subclasses." + end + + @options = self.class::DEFAULT_OPTIONS.merge options + + super self.class.normalize(code) + + @tokens = options[:tokens] || Tokens.new + @tokens.scanner = self if @tokens.respond_to? :scanner= + + setup + end + + # Sets back the scanner. Subclasses should redefine the reset_instance + # method instead of this one. + def reset + super + reset_instance + end + + # Set a new string to be scanned. + def string= code + code = self.class.normalize(code) + super code + reset_instance + end + + # the Plugin ID for this scanner + def lang + self.class.lang + end + + # the default file extension for this scanner + def file_extension + self.class.file_extension + end + + # Scan the code and returns all tokens in a Tokens object. + def tokenize source = nil, options = {} + options = @options.merge(options) + + set_tokens_from_options options + set_string_from_source source + + begin + scan_tokens @tokens, options + rescue => e + message = "Error in %s#scan_tokens, initial state was: %p" % [self.class, defined?(state) && state] + raise_inspect e.message, @tokens, message, 30, e.backtrace + end + + @cached_tokens = @tokens + if source.is_a? Array + @tokens.split_into_parts(*source.map { |part| part.size }) + else + @tokens + end + end + + # Cache the result of tokenize. + def tokens + @cached_tokens ||= tokenize + end + + # Traverse the tokens. + def each &block + tokens.each(&block) + end + include Enumerable + + # The current line position of the scanner, starting with 1. + # See also: #column. + # + # Beware, this is implemented inefficiently. It should be used + # for debugging only. + def line pos = self.pos + return 1 if pos <= 0 + binary_string[0...pos].count("\n") + 1 + end + + # The current column position of the scanner, starting with 1. + # See also: #line. + def column pos = self.pos + return 1 if pos <= 0 + pos - (binary_string.rindex(?\n, pos - 1) || -1) + end + + # The string in binary encoding. + # + # To be used with #pos, which is the index of the byte the scanner + # will scan next. + def binary_string + @binary_string ||= + if string.respond_to?(:bytesize) && string.bytesize != string.size + #:nocov: + string.dup.force_encoding('binary') + #:nocov: + else + string + end + end + + protected + + # Can be implemented by subclasses to do some initialization + # that has to be done once per instance. + # + # Use reset for initialization that has to be done once per + # scan. + def setup # :doc: + end + + def set_string_from_source source + case source + when Array + self.string = self.class.normalize(source.join) + when nil + reset + else + self.string = self.class.normalize(source) + end + end + + def set_tokens_from_options options + @tokens = options[:tokens] || @tokens || Tokens.new + @tokens.scanner = self if @tokens.respond_to? :scanner= + end + + # This is the central method, and commonly the only one a + # subclass implements. + # + # Subclasses must implement this method; it must return +tokens+ + # and must only use Tokens#<< for storing scanned tokens! + def scan_tokens tokens, options # :doc: + raise NotImplementedError, "#{self.class}#scan_tokens not implemented." + end + + # Resets the scanner. + def reset_instance + @tokens.clear if @tokens.respond_to?(:clear) && !@options[:keep_tokens] + @cached_tokens = nil + @binary_string = nil if defined? @binary_string + end + + SCAN_ERROR_MESSAGE = <<-MESSAGE + + +***ERROR in %s: %s (after %s tokens) + +tokens: +%s + +%s + +surrounding code: +%p ~~ %p + + +***ERROR*** + + MESSAGE + + def raise_inspect_arguments message, tokens, state, ambit + return File.basename(caller[0]), + message, + tokens_size(tokens), + tokens_last(tokens, 10).map(&:inspect).join("\n"), + scanner_state_info(state), + binary_string[pos - ambit, ambit], + binary_string[pos, ambit] + end + + SCANNER_STATE_INFO = <<-INFO +current line: %d column: %d pos: %d +matched: %p state: %p +bol?: %p, eos?: %p + INFO + + def scanner_state_info state + SCANNER_STATE_INFO % [ + line, column, pos, + matched, state || 'No state given!', + bol?, eos?, + ] + end + + # Scanner error with additional status information + def raise_inspect message, tokens, state = self.state, ambit = 30, backtrace = caller + raise ScanError, SCAN_ERROR_MESSAGE % raise_inspect_arguments(message, tokens, state, ambit), backtrace + end + + def tokens_size tokens + tokens.size if tokens.respond_to?(:size) + end + + def tokens_last tokens, n + tokens.respond_to?(:last) ? tokens.last(n) : [] + end + + # Shorthand for scan_until(/\z/). + # This method also avoids a JRuby 1.9 mode bug. + def scan_rest + rest = self.rest + terminate + rest + end + + end + + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/sql.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/sql.rb new file mode 100644 index 0000000..c8725a8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/sql.rb @@ -0,0 +1,169 @@ +module CodeRay +module Scanners + + # by Josh Goebel + class SQL < Scanner + + register_for :sql + + KEYWORDS = %w( + all and any as before begin between by case check collate + each else end exists + for foreign from full group having if in inner is join + like not of on or order outer over references + then to union using values when where + left right distinct + ) + + OBJECTS = %w( + database databases table tables column columns fields index constraint + constraints transaction function procedure row key view trigger + ) + + COMMANDS = %w( + add alter comment create delete drop grant insert into select update set + show prompt begin commit rollback replace truncate + ) + + PREDEFINED_TYPES = %w( + char varchar varchar2 enum binary text tinytext mediumtext + longtext blob tinyblob mediumblob longblob timestamp + date time datetime year double decimal float int + integer tinyint mediumint bigint smallint unsigned bit numeric + bool boolean hex bin oct + ) + + PREDEFINED_FUNCTIONS = %w( sum cast substring abs pi count min max avg now ) + + DIRECTIVES = %w( + auto_increment unique default charset initially deferred + deferrable cascade immediate read write asc desc after + primary foreign return engine + ) + + PREDEFINED_CONSTANTS = %w( null true false ) + + IDENT_KIND = WordList::CaseIgnoring.new(:ident). + add(KEYWORDS, :keyword). + add(OBJECTS, :type). + add(COMMANDS, :class). + add(PREDEFINED_TYPES, :predefined_type). + add(PREDEFINED_CONSTANTS, :predefined_constant). + add(PREDEFINED_FUNCTIONS, :predefined). + add(DIRECTIVES, :directive) + + ESCAPE = / [rbfntv\n\\\/'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} | . /mx + UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x + + STRING_PREFIXES = /[xnb]|_\w+/i + + STRING_CONTENT_PATTERN = { + '"' => / (?: [^\\"] | "" )+ /x, + "'" => / (?: [^\\'] | '' )+ /x, + '`' => / (?: [^\\`] | `` )+ /x, + } + + def scan_tokens encoder, options + + state = :initial + string_type = nil + string_content = '' + name_expected = false + + until eos? + + if state == :initial + + if match = scan(/ \s+ | \\\n /x) + encoder.text_token match, :space + + elsif match = scan(/(?:--\s?|#).*/) + encoder.text_token match, :comment + + elsif match = scan(%r( /\* (!)? (?: .*? \*/ | .* ) )mx) + encoder.text_token match, self[1] ? :directive : :comment + + elsif match = scan(/ [*\/=<>:;,!&^|()\[\]{}~%] | [-+\.](?!\d) /x) + name_expected = true if match == '.' && check(/[A-Za-z_]/) + encoder.text_token match, :operator + + elsif match = scan(/(#{STRING_PREFIXES})?([`"'])/o) + prefix = self[1] + string_type = self[2] + encoder.begin_group :string + encoder.text_token prefix, :modifier if prefix + match = string_type + state = :string + encoder.text_token match, :delimiter + + elsif match = scan(/ @? [A-Za-z_][A-Za-z_0-9\$]* /x) + encoder.text_token match, name_expected ? :ident : (match[0] == ?@ ? :variable : IDENT_KIND[match]) + name_expected = false + + elsif match = scan(/0[xX][0-9A-Fa-f]+/) + encoder.text_token match, :hex + + elsif match = scan(/0[0-7]+(?![89.eEfF])/) + encoder.text_token match, :octal + + elsif match = scan(/[-+]?(?>\d+)(?![.eEfF])/) + encoder.text_token match, :integer + + elsif match = scan(/[-+]?(?:\d[fF]|\d*\.\d+(?:[eE][+-]?\d+)?|\d+[eE][+-]?\d+)/) + encoder.text_token match, :float + + elsif match = scan(/\\N/) + encoder.text_token match, :predefined_constant + + else + encoder.text_token getch, :error + + end + + elsif state == :string + if match = scan(STRING_CONTENT_PATTERN[string_type]) + encoder.text_token match, :content + elsif match = scan(/["'`]/) + if string_type == match + if peek(1) == string_type # doubling means escape + encoder.text_token match + getch, :content + else + encoder.text_token match, :delimiter + encoder.end_group :string + state = :initial + string_type = nil + end + else + encoder.text_token match, :content + end + elsif match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox) + encoder.text_token match, :char + elsif match = scan(/ \\ . /mox) + encoder.text_token match, :content + elsif match = scan(/ \\ | $ /x) + encoder.text_token match, :error unless match.empty? + encoder.end_group :string + state = :initial + else + raise "else case \" reached; %p not handled." % peek(1), encoder + end + + else + raise 'else-case reached', encoder + + end + + end + + if state == :string + encoder.end_group state + end + + encoder + + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/taskpaper.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/taskpaper.rb new file mode 100644 index 0000000..42670bc --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/taskpaper.rb @@ -0,0 +1,36 @@ +module CodeRay +module Scanners + + class Taskpaper < Scanner + + register_for :taskpaper + file_extension 'taskpaper' + + protected + + def scan_tokens encoder, options + until eos? + if match = scan(/\S.*:.*$/) # project + encoder.text_token(match, :namespace) + elsif match = scan(/-.+@done.*/) # completed task + encoder.text_token(match, :done) + elsif match = scan(/-(?:[^@\n]+|@(?!due))*/) # task + encoder.text_token(match, :plain) + elsif match = scan(/@due.*/) # comment + encoder.text_token(match, :important) + elsif match = scan(/.+/) # comment + encoder.text_token(match, :comment) + elsif match = scan(/\s+/) # space + encoder.text_token(match, :space) + else # other + encoder.text_token getch, :error + end + end + + encoder + end + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/text.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/text.rb new file mode 100644 index 0000000..bde9029 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/text.rb @@ -0,0 +1,26 @@ +module CodeRay + module Scanners + + # Scanner for plain text. + # + # Yields just one token of the kind :plain. + # + # Alias: +plaintext+, +plain+ + class Text < Scanner + + register_for :text + title 'Plain text' + + KINDS_NOT_LOC = [:plain] # :nodoc: + + protected + + def scan_tokens encoder, options + encoder.text_token string, :plain + encoder + end + + end + + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/xml.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/xml.rb new file mode 100644 index 0000000..947f16e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/xml.rb @@ -0,0 +1,17 @@ +module CodeRay +module Scanners + + load :html + + # Scanner for XML. + # + # Currently this is the same scanner as Scanners::HTML. + class XML < HTML + + register_for :xml + file_extension 'xml' + + end + +end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/yaml.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/yaml.rb new file mode 100644 index 0000000..32c8e2c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/scanners/yaml.rb @@ -0,0 +1,140 @@ +module CodeRay +module Scanners + + # Scanner for YAML. + # + # Based on the YAML scanner from Syntax by Jamis Buck. + class YAML < Scanner + + register_for :yaml + file_extension 'yml' + + KINDS_NOT_LOC = :all + + protected + + def scan_tokens encoder, options + + state = :initial + key_indent = string_indent = 0 + + until eos? + + key_indent = nil if bol? + + if match = scan(/ +[\t ]*/) + encoder.text_token match, :space + + elsif match = scan(/\n+/) + encoder.text_token match, :space + state = :initial if match.index(?\n) + + elsif match = scan(/#.*/) + encoder.text_token match, :comment + + elsif bol? and case + when match = scan(/---|\.\.\./) + encoder.begin_group :head + encoder.text_token match, :head + encoder.end_group :head + next + when match = scan(/%.*/) + encoder.text_token match, :doctype + next + end + + elsif state == :value and case + when !check(/(?:"[^"]*")(?=: |:$)/) && match = scan(/"/) + encoder.begin_group :string + encoder.text_token match, :delimiter + encoder.text_token match, :content if (match = scan(/ [^"\\]* (?: \\. [^"\\]* )* /mx)) && !match.empty? + encoder.text_token match, :delimiter if match = scan(/"/) + encoder.end_group :string + next + when match = scan(/[|>][-+]?/) + encoder.begin_group :string + encoder.text_token match, :delimiter + string_indent = key_indent || column(pos - match.size) - 1 + encoder.text_token matched, :content if scan(/(?:\n+ {#{string_indent + 1}}.*)+/) + encoder.end_group :string + next + when match = scan(/(?![!"*&]).+?(?=$|\s+#)/) + encoder.begin_group :string + encoder.text_token match, :content + string_indent = key_indent || column(pos - match.size) - 1 + encoder.text_token matched, :content if scan(/(?:\n+ {#{string_indent + 1}}.*)+/) + encoder.end_group :string + next + end + + elsif case + when match = scan(/[-:](?= |$)/) + state = :value if state == :colon && (match == ':' || match == '-') + state = :value if state == :initial && match == '-' + encoder.text_token match, :operator + next + when match = scan(/[,{}\[\]]/) + encoder.text_token match, :operator + next + when state == :initial && match = scan(/[-\w.()\/ ]*\S(?= *:(?: |$))/) + encoder.text_token match, :key + key_indent = column(pos - match.size) - 1 + state = :colon + next + when match = scan(/(?:"[^"\n]*"|'[^'\n]*')(?= *:(?: |$))/) + encoder.begin_group :key + encoder.text_token match[0,1], :delimiter + encoder.text_token match[1..-2], :content if match.size > 2 + encoder.text_token match[-1,1], :delimiter + encoder.end_group :key + key_indent = column(pos - match.size) - 1 + state = :colon + next + when match = scan(/(![\w\/]+)(:([\w:]+))?/) + encoder.text_token self[1], :type + if self[2] + encoder.text_token ':', :operator + encoder.text_token self[3], :class + end + next + when match = scan(/&\S+/) + encoder.text_token match, :variable + next + when match = scan(/\*\w+/) + encoder.text_token match, :global_variable + next + when match = scan(/< 'debug', # highlight for debugging (white on blue background) + + :annotation => 'annotation', # Groovy, Java + :attribute_name => 'attribute-name', # HTML, CSS + :attribute_value => 'attribute-value', # HTML + :binary => 'binary', # Python, Ruby + :char => 'char', # most scanners, also inside of strings + :class => 'class', # lots of scanners, for different purposes also in CSS + :class_variable => 'class-variable', # Ruby, YAML + :color => 'color', # CSS + :comment => 'comment', # most scanners + :constant => 'constant', # PHP, Ruby + :content => 'content', # inside of strings, most scanners + :decorator => 'decorator', # Python + :definition => 'definition', # CSS + :delimiter => 'delimiter', # inside strings, comments and other types + :directive => 'directive', # lots of scanners + :doctype => 'doctype', # Goorvy, HTML, Ruby, YAML + :docstring => 'docstring', # Python + :done => 'done', # Taskpaper + :entity => 'entity', # HTML + :error => 'error', # invalid token, most scanners + :escape => 'escape', # Ruby (string inline variables like #$foo, #@bar) + :exception => 'exception', # Java, PHP, Python + :filename => 'filename', # Diff + :float => 'float', # most scanners + :function => 'function', # CSS, JavaScript, PHP + :global_variable => 'global-variable', # Ruby, YAML + :hex => 'hex', # hexadecimal number; lots of scanners + :id => 'id', # CSS + :imaginary => 'imaginary', # Python + :important => 'important', # CSS, Taskpaper + :include => 'include', # C, Groovy, Java, Python, Sass + :inline => 'inline', # nested code, eg. inline string evaluation; lots of scanners + :inline_delimiter => 'inline-delimiter', # used instead of :inline > :delimiter FIXME: Why use inline_delimiter? + :instance_variable => 'instance-variable', # Ruby + :integer => 'integer', # most scanners + :key => 'key', # lots of scanners, used together with :value + :keyword => 'keyword', # reserved word that's actually implemented; most scanners + :label => 'label', # C, PHP + :local_variable => 'local-variable', # local and magic variables; some scanners + :map => 'map', # Lua tables + :modifier => 'modifier', # used inside on strings; lots of scanners + :namespace => 'namespace', # Clojure, Java, Taskpaper + :octal => 'octal', # lots of scanners + :predefined => 'predefined', # predefined function: lots of scanners + :predefined_constant => 'predefined-constant',# lots of scanners + :predefined_type => 'predefined-type', # C, Java, PHP + :preprocessor => 'preprocessor', # C, Delphi, HTML + :pseudo_class => 'pseudo-class', # CSS + :regexp => 'regexp', # Groovy, JavaScript, Ruby + :reserved => 'reserved', # most scanners + :shell => 'shell', # Ruby + :string => 'string', # most scanners + :symbol => 'symbol', # Clojure, Ruby, YAML + :tag => 'tag', # CSS, HTML + :type => 'type', # CSS, Java, SQL, YAML + :value => 'value', # used together with :key; CSS, JSON, YAML + :variable => 'variable', # Sass, SQL, YAML + + :change => 'change', # Diff + :delete => 'delete', # Diff + :head => 'head', # Diff, YAML + :insert => 'insert', # Diff + :eyecatcher => 'eyecatcher', # Diff + + :ident => false, # almost all scanners + :operator => false, # almost all scanners + + :space => false, # almost all scanners + :plain => false # almost all scanners + ) + + TokenKinds[:method] = TokenKinds[:function] + TokenKinds[:unknown] = TokenKinds[:plain] +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/tokens.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/tokens.rb new file mode 100644 index 0000000..b5f78e7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/tokens.rb @@ -0,0 +1,164 @@ +module CodeRay + + # The Tokens class represents a list of tokens returned from + # a Scanner. It's actually just an Array with a few helper methods. + # + # A token itself is not a special object, just two elements in an Array: + # * the _token_ _text_ (the original source of the token in a String) or + # a _token_ _action_ (begin_group, end_group, begin_line, end_line) + # * the _token_ _kind_ (a Symbol representing the type of the token) + # + # It looks like this: + # + # ..., '# It looks like this', :comment, ... + # ..., '3.1415926', :float, ... + # ..., '$^', :error, ... + # + # Some scanners also yield sub-tokens, represented by special + # token actions, for example :begin_group and :end_group. + # + # The Ruby scanner, for example, splits "a string" into: + # + # [ + # :begin_group, :string, + # '"', :delimiter, + # 'a string', :content, + # '"', :delimiter, + # :end_group, :string + # ] + # + # Tokens can be used to save the output of a Scanners in a simple + # Ruby object that can be send to an Encoder later: + # + # tokens = CodeRay.scan('price = 2.59', :ruby).tokens + # tokens.encode(:html) + # tokens.html + # CodeRay.encoder(:html).encode_tokens(tokens) + # + # Tokens gives you the power to handle pre-scanned code very easily: + # You can serialize it to a JSON string and store it in a database, pass it + # around to encode it more than once, send it to other algorithms... + class Tokens < Array + # Remove Array#filter that is a new alias for Array#select on Ruby 2.6, + # for method_missing called with filter method. + undef_method :filter if instance_methods.include?(:filter) + + # The Scanner instance that created the tokens. + attr_accessor :scanner + + # Encode the tokens using encoder. + # + # encoder can be + # * a plugin name like :html oder 'statistic' + # * an Encoder object + # + # options are passed to the encoder. + def encode encoder, options = {} + encoder = Encoders[encoder].new options if encoder.respond_to? :to_sym + encoder.encode_tokens self, options + end + + # Turn tokens into a string by concatenating them. + def to_s + encode CodeRay::Encoders::Encoder.new + end + + # Redirects unknown methods to encoder calls. + # + # For example, if you call +tokens.html+, the HTML encoder + # is used to highlight the tokens. + def method_missing meth, options = {} + encode meth, options + rescue PluginHost::PluginNotFound + super + end + + # Split the tokens into parts of the given +sizes+. + # + # The result will be an Array of Tokens objects. The parts have + # the text size specified by the parameter. In addition, each + # part closes all opened tokens. This is useful to insert tokens + # betweem them. + # + # This method is used by @Scanner#tokenize@ when called with an Array + # of source strings. The Diff encoder uses it for inline highlighting. + def split_into_parts *sizes + return Array.new(sizes.size) { Tokens.new } if size == 2 && first == '' + parts = [] + opened = [] + content = nil + part = Tokens.new + part_size = 0 + size = sizes.first + i = 0 + for item in self + case content + when nil + content = item + when String + if size && part_size + content.size > size # token must be cut + if part_size < size # some part of the token goes into this part + content = content.dup # content may no be safe to change + part << content.slice!(0, size - part_size) << item + end + # close all open groups and lines... + closing = opened.reverse.flatten.map do |content_or_kind| + case content_or_kind + when :begin_group + :end_group + when :begin_line + :end_line + else + content_or_kind + end + end + part.concat closing + begin + parts << part + part = Tokens.new + size = sizes[i += 1] + end until size.nil? || size > 0 + # ...and open them again. + part.concat opened.flatten + part_size = 0 + redo unless content.empty? + else + part << content << item + part_size += content.size + end + content = nil + when Symbol + case content + when :begin_group, :begin_line + opened << [content, item] + when :end_group, :end_line + opened.pop + else + raise ArgumentError, 'Unknown token action: %p, kind = %p' % [content, item] + end + part << content << item + content = nil + else + raise ArgumentError, 'Token input junk: %p, kind = %p' % [content, item] + end + end + parts << part + parts << Tokens.new while parts.size < sizes.size + parts + end + + # Return the actual number of tokens. + def count + size / 2 + end + + alias text_token push + def begin_group kind; push :begin_group, kind end + def end_group kind; push :end_group, kind end + def begin_line kind; push :begin_line, kind end + def end_line kind; push :end_line, kind end + alias tokens concat + + end + +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/tokens_proxy.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/tokens_proxy.rb new file mode 100644 index 0000000..31ff39b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/tokens_proxy.rb @@ -0,0 +1,55 @@ +module CodeRay + + # The result of a scan operation is a TokensProxy, but should act like Tokens. + # + # This proxy makes it possible to use the classic CodeRay.scan.encode API + # while still providing the benefits of direct streaming. + class TokensProxy + + attr_accessor :input, :lang, :options, :block + + # Create a new TokensProxy with the arguments of CodeRay.scan. + def initialize input, lang, options = {}, block = nil + @input = input + @lang = lang + @options = options + @block = block + end + + # Call CodeRay.encode if +encoder+ is a Symbol; + # otherwise, convert the receiver to tokens and call encoder.encode_tokens. + def encode encoder, options = {} + if encoder.respond_to? :to_sym + CodeRay.encode(input, lang, encoder, options) + else + encoder.encode_tokens tokens, options + end + end + + # Tries to call encode; + # delegates to tokens otherwise. + def method_missing method, *args, &blk + encode method.to_sym, *args + rescue PluginHost::PluginNotFound + tokens.send(method, *args, &blk) + end + + # The (cached) result of the tokenized input; a Tokens instance. + def tokens + @tokens ||= scanner.tokenize(input) + end + + # A (cached) scanner instance to use for the scan task. + def scanner + @scanner ||= CodeRay.scanner(lang, options, &block) + end + + # Overwrite Struct#each. + def each *args, &blk + tokens.each(*args, &blk) + self + end + + end + +end diff --git a/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/version.rb b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/version.rb new file mode 100644 index 0000000..3c68bd8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/coderay-1.1.3/lib/coderay/version.rb @@ -0,0 +1,3 @@ +module CodeRay + VERSION = '1.1.3' +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/CHANGELOG.md b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/CHANGELOG.md new file mode 100644 index 0000000..bad0044 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/CHANGELOG.md @@ -0,0 +1,542 @@ +## Current + +## Release v1.1.10 + +concurrent-ruby: + +* (#951) Set the Ruby compatibility version at 2.2 +* (#939, #933) The `caller_runs` fallback policy no longer blocks reads from the job queue by worker threads +* (#938, #761, #652) You can now explicitly `prune_pool` a thread pool (Sylvain Joyeux) +* (#937, #757, #670) We switched the Yahoo stock API for demos to Alpha Vantage (Gustavo Caso) +* (#932, #931) We changed how `SafeTaskExecutor` handles local jump errors (Aaron Jensen) +* (#927) You can use keyword arguments in your initialize when using `Async` (Matt Larraz) +* (#926, #639) We removed timeout from `TimerTask` because it wasn't sound, and now it's a no-op with a warning (Jacob Atzen) +* (#919) If you double-lock a re-entrant read-write lock, we promote to locked for writing (zp yuan) +* (#915) `monotonic_time` now accepts an optional unit parameter, as Ruby's `clock_gettime` (Jean Boussier) + +## Release v1.1.9 (5 Jun 2021) + +concurrent-ruby: + +* (#866) Child promise state not set to :pending immediately after #execute when parent has completed +* (#905, #872) Fix RubyNonConcurrentPriorityQueue#delete method +* (2df0337d) Make sure locks are not shared on shared when objects are dup/cloned +* (#900, #906, #796, #847, #911) Fix Concurrent::Set tread-safety issues on CRuby +* (#907) Add new ConcurrentMap backend for TruffleRuby + +## Release v1.1.8 (20 January 2021) + +concurrent-ruby: + +* (#885) Fix race condition in TVar for stale reads +* (#884) RubyThreadLocalVar: Do not iterate over hash which might conflict with new pair addition + +## Release v1.1.7 (6 August 2020) + +concurrent-ruby: + +* (#879) Consider falsy value on `Concurrent::Map#compute_if_absent` for fast non-blocking path +* (#876) Reset Async queue on forking, makes Async fork-safe +* (#856) Avoid running problematic code in RubyThreadLocalVar on MRI that occasionally results in segfault +* (#853) Introduce ThreadPoolExecutor without a Queue + +## Release v1.1.6, edge v0.6.0 (10 Feb 2020) + +concurrent-ruby: + +* (#841) Concurrent.disable_at_exit_handlers! is no longer needed and was deprecated. +* (#841) AbstractExecutorService#auto_terminate= was deprecated and has no effect. + Set :auto_terminate option instead when executor is initialized. + +## Release v1.1.6.pre1, edge v0.6.0.pre1 (26 Jan 2020) + +concurrent-ruby: + +* (#828) Allow to name executors, the name is also used to name their threads +* (#838) Implement #dup and #clone for structs +* (#821) Safer finalizers for thread local variables +* Documentation fixes +* (#814) Use Ruby's Etc.nprocessors if available +* (#812) Fix directory structure not to mess with packaging tools +* (#840) Fix termination of pools on JRuby + +concurrent-ruby-edge: + +* Add WrappingExecutor (#830) + +## Release v1.1.5, edge v0.5.0 (10 Mar 2019) + +concurrent-ruby: + +* fix potential leak of context on JRuby and Java 7 + +concurrent-ruby-edge: + +* Add finalized Concurrent::Cancellation +* Add finalized Concurrent::Throttle +* Add finalized Concurrent::Promises::Channel +* Add new Concurrent::ErlangActor + +## Release v1.1.4 (14 Dec 2018) + +* (#780) Remove java_alias of 'submit' method of Runnable to let executor service work on java 11 +* (#776) Fix NameError on defining a struct with a name which is already taken in an ancestor + +## Release v1.1.3 (7 Nov 2018) + +* (#775) fix partial require of the gem (although not officially supported) + +## Release v1.1.2 (6 Nov 2018) + +* (#773) more defensive 1.9.3 support + +## Release v1.1.1, edge v0.4.1 (1 Nov 2018) + +* (#768) add support for 1.9.3 back + +## Release v1.1.0, edge v0.4.0 (31 OCt 2018) (yanked) + +* (#768) yanked because of issues with removed 1.9.3 support + +## Release v1.1.0.pre2, edge v0.4.0.pre2 (18 Sep 2018) + +concurrent-ruby: + +* fixed documentation and README links +* fix Set for TruffleRuby and Rubinius +* use properly supported TruffleRuby APIs + +concurrent-ruby-edge: + +* add Promises.zip_futures_over_on + +## Release v1.1.0.pre1, edge v0.4.0.pre1 (15 Aug 2018) + +concurrent-ruby: + +* requires at least Ruby 2.0 +* [Promises](http://ruby-concurrency.github.io/concurrent-ruby/1.1.0/Concurrent/Promises.html) + are moved from `concurrent-ruby-edge` to `concurrent-ruby` +* Add support for TruffleRuby + * (#734) Fix Array/Hash/Set construction broken on TruffleRuby + * AtomicReference fixed +* CI stabilization +* remove sharp dependency edge -> core +* remove warnings +* documentation updates +* Exchanger is no longer documented as edge since it was already available in + `concurrent-ruby` +* (#644) Fix Map#each and #each_pair not returning enumerator outside of MRI +* (#659) Edge promises fail during error handling +* (#741) Raise on recursive Delay#value call +* (#727) #717 fix global IO executor on JRuby +* (#740) Drop support for CRuby 1.9, JRuby 1.7, Rubinius. +* (#737) Move AtomicMarkableReference out of Edge +* (#708) Prefer platform specific memory barriers +* (#735) Fix wrong expected exception in channel spec assertion +* (#729) Allow executor option in `Promise#then` +* (#725) fix timeout check to use timeout_interval +* (#719) update engine detection +* (#660) Add specs for Promise#zip/Promise.zip ordering +* (#654) Promise.zip execution changes +* (#666) Add thread safe set implementation +* (#651) #699 #to_s, #inspect should not output negative object IDs. +* (#685) Avoid RSpec warnings about raise_error +* (#680) Avoid RSpec monkey patching, persist spec results locally, use RSpec + v3.7.0 +* (#665) Initialize the monitor for new subarrays on Rubinius +* (#661) Fix error handling in edge promises + +concurrent-ruby-edge: + +* (#659) Edge promises fail during error handling +* Edge files clearly separated in `lib-edge` +* added ReInclude + +## Release v1.0.5, edge v0.3.1 (26 Feb 2017) + +concurrent-ruby: + +* Documentation for Event and Semaphore +* Use Unsafe#fullFence and #loadFence directly since the shortcuts were removed in JRuby +* Do not depend on org.jruby.util.unsafe.UnsafeHolder + +concurrent-ruby-edge: + +* (#620) Actors on Pool raise an error +* (#624) Delayed promises did not interact correctly with flatting + * Fix arguments yielded by callback methods +* Overridable default executor in promises factory methods +* Asking actor to terminate will always resolve to `true` + +## Release v1.0.4, edge v0.3.0 (27 Dec 2016) + +concurrent-ruby: + +* Nothing + +concurrent-ruby-edge: + +* New promises' API renamed, lots of improvements, edge bumped to 0.3.0 + * **Incompatible** with previous 0.2.3 version + * see https://github.com/ruby-concurrency/concurrent-ruby/pull/522 + +## Release v1.0.3 (17 Dec 2016) + +* Trigger execution of flattened delayed futures +* Avoid forking for processor_count if possible +* Semaphore Mutex and JRuby parity +* Adds Map#each as alias to Map#each_pair +* Fix uninitialized instance variables +* Make Fixnum, Bignum merger ready +* Allows Promise#then to receive an executor +* TimerSet now survives a fork +* Reject promise on any exception +* Allow ThreadLocalVar to be initialized with a block +* Support Alpha with `Concurrent::processor_count` +* Fixes format-security error when compiling ruby_193_compatible.h +* Concurrent::Atom#swap fixed: reraise the exceptions from block + +## Release v1.0.2 (2 May 2016) + +* Fix bug with `Concurrent::Map` MRI backend `#inspect` method +* Fix bug with `Concurrent::Map` MRI backend using `Hash#value?` +* Improved documentation and examples +* Minor updates to Edge + +## Release v1.0.1 (27 February 2016) + +* Fix "uninitialized constant Concurrent::ReentrantReadWriteLock" error. +* Better handling of `autoload` vs. `require`. +* Improved API for Edge `Future` zipping. +* Fix reference leak in Edge `Future` constructor . +* Fix bug which prevented thread pools from surviving a `fork`. +* Fix bug in which `TimerTask` did not correctly specify all its dependencies. +* Improved support for JRuby+Truffle +* Improved error messages. +* Improved documentation. +* Updated README and CONTRIBUTING. + +## Release v1.0.0 (13 November 2015) + +* Rename `attr_volatile_with_cas` to `attr_atomic` +* Add `clear_each` to `LockFreeStack` +* Update `AtomicReference` documentation +* Further updates and improvements to the synchronization layer. +* Performance and memory usage performance with `Actor` logging. +* Fixed `ThreadPoolExecutor` task count methods. +* Improved `Async` performance for both short and long-lived objects. +* Fixed bug in `LockFreeLinkedSet`. +* Fixed bug in which `Agent#await` triggered a validation failure. +* Further `Channel` updates. +* Adopted a project Code of Conduct +* Cleared interpreter warnings +* Fixed bug in `ThreadPoolExecutor` task count methods +* Fixed bug in 'LockFreeLinkedSet' +* Improved Java extension loading +* Handle Exception children in Edge::Future +* Continued improvements to channel +* Removed interpreter warnings. +* Shared constants now in `lib/concurrent/constants.rb` +* Refactored many tests. +* Improved synchronization layer/memory model documentation. +* Bug fix in Edge `Future#flat` +* Brand new `Channel` implementation in Edge gem. +* Simplification of `RubySingleThreadExecutor` +* `Async` improvements + - Each object uses its own `SingleThreadExecutor` instead of the global thread pool. + - No longers supports executor injection + - Much better documentation +* `Atom` updates + - No longer `Dereferenceable` + - Now `Observable` + - Added a `#reset` method +* Brand new `Agent` API and implementation. Now functionally equivalent to Clojure. +* Continued improvements to the synchronization layer +* Merged in the `thread_safe` gem + - `Concurrent::Array` + - `Concurrent::Hash` + - `Concurrent::Map` (formerly ThreadSafe::Cache) + - `Concurrent::Tuple` +* Minor improvements to Concurrent::Map +* Complete rewrite of `Exchanger` +* Removed all deprecated code (classes, methods, constants, etc.) +* Updated Agent, MutexAtomic, and BufferedChannel to inherit from Synchronization::Object. +* Many improved tests +* Some internal reorganization + +## Release v0.9.1 (09 August 2015) + +* Fixed a Rubiniux bug in synchronization object +* Fixed all interpreter warnings (except circular references) +* Fixed require statements when requiring `Atom` alone +* Significantly improved `ThreadLocalVar` on non-JRuby platforms +* Fixed error handling in Edge `Concurrent.zip` +* `AtomicFixnum` methods `#increment` and `#decrement` now support optional delta +* New `AtomicFixnum#update` method +* Minor optimizations in `ReadWriteLock` +* New `ReentrantReadWriteLock` class +* `ThreadLocalVar#bind` method is now public +* Refactored many tests + +## Release v0.9.0 (10 July 2015) + +* Updated `AtomicReference` + - `AtomicReference#try_update` now simply returns instead of raising exception + - `AtomicReference#try_update!` was added to raise exceptions if an update + fails. Note: this is the same behavior as the old `try_update` +* Pure Java implementations of + - `AtomicBoolean` + - `AtomicFixnum` + - `Semaphore` +* Fixed bug when pruning Ruby thread pools +* Fixed bug in time calculations within `ScheduledTask` +* Default `count` in `CountDownLatch` to 1 +* Use monotonic clock for all timers via `Concurrent.monotonic_time` + - Use `Process.clock_gettime(Process::CLOCK_MONOTONIC)` when available + - Fallback to `java.lang.System.nanoTime()` on unsupported JRuby versions + - Pure Ruby implementation for everything else + - Effects `Concurrent.timer`, `Concurrent.timeout`, `TimerSet`, `TimerTask`, and `ScheduledTask` +* Deprecated all clock-time based timer scheduling + - Only support scheduling by delay + - Effects `Concurrent.timer`, `TimerSet`, and `ScheduledTask` +* Added new `ReadWriteLock` class +* Consistent `at_exit` behavior for Java and Ruby thread pools. +* Added `at_exit` handler to Ruby thread pools (already in Java thread pools) + - Ruby handler stores the object id and retrieves from `ObjectSpace` + - JRuby disables `ObjectSpace` by default so that handler stores the object reference +* Added a `:stop_on_exit` option to thread pools to enable/disable `at_exit` handler +* Updated thread pool docs to better explain shutting down thread pools +* Simpler `:executor` option syntax for all abstractions which support this option +* Added `Executor#auto_terminate?` predicate method (for thread pools) +* Added `at_exit` handler to `TimerSet` +* Simplified auto-termination of the global executors + - Can now disable auto-termination of global executors + - Added shutdown/kill/wait_for_termination variants for global executors +* Can now disable auto-termination for *all* executors (the nuclear option) +* Simplified auto-termination of the global executors +* Deprecated terms "task pool" and "operation pool" + - New terms are "io executor" and "fast executor" + - New functions added with new names + - Deprecation warnings added to functions referencing old names +* Moved all thread pool related functions from `Concurrent::Configuration` to `Concurrent` + - Old functions still exist with deprecation warnings + - New functions have updated names as appropriate +* All high-level abstractions default to the "io executor" +* Fixed bug in `Actor` causing it to prematurely warm global thread pools on gem load + - This also fixed a `RejectedExecutionError` bug when running with minitest/autorun via JRuby +* Moved global logger up to the `Concurrent` namespace and refactored the code +* Optimized the performance of `Delay` + - Fixed a bug in which no executor option on construction caused block execution on a global thread pool +* Numerous improvements and bug fixes to `TimerSet` +* Fixed deadlock of `Future` when the handler raises Exception +* Added shared specs for more classes +* New concurrency abstractions including: + - `Atom` + - `Maybe` + - `ImmutableStruct` + - `MutableStruct` + - `SettableStruct` +* Created an Edge gem for unstable abstractions including + - `Actor` + - `Agent` + - `Channel` + - `Exchanger` + - `LazyRegister` + - **new Future Framework** - unified + implementation of Futures and Promises which combines Features of previous `Future`, + `Promise`, `IVar`, `Event`, `Probe`, `dataflow`, `Delay`, `TimerTask` into single framework. It uses extensively + new synchronization layer to make all the paths **lock-free** with exception of blocking threads on `#wait`. + It offers better performance and does not block threads when not required. +* Actor framework changes: + - fixed reset loop in Pool + - Pool can use any actor as a worker, abstract worker class is no longer needed. + - Actor events not have format `[:event_name, *payload]` instead of just the Symbol. + - Actor now uses new Future/Promise Framework instead of `IVar` for better interoperability + - Behaviour definition array was simplified to `[BehaviourClass1, [BehaviourClass2, *initialization_args]]` + - Linking behavior responds to :linked message by returning array of linked actors + - Supervised behavior is removed in favour of just Linking + - RestartingContext is supervised by default now, `supervise: true` is not required any more + - Events can be private and public, so far only difference is that Linking will + pass to linked actors only public messages. Adding private :restarting and + :resetting events which are send before the actor restarts or resets allowing + to add callbacks to cleanup current child actors. + - Print also object_id in Reference to_s + - Add AbstractContext#default_executor to be able to override executor class wide + - Add basic IO example + - Documentation somewhat improved + - All messages should have same priority. It's now possible to send `actor << job1 << job2 << :terminate!` and + be sure that both jobs are processed first. +* Refactored `Channel` to use newer synchronization objects +* Added `#reset` and `#cancel` methods to `TimerSet` +* Added `#cancel` method to `Future` and `ScheduledTask` +* Refactored `TimerSet` to use `ScheduledTask` +* Updated `Async` with a factory that initializes the object +* Deprecated `Concurrent.timer` and `Concurrent.timeout` +* Reduced max threads on pure-Ruby thread pools (abends around 14751 threads) +* Moved many private/internal classes/modules into "namespace" modules +* Removed brute-force killing of threads in tests +* Fixed a thread pool bug when the operating system cannot allocate more threads + +## Release v0.8.0 (25 January 2015) + +* C extension for MRI have been extracted into the `concurrent-ruby-ext` companion gem. + Please see the README for more detail. +* Better variable isolation in `Promise` and `Future` via an `:args` option +* Continued to update intermittently failing tests + +## Release v0.7.2 (24 January 2015) + +* New `Semaphore` class based on [java.util.concurrent.Semaphore](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html) +* New `Promise.all?` and `Promise.any?` class methods +* Renamed `:overflow_policy` on thread pools to `:fallback_policy` +* Thread pools still accept the `:overflow_policy` option but display a warning +* Thread pools now implement `fallback_policy` behavior when not running (rather than universally rejecting tasks) +* Fixed minor `set_deref_options` constructor bug in `Promise` class +* Fixed minor `require` bug in `ThreadLocalVar` class +* Fixed race condition bug in `TimerSet` class +* Fixed race condition bug in `TimerSet` class +* Fixed signal bug in `TimerSet#post` method +* Numerous non-functional updates to clear warning when running in debug mode +* Fixed more intermittently failing tests +* Tests now run on new Travis build environment +* Multiple documentation updates + +## Release v0.7.1 (4 December 2014) + +Please see the [roadmap](https://github.com/ruby-concurrency/concurrent-ruby/issues/142) for more information on the next planned release. + +* Added `flat_map` method to `Promise` +* Added `zip` method to `Promise` +* Fixed bug with logging in `Actor` +* Improvements to `Promise` tests +* Removed actor-experimental warning +* Added an `IndirectImmediateExecutor` class +* Allow disabling auto termination of global executors +* Fix thread leaking in `ThreadLocalVar` (uses `Ref` gem on non-JRuby systems) +* Fix thread leaking when pruning pure-Ruby thread pools +* Prevent `Actor` from using an `ImmediateExecutor` (causes deadlock) +* Added missing synchronizations to `TimerSet` +* Fixed bug with return value of `Concurrent::Actor::Utils::Pool#ask` +* Fixed timing bug in `TimerTask` +* Fixed bug when creating a `JavaThreadPoolExecutor` with minimum pool size of zero +* Removed confusing warning when not using native extenstions +* Improved documentation + +## Release v0.7.0 (13 August 2014) + +* Merge the [atomic](https://github.com/ruby-concurrency/atomic) gem + - Pure Ruby `MutexAtomic` atomic reference class + - Platform native atomic reference classes `CAtomic`, `JavaAtomic`, and `RbxAtomic` + - Automated [build process](https://github.com/ruby-concurrency/rake-compiler-dev-box) + - Fat binary releases for [multiple platforms](https://rubygems.org/gems/concurrent-ruby/versions) including Windows (32/64), Linux (32/64), OS X (64-bit), Solaris (64-bit), and JRuby +* C native `CAtomicBoolean` +* C native `CAtomicFixnum` +* Refactored intermittently failing tests +* Added `dataflow!` and `dataflow_with!` methods to match `Future#value!` method +* Better handling of timeout in `Agent` +* Actor Improvements + - Fine-grained implementation using chain of behaviors. Each behavior is responsible for single aspect like: `Termination`, `Pausing`, `Linking`, `Supervising`, etc. Users can create custom Actors easily based on their needs. + - Supervision was added. `RestartingContext` will pause on error waiting on its supervisor to decide what to do next ( options are `:terminate!`, `:resume!`, `:reset!`, `:restart!`). Supervising behavior also supports strategies `:one_for_one` and `:one_for_all`. + - Linking was added to be able to monitor actor's events like: `:terminated`, `:paused`, `:restarted`, etc. + - Dead letter routing added. Rejected envelopes are collected in a configurable actor (default: `Concurrent::Actor.root.ask!(:dead_letter_routing)`) + - Old `Actor` class removed and replaced by new implementation previously called `Actress`. `Actress` was kept as an alias for `Actor` to keep compatibility. + - `Utils::Broadcast` actor which allows Publish–subscribe pattern. +* More executors for managing serialized operations + - `SerializedExecution` mixin module + - `SerializedExecutionDelegator` for serializing *any* executor +* Updated `Async` with serialized execution +* Updated `ImmediateExecutor` and `PerThreadExecutor` with full executor service lifecycle +* Added a `Delay` to root `Actress` initialization +* Minor bug fixes to thread pools +* Refactored many intermittently failing specs +* Removed Java interop warning `executor.rb:148 warning: ambiguous Java methods found, using submit(java.lang.Runnable)` +* Fixed minor bug in `RubyCachedThreadPool` overflow policy +* Updated tests to use [RSpec 3.0](http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3) +* Removed deprecated `Actor` class +* Better support for Rubinius + +## Release v0.6.1 (14 June 2014) + +* Many improvements to `Concurrent::Actress` +* Bug fixes to `Concurrent::RubyThreadPoolExecutor` +* Fixed several brittle tests +* Moved documentation to http://ruby-concurrency.github.io/concurrent-ruby/frames.html + +## Release v0.6.0 (25 May 2014) + +* Added `Concurrent::Observable` to encapsulate our thread safe observer sets +* Improvements to new `Channel` +* Major improvements to `CachedThreadPool` and `FixedThreadPool` +* Added `SingleThreadExecutor` +* Added `Current::timer` function +* Added `TimerSet` executor +* Added `AtomicBoolean` +* `ScheduledTask` refactoring +* Pure Ruby and JRuby-optimized `PriorityQueue` classes +* Updated `Agent` behavior to more closely match Clojure +* Observer sets support block callbacks to the `add_observer` method +* New algorithm for thread creation in `RubyThreadPoolExecutor` +* Minor API updates to `Event` +* Rewritten `TimerTask` now an `Executor` instead of a `Runnable` +* Fixed many brittle specs +* Renamed `FixedThreadPool` and `CachedThreadPool` to `RubyFixedThreadPool` and `RubyCachedThreadPool` +* Created JRuby optimized `JavaFixedThreadPool` and `JavaCachedThreadPool` +* Consolidated fixed thread pool tests into `spec/concurrent/fixed_thread_pool_shared.rb` and `spec/concurrent/cached_thread_pool_shared.rb` +* `FixedThreadPool` now subclasses `RubyFixedThreadPool` or `JavaFixedThreadPool` as appropriate +* `CachedThreadPool` now subclasses `RubyCachedThreadPool` or `JavaCachedThreadPool` as appropriate +* New `Delay` class +* `Concurrent::processor_count` helper function +* New `Async` module +* Renamed `NullThreadPool` to `PerThreadExecutor` +* Deprecated `Channel` (we are planning a new implementation based on [Go](http://golangtutorials.blogspot.com/2011/06/channels-in-go.html)) +* Added gem-level [configuration](http://robots.thoughtbot.com/mygem-configure-block) +* Deprecated `$GLOBAL_THREAD_POOL` in lieu of gem-level configuration +* Removed support for Ruby [1.9.2](https://www.ruby-lang.org/en/news/2013/12/17/maintenance-of-1-8-7-and-1-9-2/) +* New `RubyThreadPoolExecutor` and `JavaThreadPoolExecutor` classes +* All thread pools now extend the appropriate thread pool executor classes +* All thread pools now support `:overflow_policy` (based on Java's [reject policies](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html)) +* Deprecated `UsesGlobalThreadPool` in lieu of explicit `:executor` option (dependency injection) on `Future`, `Promise`, and `Agent` +* Added `Concurrent::dataflow_with(executor, *inputs)` method to support executor dependency injection for dataflow +* Software transactional memory with `TVar` and `Concurrent::atomically` +* First implementation of [new, high-performance](https://github.com/ruby-concurrency/concurrent-ruby/pull/49) `Channel` +* `Actor` is deprecated in favor of new experimental actor implementation [#73](https://github.com/ruby-concurrency/concurrent-ruby/pull/73). To avoid namespace collision it is living in `Actress` namespace until `Actor` is removed in next release. + +## Release v0.5.0 + +This is the most significant release of this gem since its inception. This release includes many improvements and optimizations. It also includes several bug fixes. The major areas of focus for this release were: + +* Stability improvements on Ruby versions with thread-level parallelism ([JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/)) +* Creation of new low-level concurrency abstractions +* Internal refactoring to use the new low-level abstractions + +Most of these updates had no effect on the gem API. There are a few notable exceptions which were unavoidable. Please read the [release notes](API-Updates-in-v0.5.0) for more information. + +Specific changes include: + +* New class `IVar` +* New class `MVar` +* New class `ThreadLocalVar` +* New class `AtomicFixnum` +* New class method `dataflow` +* New class `Condition` +* New class `CountDownLatch` +* New class `DependencyCounter` +* New class `SafeTaskExecutor` +* New class `CopyOnNotifyObserverSet` +* New class `CopyOnWriteObserverSet` +* `Future` updated with `execute` API +* `ScheduledTask` updated with `execute` API +* New `Promise` API +* `Future` now extends `IVar` +* `Postable#post?` now returns an `IVar` +* Thread safety fixes to `Dereferenceable` +* Thread safety fixes to `Obligation` +* Thread safety fixes to `Supervisor` +* Thread safety fixes to `Event` +* Various other thread safety (race condition) fixes +* Refactored brittle tests +* Implemented pending tests +* Added JRuby and Rubinius as Travis CI build targets +* Added [CodeClimate](https://codeclimate.com/) code review +* Improved YARD documentation diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/Gemfile b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/Gemfile new file mode 100644 index 0000000..f9ef172 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/Gemfile @@ -0,0 +1,37 @@ +source 'https://rubygems.org' + +require File.join(File.dirname(__FILE__), 'lib/concurrent-ruby/concurrent/version') +require File.join(File.dirname(__FILE__ ), 'lib/concurrent-ruby-edge/concurrent/edge/version') +require File.join(File.dirname(__FILE__ ), 'lib/concurrent-ruby/concurrent/utility/engine') + +no_path = ENV['NO_PATH'] +options = no_path ? {} : { path: '.' } + +gem 'concurrent-ruby', Concurrent::VERSION, options +gem 'concurrent-ruby-edge', Concurrent::EDGE_VERSION, options +gem 'concurrent-ruby-ext', Concurrent::VERSION, options.merge(platform: :mri) + +group :development do + gem 'rake', '~> 13.0' + gem 'rake-compiler', '~> 1.0', '>= 1.0.7' + gem 'rake-compiler-dock', '~> 1.0' + gem 'pry', '~> 0.11', platforms: :mri +end + +group :documentation, optional: true do + gem 'yard', '~> 0.9.0', require: false + gem 'redcarpet', '~> 3.0', platforms: :mri # understands github markdown + gem 'md-ruby-eval', '~> 0.6' +end + +group :testing do + gem 'rspec', '~> 3.7' + gem 'timecop', '~> 0.9' + gem 'sigdump', require: false +end + +# made opt-in since it will not install on jruby 1.7 +group :coverage, optional: !ENV['COVERAGE'] do + gem 'simplecov', '~> 0.16.0', require: false + gem 'coveralls', '~> 0.8.2', require: false +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/LICENSE.txt b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/LICENSE.txt new file mode 100644 index 0000000..1026f28 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/LICENSE.txt @@ -0,0 +1,21 @@ +Copyright (c) Jerry D'Antonio -- released under the MIT license. + +http://www.opensource.org/licenses/mit-license.php + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/README.md b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/README.md new file mode 100644 index 0000000..767f009 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/README.md @@ -0,0 +1,404 @@ +# Concurrent Ruby + +[![Gem Version](https://badge.fury.io/rb/concurrent-ruby.svg)](http://badge.fury.io/rb/concurrent-ruby) +[![License](https://img.shields.io/badge/license-MIT-green.svg)](http://opensource.org/licenses/MIT) +[![Gitter chat](https://img.shields.io/badge/IRC%20(gitter)-devs%20%26%20users-brightgreen.svg)](https://gitter.im/ruby-concurrency/concurrent-ruby) + +Modern concurrency tools for Ruby. Inspired by +[Erlang](http://www.erlang.org/doc/reference_manual/processes.html), +[Clojure](http://clojure.org/concurrent_programming), +[Scala](http://akka.io/), +[Haskell](http://www.haskell.org/haskellwiki/Applications_and_libraries/Concurrency_and_parallelism#Concurrent_Haskell), +[F#](http://blogs.msdn.com/b/dsyme/archive/2010/02/15/async-and-parallel-design-patterns-in-f-part-3-agents.aspx), +[C#](http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx), +[Java](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html), +and classic concurrency patterns. + + + +The design goals of this gem are: + +* Be an 'unopinionated' toolbox that provides useful utilities without debating which is better + or why +* Remain free of external gem dependencies +* Stay true to the spirit of the languages providing inspiration +* But implement in a way that makes sense for Ruby +* Keep the semantics as idiomatic Ruby as possible +* Support features that make sense in Ruby +* Exclude features that don't make sense in Ruby +* Be small, lean, and loosely coupled +* Thread-safety +* Backward compatibility + +## Contributing + +**This gem depends on +[contributions](https://github.com/ruby-concurrency/concurrent-ruby/graphs/contributors) and we +appreciate your help. Would you like to contribute? Great! Have a look at +[issues with `looking-for-contributor` label](https://github.com/ruby-concurrency/concurrent-ruby/issues?q=is%3Aissue+is%3Aopen+label%3Alooking-for-contributor).** And if you pick something up let us know on the issue. + +You can also get started by triaging issues which may include reproducing bug reports or asking for vital information, such as version numbers or reproduction instructions. If you would like to start triaging issues, one easy way to get started is to [subscribe to concurrent-ruby on CodeTriage](https://www.codetriage.com/ruby-concurrency/concurrent-ruby). [![Open Source Helpers](https://www.codetriage.com/ruby-concurrency/concurrent-ruby/badges/users.svg)](https://www.codetriage.com/ruby-concurrency/concurrent-ruby) + +## Thread Safety + +*Concurrent Ruby makes one of the strongest thread safety guarantees of any Ruby concurrency +library, providing consistent behavior and guarantees on all four of the main Ruby interpreters +(MRI/CRuby, JRuby, Rubinius, TruffleRuby).* + +Every abstraction in this library is thread safe. Specific thread safety guarantees are documented +with each abstraction. + +It is critical to remember, however, that Ruby is a language of mutable references. *No* +concurrency library for Ruby can ever prevent the user from making thread safety mistakes (such as +sharing a mutable object between threads and modifying it on both threads) or from creating +deadlocks through incorrect use of locks. All the library can do is provide safe abstractions which +encourage safe practices. Concurrent Ruby provides more safe concurrency abstractions than any +other Ruby library, many of which support the mantra of +["Do not communicate by sharing memory; instead, share memory by communicating"](https://blog.golang.org/share-memory-by-communicating). +Concurrent Ruby is also the only Ruby library which provides a full suite of thread safe and +immutable variable types and data structures. + +We've also initiated discussion to document [memory model](docs-source/synchronization.md) of Ruby which +would provide consistent behaviour and guarantees on all four of the main Ruby interpreters +(MRI/CRuby, JRuby, Rubinius, TruffleRuby). + +## Features & Documentation + +**The primary site for documentation is the automatically generated +[API documentation](http://ruby-concurrency.github.io/concurrent-ruby/index.html) which is up to +date with latest release.** This readme matches the master so may contain new stuff not yet +released. + +We also have a [IRC (gitter)](https://gitter.im/ruby-concurrency/concurrent-ruby). + +### Versioning + +* `concurrent-ruby` uses [Semantic Versioning](http://semver.org/) +* `concurrent-ruby-ext` has always same version as `concurrent-ruby` +* `concurrent-ruby-edge` will always be 0.y.z therefore following + [point 4](http://semver.org/#spec-item-4) applies *"Major version zero + (0.y.z) is for initial development. Anything may change at any time. The + public API should not be considered stable."* However we additionally use + following rules: + * Minor version increment means incompatible changes were made + * Patch version increment means only compatible changes were made + + +#### General-purpose Concurrency Abstractions + +* [Async](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Async.html): + A mixin module that provides simple asynchronous behavior to a class. Loosely based on Erlang's + [gen_server](http://www.erlang.org/doc/man/gen_server.html). +* [ScheduledTask](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ScheduledTask.html): + Like a Future scheduled for a specific future time. +* [TimerTask](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/TimerTask.html): + A Thread that periodically wakes up to perform work at regular intervals. +* [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html): + Unified implementation of futures and promises which combines features of previous `Future`, + `Promise`, `IVar`, `Event`, `dataflow`, `Delay`, and (partially) `TimerTask` into a single + framework. It extensively uses the new synchronization layer to make all the features + **non-blocking** and **lock-free**, with the exception of obviously blocking operations like + `#wait`, `#value`. It also offers better performance. + +#### Thread-safe Value Objects, Structures, and Collections + +Collection classes that were originally part of the (deprecated) `thread_safe` gem: + +* [Array](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Array.html) A thread-safe + subclass of Ruby's standard [Array](http://ruby-doc.org/core/Array.html). +* [Hash](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Hash.html) A thread-safe + subclass of Ruby's standard [Hash](http://ruby-doc.org/core/Hash.html). +* [Set](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Set.html) A thread-safe + subclass of Ruby's standard [Set](http://ruby-doc.org/stdlib-2.4.0/libdoc/set/rdoc/Set.html). +* [Map](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Map.html) A hash-like object + that should have much better performance characteristics, especially under high concurrency, + than `Concurrent::Hash`. +* [Tuple](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Tuple.html) A fixed size + array with volatile (synchronized, thread safe) getters/setters. + +Value objects inspired by other languages: + +* [Maybe](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Maybe.html) A thread-safe, + immutable object representing an optional value, based on + [Haskell Data.Maybe](https://hackage.haskell.org/package/base-4.2.0.1/docs/Data-Maybe.html). + +Structure classes derived from Ruby's [Struct](http://ruby-doc.org/core/Struct.html): + +* [ImmutableStruct](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ImmutableStruct.html) + Immutable struct where values are set at construction and cannot be changed later. +* [MutableStruct](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/MutableStruct.html) + Synchronized, mutable struct where values can be safely changed at any time. +* [SettableStruct](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/SettableStruct.html) + Synchronized, write-once struct where values can be set at most once, either at construction + or any time thereafter. + +Thread-safe variables: + +* [Agent](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Agent.html): A way to + manage shared, mutable, *asynchronous*, independent state. Based on Clojure's + [Agent](http://clojure.org/agents). +* [Atom](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Atom.html): A way to manage + shared, mutable, *synchronous*, independent state. Based on Clojure's + [Atom](http://clojure.org/atoms). +* [AtomicBoolean](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/AtomicBoolean.html) + A boolean value that can be updated atomically. +* [AtomicFixnum](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/AtomicFixnum.html) + A numeric value that can be updated atomically. +* [AtomicReference](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/AtomicReference.html) + An object reference that may be updated atomically. +* [Exchanger](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Exchanger.html) + A synchronization point at which threads can pair and swap elements within pairs. Based on + Java's [Exchanger](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Exchanger.html). +* [MVar](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/MVar.html) A synchronized + single element container. Based on Haskell's + [MVar](https://hackage.haskell.org/package/base-4.8.1.0/docs/Control-Concurrent-MVar.html) and + Scala's [MVar](http://docs.typelevel.org/api/scalaz/nightly/index.html#scalaz.concurrent.MVar$). +* [ThreadLocalVar](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ThreadLocalVar.html) + A variable where the value is different for each thread. +* [TVar](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/TVar.html) A transactional + variable implementing software transactional memory (STM). Based on Clojure's + [Ref](http://clojure.org/refs). + +#### Java-inspired ThreadPools and Other Executors + +* See the [thread pool](http://ruby-concurrency.github.io/concurrent-ruby/master/file.thread_pools.html) + overview, which also contains a list of other Executors available. + +#### Thread Synchronization Classes and Algorithms + +* [CountDownLatch](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/CountDownLatch.html) + A synchronization object that allows one thread to wait on multiple other threads. +* [CyclicBarrier](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/CyclicBarrier.html) + A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point. +* [Event](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Event.html) Old school + kernel-style event. +* [ReadWriteLock](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ReadWriteLock.html) + A lock that supports multiple readers but only one writer. +* [ReentrantReadWriteLock](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ReentrantReadWriteLock.html) + A read/write lock with reentrant and upgrade features. +* [Semaphore](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Semaphore.html) + A counting-based locking mechanism that uses permits. +* [AtomicMarkableReference](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/AtomicMarkableReference.html) + +#### Deprecated + +Deprecated features are still available and bugs are being fixed, but new features will not be added. + +* ~~[Future](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Future.html): + An asynchronous operation that produces a value.~~ Replaced by + [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html). + * ~~[.dataflow](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent.html#dataflow-class_method): + Built on Futures, Dataflow allows you to create a task that will be scheduled when all of + its data dependencies are available.~~ Replaced by + [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html). +* ~~[Promise](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promise.html): Similar + to Futures, with more features.~~ Replaced by + [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html). +* ~~[Delay](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Delay.html) Lazy evaluation + of a block yielding an immutable result. Based on Clojure's + [delay](https://clojuredocs.org/clojure.core/delay).~~ Replaced by + [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html). +* ~~[IVar](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/IVar.html) Similar to a + "future" but can be manually assigned once, after which it becomes immutable.~~ Replaced by + [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html). + +### Edge Features + +These are available in the `concurrent-ruby-edge` companion gem. + +These features are under active development and may change frequently. They are expected not to +keep backward compatibility (there may also lack tests and documentation). Semantic versions will +be obeyed though. Features developed in `concurrent-ruby-edge` are expected to move to +`concurrent-ruby` when final. + +* [Actor](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Actor.html): Implements + the Actor Model, where concurrent actors exchange messages. + *Status: Partial documentation and tests; depends on new future/promise framework; stability is good.* +* [Channel](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Channel.html): + Communicating Sequential Processes ([CSP](https://en.wikipedia.org/wiki/Communicating_sequential_processes)). + Functionally equivalent to Go [channels](https://tour.golang.org/concurrency/2) with additional + inspiration from Clojure [core.async](https://clojure.github.io/core.async/). + *Status: Partial documentation and tests.* +* [LazyRegister](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/LazyRegister.html) +* [LockFreeLinkedSet](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Edge/LockFreeLinkedSet.html) + *Status: will be moved to core soon.* +* [LockFreeStack](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/LockFreeStack.html) + *Status: missing documentation and tests.* +* [Promises::Channel](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises/Channel.html) + A first in first out channel that accepts messages with push family of methods and returns + messages with pop family of methods. + Pop and push operations can be represented as futures, see `#pop_op` and `#push_op`. + The capacity of the channel can be limited to support back pressure, use capacity option in `#initialize`. + `#pop` method blocks ans `#pop_op` returns pending future if there is no message in the channel. + If the capacity is limited the `#push` method blocks and `#push_op` returns pending future. +* [Cancellation](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Cancellation.html) + The Cancellation abstraction provides cooperative cancellation. + + The standard methods `Thread#raise` of `Thread#kill` available in Ruby + are very dangerous (see linked the blog posts bellow). + Therefore concurrent-ruby provides an alternative. + + * + * + * + + It provides an object which represents a task which can be executed, + the task has to get the reference to the object and periodically cooperatively check that it is not cancelled. + Good practices to make tasks cancellable: + * check cancellation every cycle of a loop which does significant work, + * do all blocking actions in a loop with a timeout then on timeout check cancellation + and if ok block again with the timeout +* [Throttle](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Throttle.html) + A tool managing concurrency level of tasks. +* [ErlangActor](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ErlangActor.html) + Actor implementation which precisely matches Erlang actor behaviour. + Requires at least Ruby 2.1 otherwise it's not loaded. +* [WrappingExecutor](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/WrappingExecutor.html) + A delegating executor which modifies each task before the task is given to + the target executor it delegates to. + +## Supported Ruby versions + +* MRI 2.2 and above +* Latest JRuby 9000 +* Latest TruffleRuby + +The legacy support for Rubinius is kept for the moment but it is no longer maintained and is liable to be removed. If you would like to help +please respond to [#739](https://github.com/ruby-concurrency/concurrent-ruby/issues/739). + +## Usage + +Everything within this gem can be loaded simply by requiring it: + +```ruby +require 'concurrent' +``` + +*Requiring only specific abstractions from Concurrent Ruby is not yet supported.* + +To use the tools in the Edge gem it must be required separately: + +```ruby +require 'concurrent-edge' +``` + +If the library does not behave as expected, `Concurrent.use_stdlib_logger(Logger::DEBUG)` could +help to reveal the problem. + +## Installation + +```shell +gem install concurrent-ruby +``` + +or add the following line to Gemfile: + +```ruby +gem 'concurrent-ruby', require: 'concurrent' +``` + +and run `bundle install` from your shell. + +### Edge Gem Installation + +The Edge gem must be installed separately from the core gem: + +```shell +gem install concurrent-ruby-edge +``` + +or add the following line to Gemfile: + +```ruby +gem 'concurrent-ruby-edge', require: 'concurrent-edge' +``` + +and run `bundle install` from your shell. + + +### C Extensions for MRI + +Potential performance improvements may be achieved under MRI by installing optional C extensions. +To minimise installation errors the C extensions are available in the `concurrent-ruby-ext` +extension gem. `concurrent-ruby` and `concurrent-ruby-ext` are always released together with same +version. Simply install the extension gem too: + +```ruby +gem install concurrent-ruby-ext +``` + +or add the following line to Gemfile: + +```ruby +gem 'concurrent-ruby-ext' +``` + +and run `bundle install` from your shell. + +In code it is only necessary to + +```ruby +require 'concurrent' +``` + +The `concurrent-ruby` gem will automatically detect the presence of the `concurrent-ruby-ext` gem +and load the appropriate C extensions. + +#### Note For gem developers + +No gems should depend on `concurrent-ruby-ext`. Doing so will force C extensions on your users. The +best practice is to depend on `concurrent-ruby` and let users to decide if they want C extensions. + +## Building the gem + +### Requirements + +* Recent CRuby +* JRuby, `rbenv install jruby-9.2.17.0` +* Set env variable `CONCURRENT_JRUBY_HOME` to point to it, e.g. `/usr/local/opt/rbenv/versions/jruby-9.2.17.0` +* Install Docker, required for Windows builds + +### Publishing the Gem + +* Update `version.rb` +* Update the CHANGELOG +* Update the Yard documentation + - Add the new version to `docs-source/signpost.md`. Needs to be done only if there are visible changes in the + documentation. + - Run `bundle exec rake yard` to update the master documentation and signpost. + - Run `bundle exec rake yard:` to add or update the documentation of the new version. +* Commit (and push) the changes. +* Use `be rake release` to release the gem. It consists + of `['release:checks', 'release:build', 'release:test', 'release:publish']` steps. It will ask at the end before + publishing anything. Steps can also be executed individually. + +## Maintainers + +* [Chris Seaton](https://github.com/chrisseaton) — Lead maintainer, point-of-contact. +* [Benoit Daloze](https://github.com/eregon) — If Chris is not available Benoit can help. + +### Special Thanks to + +* [Jerry D'Antonio](https://github.com/jdantonio) for creating the gem +* [Brian Durand](https://github.com/bdurand) for the `ref` gem +* [Charles Oliver Nutter](https://github.com/headius) for the `atomic` and `thread_safe` gems +* [thedarkone](https://github.com/thedarkone) for the `thread_safe` gem + +to the past maintainers + +* [Petr Chalupa](https://github.com/pitr-ch) +* [Michele Della Torre](https://github.com/mighe) +* [Paweł Obrok](https://github.com/obrok) +* [Lucas Allan](https://github.com/lucasallan) + +and to [Ruby Association](https://www.ruby.or.jp/en/) for sponsoring a project +["Enhancing Ruby’s concurrency tooling"](https://www.ruby.or.jp/en/news/20181106) in 2018. + +## License and Copyright + +*Concurrent Ruby* is free software released under the +[MIT License](http://www.opensource.org/licenses/MIT). + +The *Concurrent Ruby* [logo](https://raw.githubusercontent.com/ruby-concurrency/concurrent-ruby/master/docs-source/logo/concurrent-ruby-logo-300x300.png) was +designed by [David Jones](https://twitter.com/zombyboy). It is Copyright © 2014 +[Jerry D'Antonio](https://twitter.com/jerrydantonio). All Rights Reserved. diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/Rakefile b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/Rakefile new file mode 100644 index 0000000..55e78b7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/Rakefile @@ -0,0 +1,329 @@ +require_relative 'lib/concurrent-ruby/concurrent/version' +require_relative 'lib/concurrent-ruby-edge/concurrent/edge/version' +require_relative 'lib/concurrent-ruby/concurrent/utility/engine' + +core_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby.gemspec') +ext_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby-ext.gemspec') +edge_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby-edge.gemspec') + +require 'rake/javaextensiontask' + +ENV['JRUBY_HOME'] = ENV['CONCURRENT_JRUBY_HOME'] if ENV['CONCURRENT_JRUBY_HOME'] && !Concurrent.on_jruby? + +Rake::JavaExtensionTask.new('concurrent_ruby', core_gemspec) do |ext| + ext.ext_dir = 'ext/concurrent-ruby' + ext.lib_dir = 'lib/concurrent-ruby/concurrent' +end + +unless Concurrent.on_jruby? || Concurrent.on_truffleruby? + require 'rake/extensiontask' + + Rake::ExtensionTask.new('concurrent_ruby_ext', ext_gemspec) do |ext| + ext.ext_dir = 'ext/concurrent-ruby-ext' + ext.lib_dir = 'lib/concurrent-ruby/concurrent' + ext.source_pattern = '*.{c,h}' + + ext.cross_compile = true + ext.cross_platform = ['x86-mingw32', 'x64-mingw32'] + end +end + +require 'rake_compiler_dock' +namespace :repackage do + desc '* with Windows fat distributions' + task :all do + Dir.chdir(__dir__) do + # store gems in vendor cache for docker + sh 'bundle package' + + # build only the jar file not the whole gem for java platform, the jar is part the concurrent-ruby-x.y.z.gem + Rake::Task['lib/concurrent-ruby/concurrent/concurrent_ruby.jar'].invoke + + # build all gem files + %w[x86-mingw32 x64-mingw32].each do |plat| + RakeCompilerDock.sh "bundle install --local && bundle exec rake native:#{plat} gem --trace", platform: plat + end + end + end +end + +require 'rubygems' +require 'rubygems/package_task' + +Gem::PackageTask.new(core_gemspec) {} if core_gemspec +Gem::PackageTask.new(ext_gemspec) {} if ext_gemspec && !Concurrent.on_jruby? +Gem::PackageTask.new(edge_gemspec) {} if edge_gemspec + +CLEAN.include('lib/concurrent-ruby/concurrent/2.*', 'lib/concurrent-ruby/concurrent/*.jar') + +begin + require 'rspec' + require 'rspec/core/rake_task' + + RSpec::Core::RakeTask.new(:spec) + + namespace :spec do + desc '* Configured for ci' + RSpec::Core::RakeTask.new(:ci) do |t| + options = %w[ --color + --backtrace + --order defined + --format documentation ] + t.rspec_opts = [*options].join(' ') + end + + desc '* test packaged and installed gems instead of local files' + task :installed do + Dir.chdir(__dir__) do + sh "gem install pkg/concurrent-ruby-#{Concurrent::VERSION}.gem" + sh "gem install pkg/concurrent-ruby-ext-#{Concurrent::VERSION}.gem" if Concurrent.on_cruby? + sh "gem install pkg/concurrent-ruby-edge-#{Concurrent::EDGE_VERSION}.gem" + ENV['NO_PATH'] = 'true' + sh 'bundle update' + sh 'bundle exec rake spec:ci' + end + end + end + + desc 'executed in CI' + task :ci => [:compile, 'spec:ci'] + + task :default => [:clobber, :compile, :spec] +rescue LoadError => e + puts 'RSpec is not installed, skipping test task definitions: ' + e.message +end + +current_yard_version_name = Concurrent::VERSION + +begin + require 'yard' + require 'md_ruby_eval' + require_relative 'support/yard_full_types' + + common_yard_options = ['--no-yardopts', + '--no-document', + '--no-private', + '--embed-mixins', + '--markup', 'markdown', + '--title', 'Concurrent Ruby', + '--template', 'default', + '--template-path', 'yard-template', + '--default-return', 'undocumented'] + + desc 'Generate YARD Documentation (signpost, master)' + task :yard => ['yard:signpost', 'yard:master'] + + namespace :yard do + + desc '* eval markdown files' + task :eval_md do + Dir.chdir File.join(__dir__, 'docs-source') do + sh 'bundle exec md-ruby-eval --auto' + end + end + + task :update_readme do + Dir.chdir __dir__ do + content = File.read(File.join('README.md')). + gsub(/\[([\w ]+)\]\(http:\/\/ruby-concurrency\.github\.io\/concurrent-ruby\/master\/.*\)/) do |_| + case $1 + when 'LockFreeLinkedSet' + "{Concurrent::Edge::#{$1} #{$1}}" + when '.dataflow' + '{Concurrent.dataflow Concurrent.dataflow}' + when 'thread pool' + '{file:thread_pools.md thread pool}' + else + "{Concurrent::#{$1} #{$1}}" + end + end + FileUtils.mkpath 'tmp' + File.write 'tmp/README.md', content + end + end + + define_yard_task = -> name do + output_dir = "docs/#{name}" + + removal_name = "remove.#{name}" + task removal_name do + Dir.chdir __dir__ do + FileUtils.rm_rf output_dir + end + end + + desc "* of #{name} into subdir #{name}" + YARD::Rake::YardocTask.new(name) do |yard| + yard.options.push( + '--output-dir', output_dir, + '--main', 'tmp/README.md', + *common_yard_options) + yard.files = ['./lib/concurrent-ruby/**/*.rb', + './lib/concurrent-ruby-edge/**/*.rb', + './ext/concurrent_ruby_ext/**/*.c', + '-', + 'docs-source/thread_pools.md', + 'docs-source/promises.out.md', + 'docs-source/medium-example.out.rb', + 'LICENSE.txt', + 'CHANGELOG.md'] + end + Rake::Task[name].prerequisites.push removal_name, + # 'yard:eval_md', + 'yard:update_readme' + end + + define_yard_task.call current_yard_version_name + define_yard_task.call 'master' + + desc "* signpost for versions" + YARD::Rake::YardocTask.new(:signpost) do |yard| + yard.options.push( + '--output-dir', 'docs', + '--main', 'docs-source/signpost.md', + *common_yard_options) + yard.files = ['no-lib'] + end + + define_uptodate_task = -> name do + namespace name do + desc "** ensure that #{name} generated documentation is matching the source code" + task :uptodate do + Dir.chdir(__dir__) do + begin + FileUtils.cp_r 'docs', 'docs-copy', verbose: true + Rake::Task["yard:#{name}"].invoke + sh 'diff -r docs/ docs-copy/' do |ok, res| + unless ok + begin + STDOUT.puts "yard:#{name} is not properly generated and committed.", "Continue? (y/n)" + input = STDIN.gets.strip.downcase + end until %w(y n).include?(input) + exit 1 if input == 'n' + end + end + ensure + FileUtils.rm_rf 'docs-copy', verbose: true + end + end + end + end + end + + define_uptodate_task.call current_yard_version_name + define_uptodate_task.call 'master' + end + +rescue LoadError => e + puts 'YARD is not installed, skipping documentation task definitions: ' + e.message +end + +desc 'build, test, and publish the gem' +task :release => ['release:checks', 'release:build', 'release:test', 'release:publish'] + +namespace :release do + # Depends on environment of @pitr-ch + + task :checks => "yard:#{current_yard_version_name}:uptodate" do + Dir.chdir(__dir__) do + sh 'test -z "$(git status --porcelain)"' do |ok, res| + unless ok + begin + status = `git status --porcelain` + STDOUT.puts 'There are local changes that you might want to commit.', status, 'Continue? (y/n)' + input = STDIN.gets.strip.downcase + end until %w(y n).include?(input) + exit 1 if input == 'n' + end + end + sh 'git fetch' + sh 'test $(git show-ref --verify --hash refs/heads/master) = ' + + '$(git show-ref --verify --hash refs/remotes/origin/master)' do |ok, res| + unless ok + begin + STDOUT.puts 'Local master branch is not pushed to origin.', 'Continue? (y/n)' + input = STDIN.gets.strip.downcase + end until %w(y n).include?(input) + exit 1 if input == 'n' + end + end + end + end + + desc '* build all *.gem files necessary for release' + task :build => [:clobber, 'repackage:all'] + + desc '* test actual installed gems instead of cloned repository on MRI and JRuby' + task :test do + Dir.chdir(__dir__) do + old = ENV['RBENV_VERSION'] + + mri_version = `ruby -e 'puts RUBY_VERSION'`.chomp + jruby_version = File.basename(ENV['CONCURRENT_JRUBY_HOME']) + + puts "Using following version:" + pp mri_version: mri_version, jruby_version: jruby_version + + ENV['RBENV_VERSION'] = mri_version + sh 'rbenv version' + sh 'bundle exec rake spec:installed' + + ENV['RBENV_VERSION'] = jruby_version + sh 'rbenv version' + sh 'bundle exec rake spec:installed' + + puts 'Windows build is untested' + + ENV['RBENV_VERSION'] = old + end + end + + desc '* do all nested steps' + task :publish => ['publish:ask', 'publish:tag', 'publish:rubygems', 'publish:post_steps'] + + namespace :publish do + publish_edge = false + + task :ask do + begin + STDOUT.puts 'Do you want to publish anything now? (y/n)' + input = STDIN.gets.strip.downcase + end until %w(y n).include?(input) + exit 1 if input == 'n' + begin + STDOUT.puts 'It will publish `concurrent-ruby`. Do you want to publish `concurrent-ruby-edge`? (y/n)' + input = STDIN.gets.strip.downcase + end until %w(y n).include?(input) + publish_edge = input == 'y' + end + + desc '** tag HEAD with current version and push to github' + task :tag => :ask do + Dir.chdir(__dir__) do + sh "git tag v#{Concurrent::VERSION}" + sh "git push origin v#{Concurrent::VERSION}" + sh "git tag edge-v#{Concurrent::EDGE_VERSION}" if publish_edge + sh "git push origin edge-v#{Concurrent::EDGE_VERSION}" if publish_edge + end + end + + desc '** push all *.gem files to rubygems' + task :rubygems => :ask do + Dir.chdir(__dir__) do + sh "gem push pkg/concurrent-ruby-#{Concurrent::VERSION}.gem" + sh "gem push pkg/concurrent-ruby-edge-#{Concurrent::EDGE_VERSION}.gem" if publish_edge + sh "gem push pkg/concurrent-ruby-ext-#{Concurrent::VERSION}.gem" + sh "gem push pkg/concurrent-ruby-ext-#{Concurrent::VERSION}-x64-mingw32.gem" + sh "gem push pkg/concurrent-ruby-ext-#{Concurrent::VERSION}-x86-mingw32.gem" + end + end + + desc '** print post release steps' + task :post_steps do + # TODO: (petr 05-Jun-2021) automate and renew the process + # puts 'Manually: create a release on GitHub with relevant changelog part' + # puts 'Manually: send email same as release with relevant changelog part' + # puts 'Manually: tweet' + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/ConcurrentRubyService.java b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/ConcurrentRubyService.java new file mode 100644 index 0000000..fb6be96 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/ConcurrentRubyService.java @@ -0,0 +1,17 @@ +import org.jruby.Ruby; +import org.jruby.runtime.load.BasicLibraryService; + +import java.io.IOException; + +public class ConcurrentRubyService implements BasicLibraryService { + + public boolean basicLoad(final Ruby runtime) throws IOException { + new com.concurrent_ruby.ext.AtomicReferenceLibrary().load(runtime, false); + new com.concurrent_ruby.ext.JavaAtomicBooleanLibrary().load(runtime, false); + new com.concurrent_ruby.ext.JavaAtomicFixnumLibrary().load(runtime, false); + new com.concurrent_ruby.ext.JavaSemaphoreLibrary().load(runtime, false); + new com.concurrent_ruby.ext.SynchronizationLibrary().load(runtime, false); + new com.concurrent_ruby.ext.JRubyMapBackendLibrary().load(runtime, false); + return true; + } +} diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java new file mode 100644 index 0000000..dfa9e77 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java @@ -0,0 +1,175 @@ +package com.concurrent_ruby.ext; + +import java.lang.reflect.Field; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyNumeric; +import org.jruby.RubyObject; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.runtime.load.Library; + +/** + * This library adds an atomic reference type to JRuby for use in the atomic + * library. We do a native version to avoid the implicit value coercion that + * normally happens through JI. + * + * @author headius + */ +public class AtomicReferenceLibrary implements Library { + public void load(Ruby runtime, boolean wrap) throws IOException { + RubyModule concurrentMod = runtime.defineModule("Concurrent"); + RubyClass atomicCls = concurrentMod.defineClassUnder("JavaAtomicReference", runtime.getObject(), JRUBYREFERENCE_ALLOCATOR); + try { + sun.misc.Unsafe.class.getMethod("getAndSetObject", Object.class); + atomicCls.setAllocator(JRUBYREFERENCE8_ALLOCATOR); + } catch (Exception e) { + // leave it as Java 6/7 version + } + atomicCls.defineAnnotatedMethods(JRubyReference.class); + } + + private static final ObjectAllocator JRUBYREFERENCE_ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new JRubyReference(runtime, klazz); + } + }; + + private static final ObjectAllocator JRUBYREFERENCE8_ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new JRubyReference8(runtime, klazz); + } + }; + + @JRubyClass(name="JRubyReference", parent="Object") + public static class JRubyReference extends RubyObject { + volatile IRubyObject reference; + + static final sun.misc.Unsafe UNSAFE; + static final long referenceOffset; + + static { + try { + UNSAFE = UnsafeHolder.U; + Class k = JRubyReference.class; + referenceOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("reference")); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public JRubyReference(Ruby runtime, RubyClass klass) { + super(runtime, klass); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context) { + UNSAFE.putObject(this, referenceOffset, context.nil); + return context.nil; + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context, IRubyObject value) { + UNSAFE.putObject(this, referenceOffset, value); + return context.nil; + } + + @JRubyMethod(name = {"get", "value"}) + public IRubyObject get() { + return reference; + } + + @JRubyMethod(name = {"set", "value="}) + public IRubyObject set(IRubyObject newValue) { + UNSAFE.putObjectVolatile(this, referenceOffset, newValue); + return newValue; + } + + @JRubyMethod(name = {"compare_and_set", "compare_and_swap"}) + public IRubyObject compare_and_set(ThreadContext context, IRubyObject expectedValue, IRubyObject newValue) { + Ruby runtime = context.runtime; + + if (expectedValue instanceof RubyNumeric) { + // numerics are not always idempotent in Ruby, so we need to do slower logic + return compareAndSetNumeric(context, expectedValue, newValue); + } + + return runtime.newBoolean(UNSAFE.compareAndSwapObject(this, referenceOffset, expectedValue, newValue)); + } + + @JRubyMethod(name = {"get_and_set", "swap"}) + public IRubyObject get_and_set(ThreadContext context, IRubyObject newValue) { + // less-efficient version for Java 6 and 7 + while (true) { + IRubyObject oldValue = get(); + if (UNSAFE.compareAndSwapObject(this, referenceOffset, oldValue, newValue)) { + return oldValue; + } + } + } + + private IRubyObject compareAndSetNumeric(ThreadContext context, IRubyObject expectedValue, IRubyObject newValue) { + Ruby runtime = context.runtime; + + // loop until: + // * reference CAS would succeed for same-valued objects + // * current and expected have different values as determined by #equals + while (true) { + IRubyObject current = reference; + + if (!(current instanceof RubyNumeric)) { + // old value is not numeric, CAS fails + return runtime.getFalse(); + } + + RubyNumeric currentNumber = (RubyNumeric)current; + if (!currentNumber.equals(expectedValue)) { + // current number does not equal expected, fail CAS + return runtime.getFalse(); + } + + // check that current has not changed, or else allow loop to repeat + boolean success = UNSAFE.compareAndSwapObject(this, referenceOffset, current, newValue); + if (success) { + // value is same and did not change in interim...success + return runtime.getTrue(); + } + } + } + } + + private static final class UnsafeHolder { + private UnsafeHolder(){} + + public static final sun.misc.Unsafe U = loadUnsafe(); + + private static sun.misc.Unsafe loadUnsafe() { + try { + Class unsafeClass = Class.forName("sun.misc.Unsafe"); + Field f = unsafeClass.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (sun.misc.Unsafe) f.get(null); + } catch (Exception e) { + return null; + } + } + } + + public static class JRubyReference8 extends JRubyReference { + public JRubyReference8(Ruby runtime, RubyClass klass) { + super(runtime, klass); + } + + @Override + public IRubyObject get_and_set(ThreadContext context, IRubyObject newValue) { + // efficient version for Java 8 + return (IRubyObject)UNSAFE.getAndSetObject(this, referenceOffset, newValue); + } + } +} diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java new file mode 100644 index 0000000..a09f916 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java @@ -0,0 +1,248 @@ +package com.concurrent_ruby.ext; + +import org.jruby.*; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import com.concurrent_ruby.ext.jsr166e.ConcurrentHashMap; +import com.concurrent_ruby.ext.jsr166e.ConcurrentHashMapV8; +import com.concurrent_ruby.ext.jsr166e.nounsafe.*; +import org.jruby.runtime.Block; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.runtime.load.Library; + +import java.io.IOException; +import java.util.Map; + +import static org.jruby.runtime.Visibility.PRIVATE; + +/** + * Native Java implementation to avoid the JI overhead. + * + * @author thedarkone + */ +public class JRubyMapBackendLibrary implements Library { + public void load(Ruby runtime, boolean wrap) throws IOException { + + RubyModule concurrentMod = runtime.defineModule("Concurrent"); + RubyModule thread_safeMod = concurrentMod.defineModuleUnder("Collection"); + RubyClass jrubyRefClass = thread_safeMod.defineClassUnder("JRubyMapBackend", runtime.getObject(), BACKEND_ALLOCATOR); + jrubyRefClass.setAllocator(BACKEND_ALLOCATOR); + jrubyRefClass.defineAnnotatedMethods(JRubyMapBackend.class); + } + + private static final ObjectAllocator BACKEND_ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new JRubyMapBackend(runtime, klazz); + } + }; + + @JRubyClass(name="JRubyMapBackend", parent="Object") + public static class JRubyMapBackend extends RubyObject { + // Defaults used by the CHM + static final int DEFAULT_INITIAL_CAPACITY = 16; + static final float DEFAULT_LOAD_FACTOR = 0.75f; + + public static final boolean CAN_USE_UNSAFE_CHM = canUseUnsafeCHM(); + + private ConcurrentHashMap map; + + private static ConcurrentHashMap newCHM(int initialCapacity, float loadFactor) { + if (CAN_USE_UNSAFE_CHM) { + return new ConcurrentHashMapV8(initialCapacity, loadFactor); + } else { + return new com.concurrent_ruby.ext.jsr166e.nounsafe.ConcurrentHashMapV8(initialCapacity, loadFactor); + } + } + + private static ConcurrentHashMap newCHM() { + return newCHM(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); + } + + private static boolean canUseUnsafeCHM() { + try { + new com.concurrent_ruby.ext.jsr166e.ConcurrentHashMapV8(); // force class load and initialization + return true; + } catch (Throwable t) { // ensuring we really do catch everything + // Doug's Unsafe setup errors always have this "Could not ini.." message + if (isCausedBySecurityException(t)) { + return false; + } + throw (t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t)); + } + } + + private static boolean isCausedBySecurityException(Throwable t) { + while (t != null) { + if ((t.getMessage() != null && t.getMessage().contains("Could not initialize intrinsics")) || t instanceof SecurityException) { + return true; + } + t = t.getCause(); + } + return false; + } + + public JRubyMapBackend(Ruby runtime, RubyClass klass) { + super(runtime, klass); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context) { + map = newCHM(); + return context.getRuntime().getNil(); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context, IRubyObject options) { + map = toCHM(context, options); + return context.getRuntime().getNil(); + } + + private ConcurrentHashMap toCHM(ThreadContext context, IRubyObject options) { + Ruby runtime = context.getRuntime(); + if (!options.isNil() && options.respondsTo("[]")) { + IRubyObject rInitialCapacity = options.callMethod(context, "[]", runtime.newSymbol("initial_capacity")); + IRubyObject rLoadFactor = options.callMethod(context, "[]", runtime.newSymbol("load_factor")); + int initialCapacity = !rInitialCapacity.isNil() ? RubyNumeric.num2int(rInitialCapacity.convertToInteger()) : DEFAULT_INITIAL_CAPACITY; + float loadFactor = !rLoadFactor.isNil() ? (float)RubyNumeric.num2dbl(rLoadFactor.convertToFloat()) : DEFAULT_LOAD_FACTOR; + return newCHM(initialCapacity, loadFactor); + } else { + return newCHM(); + } + } + + @JRubyMethod(name = "[]", required = 1) + public IRubyObject op_aref(ThreadContext context, IRubyObject key) { + IRubyObject value; + return ((value = map.get(key)) == null) ? context.getRuntime().getNil() : value; + } + + @JRubyMethod(name = {"[]="}, required = 2) + public IRubyObject op_aset(IRubyObject key, IRubyObject value) { + map.put(key, value); + return value; + } + + @JRubyMethod + public IRubyObject put_if_absent(IRubyObject key, IRubyObject value) { + IRubyObject result = map.putIfAbsent(key, value); + return result == null ? getRuntime().getNil() : result; + } + + @JRubyMethod + public IRubyObject compute_if_absent(final ThreadContext context, final IRubyObject key, final Block block) { + return map.computeIfAbsent(key, new ConcurrentHashMap.Fun() { + @Override + public IRubyObject apply(IRubyObject key) { + return block.yieldSpecific(context); + } + }); + } + + @JRubyMethod + public IRubyObject compute_if_present(final ThreadContext context, final IRubyObject key, final Block block) { + IRubyObject result = map.computeIfPresent(key, new ConcurrentHashMap.BiFun() { + @Override + public IRubyObject apply(IRubyObject key, IRubyObject oldValue) { + IRubyObject result = block.yieldSpecific(context, oldValue == null ? context.getRuntime().getNil() : oldValue); + return result.isNil() ? null : result; + } + }); + return result == null ? context.getRuntime().getNil() : result; + } + + @JRubyMethod + public IRubyObject compute(final ThreadContext context, final IRubyObject key, final Block block) { + IRubyObject result = map.compute(key, new ConcurrentHashMap.BiFun() { + @Override + public IRubyObject apply(IRubyObject key, IRubyObject oldValue) { + IRubyObject result = block.yieldSpecific(context, oldValue == null ? context.getRuntime().getNil() : oldValue); + return result.isNil() ? null : result; + } + }); + return result == null ? context.getRuntime().getNil() : result; + } + + @JRubyMethod + public IRubyObject merge_pair(final ThreadContext context, final IRubyObject key, final IRubyObject value, final Block block) { + IRubyObject result = map.merge(key, value, new ConcurrentHashMap.BiFun() { + @Override + public IRubyObject apply(IRubyObject oldValue, IRubyObject newValue) { + IRubyObject result = block.yieldSpecific(context, oldValue == null ? context.getRuntime().getNil() : oldValue); + return result.isNil() ? null : result; + } + }); + return result == null ? context.getRuntime().getNil() : result; + } + + @JRubyMethod + public RubyBoolean replace_pair(IRubyObject key, IRubyObject oldValue, IRubyObject newValue) { + return getRuntime().newBoolean(map.replace(key, oldValue, newValue)); + } + + @JRubyMethod(name = "key?", required = 1) + public RubyBoolean has_key_p(IRubyObject key) { + return map.containsKey(key) ? getRuntime().getTrue() : getRuntime().getFalse(); + } + + @JRubyMethod + public IRubyObject key(IRubyObject value) { + final IRubyObject key = map.findKey(value); + return key == null ? getRuntime().getNil() : key; + } + + @JRubyMethod + public IRubyObject replace_if_exists(IRubyObject key, IRubyObject value) { + IRubyObject result = map.replace(key, value); + return result == null ? getRuntime().getNil() : result; + } + + @JRubyMethod + public IRubyObject get_and_set(IRubyObject key, IRubyObject value) { + IRubyObject result = map.put(key, value); + return result == null ? getRuntime().getNil() : result; + } + + @JRubyMethod + public IRubyObject delete(IRubyObject key) { + IRubyObject result = map.remove(key); + return result == null ? getRuntime().getNil() : result; + } + + @JRubyMethod + public RubyBoolean delete_pair(IRubyObject key, IRubyObject value) { + return getRuntime().newBoolean(map.remove(key, value)); + } + + @JRubyMethod + public IRubyObject clear() { + map.clear(); + return this; + } + + @JRubyMethod + public IRubyObject each_pair(ThreadContext context, Block block) { + for (Map.Entry entry : map.entrySet()) { + block.yieldSpecific(context, entry.getKey(), entry.getValue()); + } + return this; + } + + @JRubyMethod + public RubyFixnum size(ThreadContext context) { + return context.getRuntime().newFixnum(map.size()); + } + + @JRubyMethod + public IRubyObject get_or_default(IRubyObject key, IRubyObject defaultValue) { + return map.getValueOrDefault(key, defaultValue); + } + + @JRubyMethod(visibility = PRIVATE) + public JRubyMapBackend initialize_copy(ThreadContext context, IRubyObject other) { + map = newCHM(); + return this; + } + } +} diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java new file mode 100644 index 0000000..b566076 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java @@ -0,0 +1,93 @@ +package com.concurrent_ruby.ext; + +import org.jruby.Ruby; +import org.jruby.RubyBoolean; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyNil; +import org.jruby.RubyObject; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.runtime.load.Library; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; + +public class JavaAtomicBooleanLibrary implements Library { + + public void load(Ruby runtime, boolean wrap) throws IOException { + RubyModule concurrentMod = runtime.defineModule("Concurrent"); + RubyClass atomicCls = concurrentMod.defineClassUnder("JavaAtomicBoolean", runtime.getObject(), JRUBYREFERENCE_ALLOCATOR); + atomicCls.defineAnnotatedMethods(JavaAtomicBoolean.class); + } + + private static final ObjectAllocator JRUBYREFERENCE_ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new JavaAtomicBoolean(runtime, klazz); + } + }; + + @JRubyClass(name = "JavaAtomicBoolean", parent = "Object") + public static class JavaAtomicBoolean extends RubyObject { + + private AtomicBoolean atomicBoolean; + + public JavaAtomicBoolean(Ruby runtime, RubyClass metaClass) { + super(runtime, metaClass); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context, IRubyObject value) { + atomicBoolean = new AtomicBoolean(convertRubyBooleanToJavaBoolean(value)); + return context.nil; + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context) { + atomicBoolean = new AtomicBoolean(); + return context.nil; + } + + @JRubyMethod(name = "value") + public IRubyObject value() { + return getRuntime().newBoolean(atomicBoolean.get()); + } + + @JRubyMethod(name = "true?") + public IRubyObject isAtomicTrue() { + return getRuntime().newBoolean(atomicBoolean.get()); + } + + @JRubyMethod(name = "false?") + public IRubyObject isAtomicFalse() { + return getRuntime().newBoolean((atomicBoolean.get() == false)); + } + + @JRubyMethod(name = "value=") + public IRubyObject setAtomic(ThreadContext context, IRubyObject newValue) { + atomicBoolean.set(convertRubyBooleanToJavaBoolean(newValue)); + return context.nil; + } + + @JRubyMethod(name = "make_true") + public IRubyObject makeTrue() { + return getRuntime().newBoolean(atomicBoolean.compareAndSet(false, true)); + } + + @JRubyMethod(name = "make_false") + public IRubyObject makeFalse() { + return getRuntime().newBoolean(atomicBoolean.compareAndSet(true, false)); + } + + private boolean convertRubyBooleanToJavaBoolean(IRubyObject newValue) { + if (newValue instanceof RubyBoolean.False || newValue instanceof RubyNil) { + return false; + } else { + return true; + } + } + } +} diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java new file mode 100644 index 0000000..672bfc0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java @@ -0,0 +1,113 @@ +package com.concurrent_ruby.ext; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicLong; +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyFixnum; +import org.jruby.RubyModule; +import org.jruby.RubyObject; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.runtime.load.Library; +import org.jruby.runtime.Block; + +public class JavaAtomicFixnumLibrary implements Library { + + public void load(Ruby runtime, boolean wrap) throws IOException { + RubyModule concurrentMod = runtime.defineModule("Concurrent"); + RubyClass atomicCls = concurrentMod.defineClassUnder("JavaAtomicFixnum", runtime.getObject(), JRUBYREFERENCE_ALLOCATOR); + + atomicCls.defineAnnotatedMethods(JavaAtomicFixnum.class); + } + + private static final ObjectAllocator JRUBYREFERENCE_ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new JavaAtomicFixnum(runtime, klazz); + } + }; + + @JRubyClass(name = "JavaAtomicFixnum", parent = "Object") + public static class JavaAtomicFixnum extends RubyObject { + + private AtomicLong atomicLong; + + public JavaAtomicFixnum(Ruby runtime, RubyClass metaClass) { + super(runtime, metaClass); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context) { + this.atomicLong = new AtomicLong(0); + return context.nil; + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context, IRubyObject value) { + this.atomicLong = new AtomicLong(rubyFixnumToLong(value)); + return context.nil; + } + + @JRubyMethod(name = "value") + public IRubyObject getValue() { + return getRuntime().newFixnum(atomicLong.get()); + } + + @JRubyMethod(name = "value=") + public IRubyObject setValue(ThreadContext context, IRubyObject newValue) { + atomicLong.set(rubyFixnumToLong(newValue)); + return context.nil; + } + + @JRubyMethod(name = {"increment", "up"}) + public IRubyObject increment() { + return getRuntime().newFixnum(atomicLong.incrementAndGet()); + } + + @JRubyMethod(name = {"increment", "up"}) + public IRubyObject increment(IRubyObject value) { + long delta = rubyFixnumToLong(value); + return getRuntime().newFixnum(atomicLong.addAndGet(delta)); + } + + @JRubyMethod(name = {"decrement", "down"}) + public IRubyObject decrement() { + return getRuntime().newFixnum(atomicLong.decrementAndGet()); + } + + @JRubyMethod(name = {"decrement", "down"}) + public IRubyObject decrement(IRubyObject value) { + long delta = rubyFixnumToLong(value); + return getRuntime().newFixnum(atomicLong.addAndGet(-delta)); + } + + @JRubyMethod(name = "compare_and_set") + public IRubyObject compareAndSet(ThreadContext context, IRubyObject expect, IRubyObject update) { + return getRuntime().newBoolean(atomicLong.compareAndSet(rubyFixnumToLong(expect), rubyFixnumToLong(update))); + } + + @JRubyMethod + public IRubyObject update(ThreadContext context, Block block) { + for (;;) { + long _oldValue = atomicLong.get(); + IRubyObject oldValue = getRuntime().newFixnum(_oldValue); + IRubyObject newValue = block.yield(context, oldValue); + if (atomicLong.compareAndSet(_oldValue, rubyFixnumToLong(newValue))) { + return newValue; + } + } + } + + private long rubyFixnumToLong(IRubyObject value) { + if (value instanceof RubyFixnum) { + RubyFixnum fixNum = (RubyFixnum) value; + return fixNum.getLongValue(); + } else { + throw getRuntime().newArgumentError("value must be a Fixnum"); + } + } + } +} diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java new file mode 100644 index 0000000..d887f25 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java @@ -0,0 +1,189 @@ +package com.concurrent_ruby.ext; + +import java.io.IOException; +import java.util.concurrent.Semaphore; +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyFixnum; +import org.jruby.RubyModule; +import org.jruby.RubyNumeric; +import org.jruby.RubyObject; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.Block; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +public class JavaSemaphoreLibrary { + + public void load(Ruby runtime, boolean wrap) throws IOException { + RubyModule concurrentMod = runtime.defineModule("Concurrent"); + RubyClass atomicCls = concurrentMod.defineClassUnder("JavaSemaphore", runtime.getObject(), JRUBYREFERENCE_ALLOCATOR); + + atomicCls.defineAnnotatedMethods(JavaSemaphore.class); + } + + private static final ObjectAllocator JRUBYREFERENCE_ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new JavaSemaphore(runtime, klazz); + } + }; + + @JRubyClass(name = "JavaSemaphore", parent = "Object") + public static class JavaSemaphore extends RubyObject { + + private JRubySemaphore semaphore; + + public JavaSemaphore(Ruby runtime, RubyClass metaClass) { + super(runtime, metaClass); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context, IRubyObject value) { + this.semaphore = new JRubySemaphore(rubyFixnumInt(value, "count")); + return context.nil; + } + + @JRubyMethod + public IRubyObject acquire(ThreadContext context, final Block block) throws InterruptedException { + return this.acquire(context, 1, block); + } + + @JRubyMethod + public IRubyObject acquire(ThreadContext context, IRubyObject permits, final Block block) throws InterruptedException { + return this.acquire(context, rubyFixnumToPositiveInt(permits, "permits"), block); + } + + @JRubyMethod(name = "available_permits") + public IRubyObject availablePermits(ThreadContext context) { + return getRuntime().newFixnum(this.semaphore.availablePermits()); + } + + @JRubyMethod(name = "drain_permits") + public IRubyObject drainPermits(ThreadContext context) { + return getRuntime().newFixnum(this.semaphore.drainPermits()); + } + + @JRubyMethod(name = "try_acquire") + public IRubyObject tryAcquire(ThreadContext context, final Block block) throws InterruptedException { + int permitsInt = 1; + boolean acquired = semaphore.tryAcquire(permitsInt); + + return triedAcquire(context, permitsInt, acquired, block); + } + + @JRubyMethod(name = "try_acquire") + public IRubyObject tryAcquire(ThreadContext context, IRubyObject permits, final Block block) throws InterruptedException { + int permitsInt = rubyFixnumToPositiveInt(permits, "permits"); + boolean acquired = semaphore.tryAcquire(permitsInt); + + return triedAcquire(context, permitsInt, acquired, block); + } + + @JRubyMethod(name = "try_acquire") + public IRubyObject tryAcquire(ThreadContext context, IRubyObject permits, IRubyObject timeout, final Block block) throws InterruptedException { + int permitsInt = rubyFixnumToPositiveInt(permits, "permits"); + boolean acquired = semaphore.tryAcquire( + permitsInt, + rubyNumericToLong(timeout, "timeout"), + java.util.concurrent.TimeUnit.SECONDS + ); + + return triedAcquire(context, permitsInt, acquired, block); + } + + @JRubyMethod + public IRubyObject release(ThreadContext context) { + this.semaphore.release(1); + return getRuntime().newBoolean(true); + } + + @JRubyMethod + public IRubyObject release(ThreadContext context, IRubyObject permits) { + this.semaphore.release(rubyFixnumToPositiveInt(permits, "permits")); + return getRuntime().newBoolean(true); + } + + @JRubyMethod(name = "reduce_permits") + public IRubyObject reducePermits(ThreadContext context, IRubyObject reduction) throws InterruptedException { + this.semaphore.publicReducePermits(rubyFixnumToNonNegativeInt(reduction, "reduction")); + return context.nil; + } + + private IRubyObject acquire(ThreadContext context, int permits, final Block block) throws InterruptedException { + this.semaphore.acquire(permits); + + if (!block.isGiven()) return context.nil; + + try { + return block.yieldSpecific(context); + } finally { + this.semaphore.release(permits); + } + } + + private IRubyObject triedAcquire(ThreadContext context, int permits, boolean acquired, final Block block) { + if (!block.isGiven()) return getRuntime().newBoolean(acquired); + if (!acquired) return context.nil; + + try { + return block.yieldSpecific(context); + } finally { + this.semaphore.release(permits); + } + } + + private int rubyFixnumInt(IRubyObject value, String paramName) { + if (value instanceof RubyFixnum) { + RubyFixnum fixNum = (RubyFixnum) value; + return (int) fixNum.getLongValue(); + } else { + throw getRuntime().newArgumentError(paramName + " must be integer"); + } + } + + private int rubyFixnumToNonNegativeInt(IRubyObject value, String paramName) { + if (value instanceof RubyFixnum && ((RubyFixnum) value).getLongValue() >= 0) { + RubyFixnum fixNum = (RubyFixnum) value; + return (int) fixNum.getLongValue(); + } else { + throw getRuntime().newArgumentError(paramName + " must be a non-negative integer"); + } + } + + private int rubyFixnumToPositiveInt(IRubyObject value, String paramName) { + if (value instanceof RubyFixnum && ((RubyFixnum) value).getLongValue() > 0) { + RubyFixnum fixNum = (RubyFixnum) value; + return (int) fixNum.getLongValue(); + } else { + throw getRuntime().newArgumentError(paramName + " must be an integer greater than zero"); + } + } + + private long rubyNumericToLong(IRubyObject value, String paramName) { + if (value instanceof RubyNumeric && ((RubyNumeric) value).getDoubleValue() > 0) { + RubyNumeric fixNum = (RubyNumeric) value; + return fixNum.getLongValue(); + } else { + throw getRuntime().newArgumentError(paramName + " must be a float greater than zero"); + } + } + + class JRubySemaphore extends Semaphore { + + public JRubySemaphore(int permits) { + super(permits); + } + + public JRubySemaphore(int permits, boolean value) { + super(permits, value); + } + + public void publicReducePermits(int i) { + reducePermits(i); + } + + } + } +} diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java new file mode 100644 index 0000000..bfcc0d0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java @@ -0,0 +1,307 @@ +package com.concurrent_ruby.ext; + +import org.jruby.Ruby; +import org.jruby.RubyBasicObject; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyObject; +import org.jruby.RubyThread; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.Block; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.Visibility; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.runtime.load.Library; +import sun.misc.Unsafe; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class SynchronizationLibrary implements Library { + + private static final Unsafe UNSAFE = loadUnsafe(); + private static final boolean FULL_FENCE = supportsFences(); + + private static Unsafe loadUnsafe() { + try { + Class ncdfe = Class.forName("sun.misc.Unsafe"); + Field f = ncdfe.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (Unsafe) f.get((java.lang.Object) null); + } catch (Exception var2) { + return null; + } catch (NoClassDefFoundError var3) { + return null; + } + } + + private static boolean supportsFences() { + if (UNSAFE == null) { + return false; + } else { + try { + Method m = UNSAFE.getClass().getDeclaredMethod("fullFence", new Class[0]); + if (m != null) { + return true; + } + } catch (Exception var1) { + // nothing + } + + return false; + } + } + + private static final ObjectAllocator JRUBY_OBJECT_ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new JRubyObject(runtime, klazz); + } + }; + + private static final ObjectAllocator OBJECT_ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new Object(runtime, klazz); + } + }; + + private static final ObjectAllocator ABSTRACT_LOCKABLE_OBJECT_ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new AbstractLockableObject(runtime, klazz); + } + }; + + private static final ObjectAllocator JRUBY_LOCKABLE_OBJECT_ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new JRubyLockableObject(runtime, klazz); + } + }; + + public void load(Ruby runtime, boolean wrap) throws IOException { + RubyModule synchronizationModule = runtime. + defineModule("Concurrent"). + defineModuleUnder("Synchronization"); + + RubyModule jrubyAttrVolatileModule = synchronizationModule.defineModuleUnder("JRubyAttrVolatile"); + jrubyAttrVolatileModule.defineAnnotatedMethods(JRubyAttrVolatile.class); + + defineClass(runtime, synchronizationModule, "AbstractObject", "JRubyObject", + JRubyObject.class, JRUBY_OBJECT_ALLOCATOR); + + defineClass(runtime, synchronizationModule, "JRubyObject", "Object", + Object.class, OBJECT_ALLOCATOR); + + defineClass(runtime, synchronizationModule, "Object", "AbstractLockableObject", + AbstractLockableObject.class, ABSTRACT_LOCKABLE_OBJECT_ALLOCATOR); + + defineClass(runtime, synchronizationModule, "AbstractLockableObject", "JRubyLockableObject", + JRubyLockableObject.class, JRUBY_LOCKABLE_OBJECT_ALLOCATOR); + + defineClass(runtime, synchronizationModule, "Object", "JRuby", + JRuby.class, new ObjectAllocator() { + @Override + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new JRuby(runtime, klazz); + } + }); + } + + private RubyClass defineClass( + Ruby runtime, + RubyModule namespace, + String parentName, + String name, + Class javaImplementation, + ObjectAllocator allocator) { + final RubyClass parentClass = namespace.getClass(parentName); + + if (parentClass == null) { + System.out.println("not found " + parentName); + throw runtime.newRuntimeError(namespace.toString() + "::" + parentName + " is missing"); + } + + final RubyClass newClass = namespace.defineClassUnder(name, parentClass, allocator); + newClass.defineAnnotatedMethods(javaImplementation); + return newClass; + } + + // Facts: + // - all ivar reads are without any synchronisation of fences see + // https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/runtime/ivars/VariableAccessor.java#L110-110 + // - writes depend on UnsafeHolder.U, null -> SynchronizedVariableAccessor, !null -> StampedVariableAccessor + // SynchronizedVariableAccessor wraps with synchronized block, StampedVariableAccessor uses fullFence or + // volatilePut + // TODO (pitr 16-Sep-2015): what do we do in Java 9 ? + + // module JRubyAttrVolatile + public static class JRubyAttrVolatile { + + // volatile threadContext is used as a memory barrier per the JVM memory model happens-before semantic + // on volatile fields. any volatile field could have been used but using the thread context is an + // attempt to avoid code elimination. + private static volatile int volatileField; + + @JRubyMethod(name = "full_memory_barrier", visibility = Visibility.PUBLIC) + public static IRubyObject fullMemoryBarrier(ThreadContext context, IRubyObject self) { + // Prevent reordering of ivar writes with publication of this instance + if (!FULL_FENCE) { + // Assuming that following volatile read and write is not eliminated it simulates fullFence. + // If it's eliminated it'll cause problems only on non-x86 platforms. + // http://shipilev.net/blog/2014/jmm-pragmatics/#_happens_before_test_your_understanding + final int volatileRead = volatileField; + volatileField = context.getLine(); + } else { + UNSAFE.fullFence(); + } + return context.nil; + } + + @JRubyMethod(name = "instance_variable_get_volatile", visibility = Visibility.PUBLIC) + public static IRubyObject instanceVariableGetVolatile( + ThreadContext context, + IRubyObject self, + IRubyObject name) { + // Ensure we ses latest value with loadFence + if (!FULL_FENCE) { + // piggybacking on volatile read, simulating loadFence + final int volatileRead = volatileField; + return ((RubyBasicObject) self).instance_variable_get(context, name); + } else { + UNSAFE.loadFence(); + return ((RubyBasicObject) self).instance_variable_get(context, name); + } + } + + @JRubyMethod(name = "instance_variable_set_volatile", visibility = Visibility.PUBLIC) + public static IRubyObject InstanceVariableSetVolatile( + ThreadContext context, + IRubyObject self, + IRubyObject name, + IRubyObject value) { + // Ensure we make last update visible + if (!FULL_FENCE) { + // piggybacking on volatile write, simulating storeFence + final IRubyObject result = ((RubyBasicObject) self).instance_variable_set(name, value); + volatileField = context.getLine(); + return result; + } else { + // JRuby uses StampedVariableAccessor which calls fullFence + // so no additional steps needed. + // See https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/runtime/ivars/StampedVariableAccessor.java#L151-L159 + return ((RubyBasicObject) self).instance_variable_set(name, value); + } + } + } + + @JRubyClass(name = "JRubyObject", parent = "AbstractObject") + public static class JRubyObject extends RubyObject { + + public JRubyObject(Ruby runtime, RubyClass metaClass) { + super(runtime, metaClass); + } + } + + @JRubyClass(name = "Object", parent = "JRubyObject") + public static class Object extends JRubyObject { + + public Object(Ruby runtime, RubyClass metaClass) { + super(runtime, metaClass); + } + } + + @JRubyClass(name = "AbstractLockableObject", parent = "Object") + public static class AbstractLockableObject extends Object { + + public AbstractLockableObject(Ruby runtime, RubyClass metaClass) { + super(runtime, metaClass); + } + } + + @JRubyClass(name = "JRubyLockableObject", parent = "AbstractLockableObject") + public static class JRubyLockableObject extends JRubyObject { + + public JRubyLockableObject(Ruby runtime, RubyClass metaClass) { + super(runtime, metaClass); + } + + @JRubyMethod(name = "synchronize", visibility = Visibility.PROTECTED) + public IRubyObject rubySynchronize(ThreadContext context, Block block) { + synchronized (this) { + return block.yield(context, null); + } + } + + @JRubyMethod(name = "ns_wait", optional = 1, visibility = Visibility.PROTECTED) + public IRubyObject nsWait(ThreadContext context, IRubyObject[] args) { + Ruby runtime = context.runtime; + if (args.length > 1) { + throw runtime.newArgumentError(args.length, 1); + } + Double timeout = null; + if (args.length > 0 && !args[0].isNil()) { + timeout = args[0].convertToFloat().getDoubleValue(); + if (timeout < 0) { + throw runtime.newArgumentError("time interval must be positive"); + } + } + if (Thread.interrupted()) { + throw runtime.newConcurrencyError("thread interrupted"); + } + boolean success = false; + try { + success = context.getThread().wait_timeout(this, timeout); + } catch (InterruptedException ie) { + throw runtime.newConcurrencyError(ie.getLocalizedMessage()); + } finally { + // An interrupt or timeout may have caused us to miss + // a notify that we consumed, so do another notify in + // case someone else is available to pick it up. + if (!success) { + this.notify(); + } + } + return this; + } + + @JRubyMethod(name = "ns_signal", visibility = Visibility.PROTECTED) + public IRubyObject nsSignal(ThreadContext context) { + notify(); + return this; + } + + @JRubyMethod(name = "ns_broadcast", visibility = Visibility.PROTECTED) + public IRubyObject nsBroadcast(ThreadContext context) { + notifyAll(); + return this; + } + } + + @JRubyClass(name = "JRuby") + public static class JRuby extends RubyObject { + public JRuby(Ruby runtime, RubyClass metaClass) { + super(runtime, metaClass); + } + + @JRubyMethod(name = "sleep_interruptibly", visibility = Visibility.PUBLIC, module = true) + public static IRubyObject sleepInterruptibly(final ThreadContext context, IRubyObject receiver, final Block block) { + try { + context.getThread().executeBlockingTask(new RubyThread.BlockingTask() { + @Override + public void run() throws InterruptedException { + block.call(context); + } + + @Override + public void wakeup() { + context.getThread().getNativeThread().interrupt(); + } + }); + } catch (InterruptedException e) { + throw context.runtime.newThreadError("interrupted in Concurrent::Synchronization::JRuby.sleep_interruptibly"); + } + return context.nil; + } + } +} diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java new file mode 100644 index 0000000..e11e15a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java @@ -0,0 +1,31 @@ +package com.concurrent_ruby.ext.jsr166e; + +import java.util.Map; +import java.util.Set; + +public interface ConcurrentHashMap { + /** Interface describing a function of one argument */ + public interface Fun { T apply(A a); } + /** Interface describing a function of two arguments */ + public interface BiFun { T apply(A a, B b); } + + public V get(K key); + public V put(K key, V value); + public V putIfAbsent(K key, V value); + public V computeIfAbsent(K key, Fun mf); + public V computeIfPresent(K key, BiFun mf); + public V compute(K key, BiFun mf); + public V merge(K key, V value, BiFun mf); + public boolean replace(K key, V oldVal, V newVal); + public V replace(K key, V value); + public boolean containsKey(K key); + public boolean remove(Object key, Object value); + public V remove(K key); + public void clear(); + public Set> entrySet(); + public int size(); + public V getValueOrDefault(Object key, V defaultValue); + + public boolean containsValue(V value); + public K findKey(V value); +} diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java new file mode 100644 index 0000000..86aa4eb --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java @@ -0,0 +1,3863 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This is based on the 1.79 version. + +package com.concurrent_ruby.ext.jsr166e; + +import org.jruby.RubyClass; +import org.jruby.RubyNumeric; +import org.jruby.RubyObject; +import org.jruby.exceptions.RaiseException; +import com.concurrent_ruby.ext.jsr166y.ThreadLocalRandom; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.Collection; +import java.util.Hashtable; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Enumeration; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.locks.AbstractQueuedSynchronizer; + +import java.io.Serializable; + +/** + * A hash table supporting full concurrency of retrievals and + * high expected concurrency for updates. This class obeys the + * same functional specification as {@link java.util.Hashtable}, and + * includes versions of methods corresponding to each method of + * {@code Hashtable}. However, even though all operations are + * thread-safe, retrieval operations do not entail locking, + * and there is not any support for locking the entire table + * in a way that prevents all access. This class is fully + * interoperable with {@code Hashtable} in programs that rely on its + * thread safety but not on its synchronization details. + * + *

Retrieval operations (including {@code get}) generally do not + * block, so may overlap with update operations (including {@code put} + * and {@code remove}). Retrievals reflect the results of the most + * recently completed update operations holding upon their + * onset. (More formally, an update operation for a given key bears a + * happens-before relation with any (non-null) retrieval for + * that key reporting the updated value.) For aggregate operations + * such as {@code putAll} and {@code clear}, concurrent retrievals may + * reflect insertion or removal of only some entries. Similarly, + * Iterators and Enumerations return elements reflecting the state of + * the hash table at some point at or since the creation of the + * iterator/enumeration. They do not throw {@link + * ConcurrentModificationException}. However, iterators are designed + * to be used by only one thread at a time. Bear in mind that the + * results of aggregate status methods including {@code size}, {@code + * isEmpty}, and {@code containsValue} are typically useful only when + * a map is not undergoing concurrent updates in other threads. + * Otherwise the results of these methods reflect transient states + * that may be adequate for monitoring or estimation purposes, but not + * for program control. + * + *

The table is dynamically expanded when there are too many + * collisions (i.e., keys that have distinct hash codes but fall into + * the same slot modulo the table size), with the expected average + * effect of maintaining roughly two bins per mapping (corresponding + * to a 0.75 load factor threshold for resizing). There may be much + * variance around this average as mappings are added and removed, but + * overall, this maintains a commonly accepted time/space tradeoff for + * hash tables. However, resizing this or any other kind of hash + * table may be a relatively slow operation. When possible, it is a + * good idea to provide a size estimate as an optional {@code + * initialCapacity} constructor argument. An additional optional + * {@code loadFactor} constructor argument provides a further means of + * customizing initial table capacity by specifying the table density + * to be used in calculating the amount of space to allocate for the + * given number of elements. Also, for compatibility with previous + * versions of this class, constructors may optionally specify an + * expected {@code concurrencyLevel} as an additional hint for + * internal sizing. Note that using many keys with exactly the same + * {@code hashCode()} is a sure way to slow down performance of any + * hash table. + * + *

A {@link Set} projection of a ConcurrentHashMapV8 may be created + * (using {@link #newKeySet()} or {@link #newKeySet(int)}), or viewed + * (using {@link #keySet(Object)} when only keys are of interest, and the + * mapped values are (perhaps transiently) not used or all take the + * same mapping value. + * + *

A ConcurrentHashMapV8 can be used as scalable frequency map (a + * form of histogram or multiset) by using {@link LongAdder} values + * and initializing via {@link #computeIfAbsent}. For example, to add + * a count to a {@code ConcurrentHashMapV8 freqs}, you + * can use {@code freqs.computeIfAbsent(k -> new + * LongAdder()).increment();} + * + *

This class and its views and iterators implement all of the + * optional methods of the {@link Map} and {@link Iterator} + * interfaces. + * + *

Like {@link Hashtable} but unlike {@link HashMap}, this class + * does not allow {@code null} to be used as a key or value. + * + *

ConcurrentHashMapV8s support parallel operations using the {@link + * ForkJoinPool#commonPool}. (Tasks that may be used in other contexts + * are available in class {@link ForkJoinTasks}). These operations are + * designed to be safely, and often sensibly, applied even with maps + * that are being concurrently updated by other threads; for example, + * when computing a snapshot summary of the values in a shared + * registry. There are three kinds of operation, each with four + * forms, accepting functions with Keys, Values, Entries, and (Key, + * Value) arguments and/or return values. (The first three forms are + * also available via the {@link #keySet()}, {@link #values()} and + * {@link #entrySet()} views). Because the elements of a + * ConcurrentHashMapV8 are not ordered in any particular way, and may be + * processed in different orders in different parallel executions, the + * correctness of supplied functions should not depend on any + * ordering, or on any other objects or values that may transiently + * change while computation is in progress; and except for forEach + * actions, should ideally be side-effect-free. + * + *

    + *
  • forEach: Perform a given action on each element. + * A variant form applies a given transformation on each element + * before performing the action.
  • + * + *
  • search: Return the first available non-null result of + * applying a given function on each element; skipping further + * search when a result is found.
  • + * + *
  • reduce: Accumulate each element. The supplied reduction + * function cannot rely on ordering (more formally, it should be + * both associative and commutative). There are five variants: + * + *
      + * + *
    • Plain reductions. (There is not a form of this method for + * (key, value) function arguments since there is no corresponding + * return type.)
    • + * + *
    • Mapped reductions that accumulate the results of a given + * function applied to each element.
    • + * + *
    • Reductions to scalar doubles, longs, and ints, using a + * given basis value.
    • + * + * + *
    + *
+ * + *

The concurrency properties of bulk operations follow + * from those of ConcurrentHashMapV8: Any non-null result returned + * from {@code get(key)} and related access methods bears a + * happens-before relation with the associated insertion or + * update. The result of any bulk operation reflects the + * composition of these per-element relations (but is not + * necessarily atomic with respect to the map as a whole unless it + * is somehow known to be quiescent). Conversely, because keys + * and values in the map are never null, null serves as a reliable + * atomic indicator of the current lack of any result. To + * maintain this property, null serves as an implicit basis for + * all non-scalar reduction operations. For the double, long, and + * int versions, the basis should be one that, when combined with + * any other value, returns that other value (more formally, it + * should be the identity element for the reduction). Most common + * reductions have these properties; for example, computing a sum + * with basis 0 or a minimum with basis MAX_VALUE. + * + *

Search and transformation functions provided as arguments + * should similarly return null to indicate the lack of any result + * (in which case it is not used). In the case of mapped + * reductions, this also enables transformations to serve as + * filters, returning null (or, in the case of primitive + * specializations, the identity basis) if the element should not + * be combined. You can create compound transformations and + * filterings by composing them yourself under this "null means + * there is nothing there now" rule before using them in search or + * reduce operations. + * + *

Methods accepting and/or returning Entry arguments maintain + * key-value associations. They may be useful for example when + * finding the key for the greatest value. Note that "plain" Entry + * arguments can be supplied using {@code new + * AbstractMap.SimpleEntry(k,v)}. + * + *

Bulk operations may complete abruptly, throwing an + * exception encountered in the application of a supplied + * function. Bear in mind when handling such exceptions that other + * concurrently executing functions could also have thrown + * exceptions, or would have done so if the first exception had + * not occurred. + * + *

Parallel speedups for bulk operations compared to sequential + * processing are common but not guaranteed. Operations involving + * brief functions on small maps may execute more slowly than + * sequential loops if the underlying work to parallelize the + * computation is more expensive than the computation itself. + * Similarly, parallelization may not lead to much actual parallelism + * if all processors are busy performing unrelated tasks. + * + *

All arguments to all task methods must be non-null. + * + *

jsr166e note: During transition, this class + * uses nested functional interfaces with different names but the + * same forms as those expected for JDK8. + * + *

This class is a member of the + * + * Java Collections Framework. + * + * @since 1.5 + * @author Doug Lea + * @param the type of keys maintained by this map + * @param the type of mapped values + */ +public class ConcurrentHashMapV8 + implements ConcurrentMap, Serializable, ConcurrentHashMap { + private static final long serialVersionUID = 7249069246763182397L; + + /** + * A partitionable iterator. A Spliterator can be traversed + * directly, but can also be partitioned (before traversal) by + * creating another Spliterator that covers a non-overlapping + * portion of the elements, and so may be amenable to parallel + * execution. + * + *

This interface exports a subset of expected JDK8 + * functionality. + * + *

Sample usage: Here is one (of the several) ways to compute + * the sum of the values held in a map using the ForkJoin + * framework. As illustrated here, Spliterators are well suited to + * designs in which a task repeatedly splits off half its work + * into forked subtasks until small enough to process directly, + * and then joins these subtasks. Variants of this style can also + * be used in completion-based designs. + * + *

+     * {@code ConcurrentHashMapV8 m = ...
+     * // split as if have 8 * parallelism, for load balance
+     * int n = m.size();
+     * int p = aForkJoinPool.getParallelism() * 8;
+     * int split = (n < p)? n : p;
+     * long sum = aForkJoinPool.invoke(new SumValues(m.valueSpliterator(), split, null));
+     * // ...
+     * static class SumValues extends RecursiveTask {
+     *   final Spliterator s;
+     *   final int split;             // split while > 1
+     *   final SumValues nextJoin;    // records forked subtasks to join
+     *   SumValues(Spliterator s, int depth, SumValues nextJoin) {
+     *     this.s = s; this.depth = depth; this.nextJoin = nextJoin;
+     *   }
+     *   public Long compute() {
+     *     long sum = 0;
+     *     SumValues subtasks = null; // fork subtasks
+     *     for (int s = split >>> 1; s > 0; s >>>= 1)
+     *       (subtasks = new SumValues(s.split(), s, subtasks)).fork();
+     *     while (s.hasNext())        // directly process remaining elements
+     *       sum += s.next();
+     *     for (SumValues t = subtasks; t != null; t = t.nextJoin)
+     *       sum += t.join();         // collect subtask results
+     *     return sum;
+     *   }
+     * }
+     * }
+ */ + public static interface Spliterator extends Iterator { + /** + * Returns a Spliterator covering approximately half of the + * elements, guaranteed not to overlap with those subsequently + * returned by this Spliterator. After invoking this method, + * the current Spliterator will not produce any of + * the elements of the returned Spliterator, but the two + * Spliterators together will produce all of the elements that + * would have been produced by this Spliterator had this + * method not been called. The exact number of elements + * produced by the returned Spliterator is not guaranteed, and + * may be zero (i.e., with {@code hasNext()} reporting {@code + * false}) if this Spliterator cannot be further split. + * + * @return a Spliterator covering approximately half of the + * elements + * @throws IllegalStateException if this Spliterator has + * already commenced traversing elements + */ + Spliterator split(); + } + + + /* + * Overview: + * + * The primary design goal of this hash table is to maintain + * concurrent readability (typically method get(), but also + * iterators and related methods) while minimizing update + * contention. Secondary goals are to keep space consumption about + * the same or better than java.util.HashMap, and to support high + * initial insertion rates on an empty table by many threads. + * + * Each key-value mapping is held in a Node. Because Node fields + * can contain special values, they are defined using plain Object + * types. Similarly in turn, all internal methods that use them + * work off Object types. And similarly, so do the internal + * methods of auxiliary iterator and view classes. All public + * generic typed methods relay in/out of these internal methods, + * supplying null-checks and casts as needed. This also allows + * many of the public methods to be factored into a smaller number + * of internal methods (although sadly not so for the five + * variants of put-related operations). The validation-based + * approach explained below leads to a lot of code sprawl because + * retry-control precludes factoring into smaller methods. + * + * The table is lazily initialized to a power-of-two size upon the + * first insertion. Each bin in the table normally contains a + * list of Nodes (most often, the list has only zero or one Node). + * Table accesses require volatile/atomic reads, writes, and + * CASes. Because there is no other way to arrange this without + * adding further indirections, we use intrinsics + * (sun.misc.Unsafe) operations. The lists of nodes within bins + * are always accurately traversable under volatile reads, so long + * as lookups check hash code and non-nullness of value before + * checking key equality. + * + * We use the top two bits of Node hash fields for control + * purposes -- they are available anyway because of addressing + * constraints. As explained further below, these top bits are + * used as follows: + * 00 - Normal + * 01 - Locked + * 11 - Locked and may have a thread waiting for lock + * 10 - Node is a forwarding node + * + * The lower 30 bits of each Node's hash field contain a + * transformation of the key's hash code, except for forwarding + * nodes, for which the lower bits are zero (and so always have + * hash field == MOVED). + * + * Insertion (via put or its variants) of the first node in an + * empty bin is performed by just CASing it to the bin. This is + * by far the most common case for put operations under most + * key/hash distributions. Other update operations (insert, + * delete, and replace) require locks. We do not want to waste + * the space required to associate a distinct lock object with + * each bin, so instead use the first node of a bin list itself as + * a lock. Blocking support for these locks relies on the builtin + * "synchronized" monitors. However, we also need a tryLock + * construction, so we overlay these by using bits of the Node + * hash field for lock control (see above), and so normally use + * builtin monitors only for blocking and signalling using + * wait/notifyAll constructions. See Node.tryAwaitLock. + * + * Using the first node of a list as a lock does not by itself + * suffice though: When a node is locked, any update must first + * validate that it is still the first node after locking it, and + * retry if not. Because new nodes are always appended to lists, + * once a node is first in a bin, it remains first until deleted + * or the bin becomes invalidated (upon resizing). However, + * operations that only conditionally update may inspect nodes + * until the point of update. This is a converse of sorts to the + * lazy locking technique described by Herlihy & Shavit. + * + * The main disadvantage of per-bin locks is that other update + * operations on other nodes in a bin list protected by the same + * lock can stall, for example when user equals() or mapping + * functions take a long time. However, statistically, under + * random hash codes, this is not a common problem. Ideally, the + * frequency of nodes in bins follows a Poisson distribution + * (http://en.wikipedia.org/wiki/Poisson_distribution) with a + * parameter of about 0.5 on average, given the resizing threshold + * of 0.75, although with a large variance because of resizing + * granularity. Ignoring variance, the expected occurrences of + * list size k are (exp(-0.5) * pow(0.5, k) / factorial(k)). The + * first values are: + * + * 0: 0.60653066 + * 1: 0.30326533 + * 2: 0.07581633 + * 3: 0.01263606 + * 4: 0.00157952 + * 5: 0.00015795 + * 6: 0.00001316 + * 7: 0.00000094 + * 8: 0.00000006 + * more: less than 1 in ten million + * + * Lock contention probability for two threads accessing distinct + * elements is roughly 1 / (8 * #elements) under random hashes. + * + * Actual hash code distributions encountered in practice + * sometimes deviate significantly from uniform randomness. This + * includes the case when N > (1<<30), so some keys MUST collide. + * Similarly for dumb or hostile usages in which multiple keys are + * designed to have identical hash codes. Also, although we guard + * against the worst effects of this (see method spread), sets of + * hashes may differ only in bits that do not impact their bin + * index for a given power-of-two mask. So we use a secondary + * strategy that applies when the number of nodes in a bin exceeds + * a threshold, and at least one of the keys implements + * Comparable. These TreeBins use a balanced tree to hold nodes + * (a specialized form of red-black trees), bounding search time + * to O(log N). Each search step in a TreeBin is around twice as + * slow as in a regular list, but given that N cannot exceed + * (1<<64) (before running out of addresses) this bounds search + * steps, lock hold times, etc, to reasonable constants (roughly + * 100 nodes inspected per operation worst case) so long as keys + * are Comparable (which is very common -- String, Long, etc). + * TreeBin nodes (TreeNodes) also maintain the same "next" + * traversal pointers as regular nodes, so can be traversed in + * iterators in the same way. + * + * The table is resized when occupancy exceeds a percentage + * threshold (nominally, 0.75, but see below). Only a single + * thread performs the resize (using field "sizeCtl", to arrange + * exclusion), but the table otherwise remains usable for reads + * and updates. Resizing proceeds by transferring bins, one by + * one, from the table to the next table. Because we are using + * power-of-two expansion, the elements from each bin must either + * stay at same index, or move with a power of two offset. We + * eliminate unnecessary node creation by catching cases where old + * nodes can be reused because their next fields won't change. On + * average, only about one-sixth of them need cloning when a table + * doubles. The nodes they replace will be garbage collectable as + * soon as they are no longer referenced by any reader thread that + * may be in the midst of concurrently traversing table. Upon + * transfer, the old table bin contains only a special forwarding + * node (with hash field "MOVED") that contains the next table as + * its key. On encountering a forwarding node, access and update + * operations restart, using the new table. + * + * Each bin transfer requires its bin lock. However, unlike other + * cases, a transfer can skip a bin if it fails to acquire its + * lock, and revisit it later (unless it is a TreeBin). Method + * rebuild maintains a buffer of TRANSFER_BUFFER_SIZE bins that + * have been skipped because of failure to acquire a lock, and + * blocks only if none are available (i.e., only very rarely). + * The transfer operation must also ensure that all accessible + * bins in both the old and new table are usable by any traversal. + * When there are no lock acquisition failures, this is arranged + * simply by proceeding from the last bin (table.length - 1) up + * towards the first. Upon seeing a forwarding node, traversals + * (see class Iter) arrange to move to the new table + * without revisiting nodes. However, when any node is skipped + * during a transfer, all earlier table bins may have become + * visible, so are initialized with a reverse-forwarding node back + * to the old table until the new ones are established. (This + * sometimes requires transiently locking a forwarding node, which + * is possible under the above encoding.) These more expensive + * mechanics trigger only when necessary. + * + * The traversal scheme also applies to partial traversals of + * ranges of bins (via an alternate Traverser constructor) + * to support partitioned aggregate operations. Also, read-only + * operations give up if ever forwarded to a null table, which + * provides support for shutdown-style clearing, which is also not + * currently implemented. + * + * Lazy table initialization minimizes footprint until first use, + * and also avoids resizings when the first operation is from a + * putAll, constructor with map argument, or deserialization. + * These cases attempt to override the initial capacity settings, + * but harmlessly fail to take effect in cases of races. + * + * The element count is maintained using a LongAdder, which avoids + * contention on updates but can encounter cache thrashing if read + * too frequently during concurrent access. To avoid reading so + * often, resizing is attempted either when a bin lock is + * contended, or upon adding to a bin already holding two or more + * nodes (checked before adding in the xIfAbsent methods, after + * adding in others). Under uniform hash distributions, the + * probability of this occurring at threshold is around 13%, + * meaning that only about 1 in 8 puts check threshold (and after + * resizing, many fewer do so). But this approximation has high + * variance for small table sizes, so we check on any collision + * for sizes <= 64. The bulk putAll operation further reduces + * contention by only committing count updates upon these size + * checks. + * + * Maintaining API and serialization compatibility with previous + * versions of this class introduces several oddities. Mainly: We + * leave untouched but unused constructor arguments refering to + * concurrencyLevel. We accept a loadFactor constructor argument, + * but apply it only to initial table capacity (which is the only + * time that we can guarantee to honor it.) We also declare an + * unused "Segment" class that is instantiated in minimal form + * only when serializing. + */ + + /* ---------------- Constants -------------- */ + + /** + * The largest possible table capacity. This value must be + * exactly 1<<30 to stay within Java array allocation and indexing + * bounds for power of two table sizes, and is further required + * because the top two bits of 32bit hash fields are used for + * control purposes. + */ + private static final int MAXIMUM_CAPACITY = 1 << 30; + + /** + * The default initial table capacity. Must be a power of 2 + * (i.e., at least 1) and at most MAXIMUM_CAPACITY. + */ + private static final int DEFAULT_CAPACITY = 16; + + /** + * The largest possible (non-power of two) array size. + * Needed by toArray and related methods. + */ + static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + + /** + * The default concurrency level for this table. Unused but + * defined for compatibility with previous versions of this class. + */ + private static final int DEFAULT_CONCURRENCY_LEVEL = 16; + + /** + * The load factor for this table. Overrides of this value in + * constructors affect only the initial table capacity. The + * actual floating point value isn't normally used -- it is + * simpler to use expressions such as {@code n - (n >>> 2)} for + * the associated resizing threshold. + */ + private static final float LOAD_FACTOR = 0.75f; + + /** + * The buffer size for skipped bins during transfers. The + * value is arbitrary but should be large enough to avoid + * most locking stalls during resizes. + */ + private static final int TRANSFER_BUFFER_SIZE = 32; + + /** + * The bin count threshold for using a tree rather than list for a + * bin. The value reflects the approximate break-even point for + * using tree-based operations. + * Note that Doug's version defaults to 8, but when dealing with + * Ruby objects it is actually beneficial to avoid TreeNodes + * as long as possible as it usually means going into Ruby land. + */ + private static final int TREE_THRESHOLD = 16; + + /* + * Encodings for special uses of Node hash fields. See above for + * explanation. + */ + static final int MOVED = 0x80000000; // hash field for forwarding nodes + static final int LOCKED = 0x40000000; // set/tested only as a bit + static final int WAITING = 0xc0000000; // both bits set/tested together + static final int HASH_BITS = 0x3fffffff; // usable bits of normal node hash + + /* ---------------- Fields -------------- */ + + /** + * The array of bins. Lazily initialized upon first insertion. + * Size is always a power of two. Accessed directly by iterators. + */ + transient volatile Node[] table; + + /** + * The counter maintaining number of elements. + */ + private transient final LongAdder counter; + + /** + * Table initialization and resizing control. When negative, the + * table is being initialized or resized. Otherwise, when table is + * null, holds the initial table size to use upon creation, or 0 + * for default. After initialization, holds the next element count + * value upon which to resize the table. + */ + private transient volatile int sizeCtl; + + // views + private transient KeySetView keySet; + private transient ValuesView values; + private transient EntrySetView entrySet; + + /** For serialization compatibility. Null unless serialized; see below */ + private Segment[] segments; + + /* ---------------- Table element access -------------- */ + + /* + * Volatile access methods are used for table elements as well as + * elements of in-progress next table while resizing. Uses are + * null checked by callers, and implicitly bounds-checked, relying + * on the invariants that tab arrays have non-zero size, and all + * indices are masked with (tab.length - 1) which is never + * negative and always less than length. Note that, to be correct + * wrt arbitrary concurrency errors by users, bounds checks must + * operate on local variables, which accounts for some odd-looking + * inline assignments below. + */ + + static final Node tabAt(Node[] tab, int i) { // used by Iter + return (Node)UNSAFE.getObjectVolatile(tab, ((long)i< 1 ? 64 : 1; + + /** + * Spins a while if LOCKED bit set and this node is the first + * of its bin, and then sets WAITING bits on hash field and + * blocks (once) if they are still set. It is OK for this + * method to return even if lock is not available upon exit, + * which enables these simple single-wait mechanics. + * + * The corresponding signalling operation is performed within + * callers: Upon detecting that WAITING has been set when + * unlocking lock (via a failed CAS from non-waiting LOCKED + * state), unlockers acquire the sync lock and perform a + * notifyAll. + * + * The initial sanity check on tab and bounds is not currently + * necessary in the only usages of this method, but enables + * use in other future contexts. + */ + final void tryAwaitLock(Node[] tab, int i) { + if (tab != null && i >= 0 && i < tab.length) { // sanity check + int r = ThreadLocalRandom.current().nextInt(); // randomize spins + int spins = MAX_SPINS, h; + while (tabAt(tab, i) == this && ((h = hash) & LOCKED) != 0) { + if (spins >= 0) { + r ^= r << 1; r ^= r >>> 3; r ^= r << 10; // xorshift + if (r >= 0 && --spins == 0) + Thread.yield(); // yield before block + } + else if (casHash(h, h | WAITING)) { + synchronized (this) { + if (tabAt(tab, i) == this && + (hash & WAITING) == WAITING) { + try { + wait(); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + } + else + notifyAll(); // possibly won race vs signaller + } + break; + } + } + } + } + + // Unsafe mechanics for casHash + private static final sun.misc.Unsafe UNSAFE; + private static final long hashOffset; + + static { + try { + UNSAFE = getUnsafe(); + Class k = Node.class; + hashOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("hash")); + } catch (Exception e) { + throw new Error(e); + } + } + } + + /* ---------------- TreeBins -------------- */ + + /** + * Nodes for use in TreeBins + */ + static final class TreeNode extends Node { + TreeNode parent; // red-black tree links + TreeNode left; + TreeNode right; + TreeNode prev; // needed to unlink next upon deletion + boolean red; + + TreeNode(int hash, Object key, Object val, Node next, TreeNode parent) { + super(hash, key, val, next); + this.parent = parent; + } + } + + /** + * A specialized form of red-black tree for use in bins + * whose size exceeds a threshold. + * + * TreeBins use a special form of comparison for search and + * related operations (which is the main reason we cannot use + * existing collections such as TreeMaps). TreeBins contain + * Comparable elements, but may contain others, as well as + * elements that are Comparable but not necessarily Comparable + * for the same T, so we cannot invoke compareTo among them. To + * handle this, the tree is ordered primarily by hash value, then + * by getClass().getName() order, and then by Comparator order + * among elements of the same class. On lookup at a node, if + * elements are not comparable or compare as 0, both left and + * right children may need to be searched in the case of tied hash + * values. (This corresponds to the full list search that would be + * necessary if all elements were non-Comparable and had tied + * hashes.) The red-black balancing code is updated from + * pre-jdk-collections + * (http://gee.cs.oswego.edu/dl/classes/collections/RBCell.java) + * based in turn on Cormen, Leiserson, and Rivest "Introduction to + * Algorithms" (CLR). + * + * TreeBins also maintain a separate locking discipline than + * regular bins. Because they are forwarded via special MOVED + * nodes at bin heads (which can never change once established), + * we cannot use those nodes as locks. Instead, TreeBin + * extends AbstractQueuedSynchronizer to support a simple form of + * read-write lock. For update operations and table validation, + * the exclusive form of lock behaves in the same way as bin-head + * locks. However, lookups use shared read-lock mechanics to allow + * multiple readers in the absence of writers. Additionally, + * these lookups do not ever block: While the lock is not + * available, they proceed along the slow traversal path (via + * next-pointers) until the lock becomes available or the list is + * exhausted, whichever comes first. (These cases are not fast, + * but maximize aggregate expected throughput.) The AQS mechanics + * for doing this are straightforward. The lock state is held as + * AQS getState(). Read counts are negative; the write count (1) + * is positive. There are no signalling preferences among readers + * and writers. Since we don't need to export full Lock API, we + * just override the minimal AQS methods and use them directly. + */ + static final class TreeBin extends AbstractQueuedSynchronizer { + private static final long serialVersionUID = 2249069246763182397L; + transient TreeNode root; // root of tree + transient TreeNode first; // head of next-pointer list + + /* AQS overrides */ + public final boolean isHeldExclusively() { return getState() > 0; } + public final boolean tryAcquire(int ignore) { + if (compareAndSetState(0, 1)) { + setExclusiveOwnerThread(Thread.currentThread()); + return true; + } + return false; + } + public final boolean tryRelease(int ignore) { + setExclusiveOwnerThread(null); + setState(0); + return true; + } + public final int tryAcquireShared(int ignore) { + for (int c;;) { + if ((c = getState()) > 0) + return -1; + if (compareAndSetState(c, c -1)) + return 1; + } + } + public final boolean tryReleaseShared(int ignore) { + int c; + do {} while (!compareAndSetState(c = getState(), c + 1)); + return c == -1; + } + + /** From CLR */ + private void rotateLeft(TreeNode p) { + if (p != null) { + TreeNode r = p.right, pp, rl; + if ((rl = p.right = r.left) != null) + rl.parent = p; + if ((pp = r.parent = p.parent) == null) + root = r; + else if (pp.left == p) + pp.left = r; + else + pp.right = r; + r.left = p; + p.parent = r; + } + } + + /** From CLR */ + private void rotateRight(TreeNode p) { + if (p != null) { + TreeNode l = p.left, pp, lr; + if ((lr = p.left = l.right) != null) + lr.parent = p; + if ((pp = l.parent = p.parent) == null) + root = l; + else if (pp.right == p) + pp.right = l; + else + pp.left = l; + l.right = p; + p.parent = l; + } + } + + @SuppressWarnings("unchecked") final TreeNode getTreeNode + (int h, Object k, TreeNode p) { + return getTreeNode(h, (RubyObject)k, p); + } + + /** + * Returns the TreeNode (or null if not found) for the given key + * starting at given root. + */ + @SuppressWarnings("unchecked") final TreeNode getTreeNode + (int h, RubyObject k, TreeNode p) { + RubyClass c = k.getMetaClass(); boolean kNotComparable = !k.respondsTo("<=>"); + while (p != null) { + int dir, ph; RubyObject pk; RubyClass pc; + if ((ph = p.hash) == h) { + if ((pk = (RubyObject)p.key) == k || k.equals(pk)) + return p; + if (c != (pc = (RubyClass)pk.getMetaClass()) || + kNotComparable || + (dir = rubyCompare(k, pk)) == 0) { + dir = (c == pc) ? 0 : c.getName().compareTo(pc.getName()); + if (dir == 0) { // if still stuck, need to check both sides + TreeNode r = null, pl, pr; + // try to recurse on the right + if ((pr = p.right) != null && h >= pr.hash && (r = getTreeNode(h, k, pr)) != null) + return r; + // try to continue iterating on the left side + else if ((pl = p.left) != null && h <= pl.hash) + dir = -1; + else // no matching node found + return null; + } + } + } + else + dir = (h < ph) ? -1 : 1; + p = (dir > 0) ? p.right : p.left; + } + return null; + } + + int rubyCompare(RubyObject l, RubyObject r) { + ThreadContext context = l.getMetaClass().getRuntime().getCurrentContext(); + IRubyObject result; + try { + result = l.callMethod(context, "<=>", r); + } catch (RaiseException e) { + // handle objects "lying" about responding to <=>, ie: an Array containing non-comparable keys + if (context.runtime.getNoMethodError().isInstance(e.getException())) { + return 0; + } + throw e; + } + + return result.isNil() ? 0 : RubyNumeric.num2int(result.convertToInteger()); + } + + /** + * Wrapper for getTreeNode used by CHM.get. Tries to obtain + * read-lock to call getTreeNode, but during failure to get + * lock, searches along next links. + */ + final Object getValue(int h, Object k) { + Node r = null; + int c = getState(); // Must read lock state first + for (Node e = first; e != null; e = e.next) { + if (c <= 0 && compareAndSetState(c, c - 1)) { + try { + r = getTreeNode(h, k, root); + } finally { + releaseShared(0); + } + break; + } + else if ((e.hash & HASH_BITS) == h && k.equals(e.key)) { + r = e; + break; + } + else + c = getState(); + } + return r == null ? null : r.val; + } + + @SuppressWarnings("unchecked") final TreeNode putTreeNode + (int h, Object k, Object v) { + return putTreeNode(h, (RubyObject)k, v); + } + + /** + * Finds or adds a node. + * @return null if added + */ + @SuppressWarnings("unchecked") final TreeNode putTreeNode + (int h, RubyObject k, Object v) { + RubyClass c = k.getMetaClass(); + boolean kNotComparable = !k.respondsTo("<=>"); + TreeNode pp = root, p = null; + int dir = 0; + while (pp != null) { // find existing node or leaf to insert at + int ph; RubyObject pk; RubyClass pc; + p = pp; + if ((ph = p.hash) == h) { + if ((pk = (RubyObject)p.key) == k || k.equals(pk)) + return p; + if (c != (pc = pk.getMetaClass()) || + kNotComparable || + (dir = rubyCompare(k, pk)) == 0) { + dir = (c == pc) ? 0 : c.getName().compareTo(pc.getName()); + if (dir == 0) { // if still stuck, need to check both sides + TreeNode r = null, pr; + // try to recurse on the right + if ((pr = p.right) != null && h >= pr.hash && (r = getTreeNode(h, k, pr)) != null) + return r; + else // continue descending down the left subtree + dir = -1; + } + } + } + else + dir = (h < ph) ? -1 : 1; + pp = (dir > 0) ? p.right : p.left; + } + + TreeNode f = first; + TreeNode x = first = new TreeNode(h, (Object)k, v, f, p); + if (p == null) + root = x; + else { // attach and rebalance; adapted from CLR + TreeNode xp, xpp; + if (f != null) + f.prev = x; + if (dir <= 0) + p.left = x; + else + p.right = x; + x.red = true; + while (x != null && (xp = x.parent) != null && xp.red && + (xpp = xp.parent) != null) { + TreeNode xppl = xpp.left; + if (xp == xppl) { + TreeNode y = xpp.right; + if (y != null && y.red) { + y.red = false; + xp.red = false; + xpp.red = true; + x = xpp; + } + else { + if (x == xp.right) { + rotateLeft(x = xp); + xpp = (xp = x.parent) == null ? null : xp.parent; + } + if (xp != null) { + xp.red = false; + if (xpp != null) { + xpp.red = true; + rotateRight(xpp); + } + } + } + } + else { + TreeNode y = xppl; + if (y != null && y.red) { + y.red = false; + xp.red = false; + xpp.red = true; + x = xpp; + } + else { + if (x == xp.left) { + rotateRight(x = xp); + xpp = (xp = x.parent) == null ? null : xp.parent; + } + if (xp != null) { + xp.red = false; + if (xpp != null) { + xpp.red = true; + rotateLeft(xpp); + } + } + } + } + } + TreeNode r = root; + if (r != null && r.red) + r.red = false; + } + return null; + } + + /** + * Removes the given node, that must be present before this + * call. This is messier than typical red-black deletion code + * because we cannot swap the contents of an interior node + * with a leaf successor that is pinned by "next" pointers + * that are accessible independently of lock. So instead we + * swap the tree linkages. + */ + final void deleteTreeNode(TreeNode p) { + TreeNode next = (TreeNode)p.next; // unlink traversal pointers + TreeNode pred = p.prev; + if (pred == null) + first = next; + else + pred.next = next; + if (next != null) + next.prev = pred; + TreeNode replacement; + TreeNode pl = p.left; + TreeNode pr = p.right; + if (pl != null && pr != null) { + TreeNode s = pr, sl; + while ((sl = s.left) != null) // find successor + s = sl; + boolean c = s.red; s.red = p.red; p.red = c; // swap colors + TreeNode sr = s.right; + TreeNode pp = p.parent; + if (s == pr) { // p was s's direct parent + p.parent = s; + s.right = p; + } + else { + TreeNode sp = s.parent; + if ((p.parent = sp) != null) { + if (s == sp.left) + sp.left = p; + else + sp.right = p; + } + if ((s.right = pr) != null) + pr.parent = s; + } + p.left = null; + if ((p.right = sr) != null) + sr.parent = p; + if ((s.left = pl) != null) + pl.parent = s; + if ((s.parent = pp) == null) + root = s; + else if (p == pp.left) + pp.left = s; + else + pp.right = s; + replacement = sr; + } + else + replacement = (pl != null) ? pl : pr; + TreeNode pp = p.parent; + if (replacement == null) { + if (pp == null) { + root = null; + return; + } + replacement = p; + } + else { + replacement.parent = pp; + if (pp == null) + root = replacement; + else if (p == pp.left) + pp.left = replacement; + else + pp.right = replacement; + p.left = p.right = p.parent = null; + } + if (!p.red) { // rebalance, from CLR + TreeNode x = replacement; + while (x != null) { + TreeNode xp, xpl; + if (x.red || (xp = x.parent) == null) { + x.red = false; + break; + } + if (x == (xpl = xp.left)) { + TreeNode sib = xp.right; + if (sib != null && sib.red) { + sib.red = false; + xp.red = true; + rotateLeft(xp); + sib = (xp = x.parent) == null ? null : xp.right; + } + if (sib == null) + x = xp; + else { + TreeNode sl = sib.left, sr = sib.right; + if ((sr == null || !sr.red) && + (sl == null || !sl.red)) { + sib.red = true; + x = xp; + } + else { + if (sr == null || !sr.red) { + if (sl != null) + sl.red = false; + sib.red = true; + rotateRight(sib); + sib = (xp = x.parent) == null ? null : xp.right; + } + if (sib != null) { + sib.red = (xp == null) ? false : xp.red; + if ((sr = sib.right) != null) + sr.red = false; + } + if (xp != null) { + xp.red = false; + rotateLeft(xp); + } + x = root; + } + } + } + else { // symmetric + TreeNode sib = xpl; + if (sib != null && sib.red) { + sib.red = false; + xp.red = true; + rotateRight(xp); + sib = (xp = x.parent) == null ? null : xp.left; + } + if (sib == null) + x = xp; + else { + TreeNode sl = sib.left, sr = sib.right; + if ((sl == null || !sl.red) && + (sr == null || !sr.red)) { + sib.red = true; + x = xp; + } + else { + if (sl == null || !sl.red) { + if (sr != null) + sr.red = false; + sib.red = true; + rotateLeft(sib); + sib = (xp = x.parent) == null ? null : xp.left; + } + if (sib != null) { + sib.red = (xp == null) ? false : xp.red; + if ((sl = sib.left) != null) + sl.red = false; + } + if (xp != null) { + xp.red = false; + rotateRight(xp); + } + x = root; + } + } + } + } + } + if (p == replacement && (pp = p.parent) != null) { + if (p == pp.left) // detach pointers + pp.left = null; + else if (p == pp.right) + pp.right = null; + p.parent = null; + } + } + } + + /* ---------------- Collision reduction methods -------------- */ + + /** + * Spreads higher bits to lower, and also forces top 2 bits to 0. + * Because the table uses power-of-two masking, sets of hashes + * that vary only in bits above the current mask will always + * collide. (Among known examples are sets of Float keys holding + * consecutive whole numbers in small tables.) To counter this, + * we apply a transform that spreads the impact of higher bits + * downward. There is a tradeoff between speed, utility, and + * quality of bit-spreading. Because many common sets of hashes + * are already reasonably distributed across bits (so don't benefit + * from spreading), and because we use trees to handle large sets + * of collisions in bins, we don't need excessively high quality. + */ + private static final int spread(int h) { + h ^= (h >>> 18) ^ (h >>> 12); + return (h ^ (h >>> 10)) & HASH_BITS; + } + + /** + * Replaces a list bin with a tree bin. Call only when locked. + * Fails to replace if the given key is non-comparable or table + * is, or needs, resizing. + */ + private final void replaceWithTreeBin(Node[] tab, int index, Object key) { + if ((key instanceof Comparable) && + (tab.length >= MAXIMUM_CAPACITY || counter.sum() < (long)sizeCtl)) { + TreeBin t = new TreeBin(); + for (Node e = tabAt(tab, index); e != null; e = e.next) + t.putTreeNode(e.hash & HASH_BITS, e.key, e.val); + setTabAt(tab, index, new Node(MOVED, t, null, null)); + } + } + + /* ---------------- Internal access and update methods -------------- */ + + /** Implementation for get and containsKey */ + private final Object internalGet(Object k) { + int h = spread(k.hashCode()); + retry: for (Node[] tab = table; tab != null;) { + Node e, p; Object ek, ev; int eh; // locals to read fields once + for (e = tabAt(tab, (tab.length - 1) & h); e != null; e = e.next) { + if ((eh = e.hash) == MOVED) { + if ((ek = e.key) instanceof TreeBin) // search TreeBin + return ((TreeBin)ek).getValue(h, k); + else { // restart with new table + tab = (Node[])ek; + continue retry; + } + } + else if ((eh & HASH_BITS) == h && (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) + return ev; + } + break; + } + return null; + } + + /** + * Implementation for the four public remove/replace methods: + * Replaces node value with v, conditional upon match of cv if + * non-null. If resulting value is null, delete. + */ + private final Object internalReplace(Object k, Object v, Object cv) { + int h = spread(k.hashCode()); + Object oldVal = null; + for (Node[] tab = table;;) { + Node f; int i, fh; Object fk; + if (tab == null || + (f = tabAt(tab, i = (tab.length - 1) & h)) == null) + break; + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + boolean validated = false; + boolean deleted = false; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + validated = true; + TreeNode p = t.getTreeNode(h, k, t.root); + if (p != null) { + Object pv = p.val; + if (cv == null || cv == pv || cv.equals(pv)) { + oldVal = pv; + if ((p.val = v) == null) { + deleted = true; + t.deleteTreeNode(p); + } + } + } + } + } finally { + t.release(0); + } + if (validated) { + if (deleted) + counter.add(-1L); + break; + } + } + else + tab = (Node[])fk; + } + else if ((fh & HASH_BITS) != h && f.next == null) // precheck + break; // rules out possible existence + else if ((fh & LOCKED) != 0) { + checkForResize(); // try resizing if can't get lock + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + boolean validated = false; + boolean deleted = false; + try { + if (tabAt(tab, i) == f) { + validated = true; + for (Node e = f, pred = null;;) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + ((ev = e.val) != null) && + ((ek = e.key) == k || k.equals(ek))) { + if (cv == null || cv == ev || cv.equals(ev)) { + oldVal = ev; + if ((e.val = v) == null) { + deleted = true; + Node en = e.next; + if (pred != null) + pred.next = en; + else + setTabAt(tab, i, en); + } + } + break; + } + pred = e; + if ((e = e.next) == null) + break; + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (validated) { + if (deleted) + counter.add(-1L); + break; + } + } + } + return oldVal; + } + + /* + * Internal versions of the six insertion methods, each a + * little more complicated than the last. All have + * the same basic structure as the first (internalPut): + * 1. If table uninitialized, create + * 2. If bin empty, try to CAS new node + * 3. If bin stale, use new table + * 4. if bin converted to TreeBin, validate and relay to TreeBin methods + * 5. Lock and validate; if valid, scan and add or update + * + * The others interweave other checks and/or alternative actions: + * * Plain put checks for and performs resize after insertion. + * * putIfAbsent prescans for mapping without lock (and fails to add + * if present), which also makes pre-emptive resize checks worthwhile. + * * computeIfAbsent extends form used in putIfAbsent with additional + * mechanics to deal with, calls, potential exceptions and null + * returns from function call. + * * compute uses the same function-call mechanics, but without + * the prescans + * * merge acts as putIfAbsent in the absent case, but invokes the + * update function if present + * * putAll attempts to pre-allocate enough table space + * and more lazily performs count updates and checks. + * + * Someday when details settle down a bit more, it might be worth + * some factoring to reduce sprawl. + */ + + /** Implementation for put */ + private final Object internalPut(Object k, Object v) { + int h = spread(k.hashCode()); + int count = 0; + for (Node[] tab = table;;) { + int i; Node f; int fh; Object fk; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) { + if (casTabAt(tab, i, null, new Node(h, k, v, null))) + break; // no lock when adding to empty bin + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + Object oldVal = null; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + count = 2; + TreeNode p = t.putTreeNode(h, k, v); + if (p != null) { + oldVal = p.val; + p.val = v; + } + } + } finally { + t.release(0); + } + if (count != 0) { + if (oldVal != null) + return oldVal; + break; + } + } + else + tab = (Node[])fk; + } + else if ((fh & LOCKED) != 0) { + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + Object oldVal = null; + try { // needed in case equals() throws + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + oldVal = ev; + e.val = v; + break; + } + Node last = e; + if ((e = e.next) == null) { + last.next = new Node(h, k, v, null); + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + break; + } + } + } + } finally { // unlock and signal if needed + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (oldVal != null) + return oldVal; + if (tab.length <= 64) + count = 2; + break; + } + } + } + counter.add(1L); + if (count > 1) + checkForResize(); + return null; + } + + /** Implementation for putIfAbsent */ + private final Object internalPutIfAbsent(Object k, Object v) { + int h = spread(k.hashCode()); + int count = 0; + for (Node[] tab = table;;) { + int i; Node f; int fh; Object fk, fv; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) { + if (casTabAt(tab, i, null, new Node(h, k, v, null))) + break; + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + Object oldVal = null; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + count = 2; + TreeNode p = t.putTreeNode(h, k, v); + if (p != null) + oldVal = p.val; + } + } finally { + t.release(0); + } + if (count != 0) { + if (oldVal != null) + return oldVal; + break; + } + } + else + tab = (Node[])fk; + } + else if ((fh & HASH_BITS) == h && (fv = f.val) != null && + ((fk = f.key) == k || k.equals(fk))) + return fv; + else { + Node g = f.next; + if (g != null) { // at least 2 nodes -- search and maybe resize + for (Node e = g;;) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) + return ev; + if ((e = e.next) == null) { + checkForResize(); + break; + } + } + } + if (((fh = f.hash) & LOCKED) != 0) { + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (tabAt(tab, i) == f && f.casHash(fh, fh | LOCKED)) { + Object oldVal = null; + try { + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + oldVal = ev; + break; + } + Node last = e; + if ((e = e.next) == null) { + last.next = new Node(h, k, v, null); + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + break; + } + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (oldVal != null) + return oldVal; + if (tab.length <= 64) + count = 2; + break; + } + } + } + } + counter.add(1L); + if (count > 1) + checkForResize(); + return null; + } + + /** Implementation for computeIfAbsent */ + private final Object internalComputeIfAbsent(K k, + Fun mf) { + int h = spread(k.hashCode()); + Object val = null; + int count = 0; + for (Node[] tab = table;;) { + Node f; int i, fh; Object fk, fv; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) { + Node node = new Node(fh = h | LOCKED, k, null, null); + if (casTabAt(tab, i, null, node)) { + count = 1; + try { + if ((val = mf.apply(k)) != null) + node.val = val; + } finally { + if (val == null) + setTabAt(tab, i, null); + if (!node.casHash(fh, h)) { + node.hash = h; + synchronized (node) { node.notifyAll(); }; + } + } + } + if (count != 0) + break; + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + boolean added = false; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + count = 1; + TreeNode p = t.getTreeNode(h, k, t.root); + if (p != null) + val = p.val; + else if ((val = mf.apply(k)) != null) { + added = true; + count = 2; + t.putTreeNode(h, k, val); + } + } + } finally { + t.release(0); + } + if (count != 0) { + if (!added) + return val; + break; + } + } + else + tab = (Node[])fk; + } + else if ((fh & HASH_BITS) == h && (fv = f.val) != null && + ((fk = f.key) == k || k.equals(fk))) + return fv; + else { + Node g = f.next; + if (g != null) { + for (Node e = g;;) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) + return ev; + if ((e = e.next) == null) { + checkForResize(); + break; + } + } + } + if (((fh = f.hash) & LOCKED) != 0) { + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (tabAt(tab, i) == f && f.casHash(fh, fh | LOCKED)) { + boolean added = false; + try { + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + val = ev; + break; + } + Node last = e; + if ((e = e.next) == null) { + if ((val = mf.apply(k)) != null) { + added = true; + last.next = new Node(h, k, val, null); + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + } + break; + } + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (!added) + return val; + if (tab.length <= 64) + count = 2; + break; + } + } + } + } + if (val != null) { + counter.add(1L); + if (count > 1) + checkForResize(); + } + return val; + } + + /** Implementation for compute */ + @SuppressWarnings("unchecked") private final Object internalCompute + (K k, boolean onlyIfPresent, BiFun mf) { + int h = spread(k.hashCode()); + Object val = null; + int delta = 0; + int count = 0; + for (Node[] tab = table;;) { + Node f; int i, fh; Object fk; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) { + if (onlyIfPresent) + break; + Node node = new Node(fh = h | LOCKED, k, null, null); + if (casTabAt(tab, i, null, node)) { + try { + count = 1; + if ((val = mf.apply(k, null)) != null) { + node.val = val; + delta = 1; + } + } finally { + if (delta == 0) + setTabAt(tab, i, null); + if (!node.casHash(fh, h)) { + node.hash = h; + synchronized (node) { node.notifyAll(); }; + } + } + } + if (count != 0) + break; + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + count = 1; + TreeNode p = t.getTreeNode(h, k, t.root); + Object pv; + if (p == null) { + if (onlyIfPresent) + break; + pv = null; + } else + pv = p.val; + if ((val = mf.apply(k, (V)pv)) != null) { + if (p != null) + p.val = val; + else { + count = 2; + delta = 1; + t.putTreeNode(h, k, val); + } + } + else if (p != null) { + delta = -1; + t.deleteTreeNode(p); + } + } + } finally { + t.release(0); + } + if (count != 0) + break; + } + else + tab = (Node[])fk; + } + else if ((fh & LOCKED) != 0) { + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + try { + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f, pred = null;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + val = mf.apply(k, (V)ev); + if (val != null) + e.val = val; + else { + delta = -1; + Node en = e.next; + if (pred != null) + pred.next = en; + else + setTabAt(tab, i, en); + } + break; + } + pred = e; + if ((e = e.next) == null) { + if (!onlyIfPresent && (val = mf.apply(k, null)) != null) { + pred.next = new Node(h, k, val, null); + delta = 1; + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + } + break; + } + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (tab.length <= 64) + count = 2; + break; + } + } + } + if (delta != 0) { + counter.add((long)delta); + if (count > 1) + checkForResize(); + } + return val; + } + + /** Implementation for merge */ + @SuppressWarnings("unchecked") private final Object internalMerge + (K k, V v, BiFun mf) { + int h = spread(k.hashCode()); + Object val = null; + int delta = 0; + int count = 0; + for (Node[] tab = table;;) { + int i; Node f; int fh; Object fk, fv; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) { + if (casTabAt(tab, i, null, new Node(h, k, v, null))) { + delta = 1; + val = v; + break; + } + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + count = 1; + TreeNode p = t.getTreeNode(h, k, t.root); + val = (p == null) ? v : mf.apply((V)p.val, v); + if (val != null) { + if (p != null) + p.val = val; + else { + count = 2; + delta = 1; + t.putTreeNode(h, k, val); + } + } + else if (p != null) { + delta = -1; + t.deleteTreeNode(p); + } + } + } finally { + t.release(0); + } + if (count != 0) + break; + } + else + tab = (Node[])fk; + } + else if ((fh & LOCKED) != 0) { + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + try { + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f, pred = null;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + val = mf.apply((V)ev, v); + if (val != null) + e.val = val; + else { + delta = -1; + Node en = e.next; + if (pred != null) + pred.next = en; + else + setTabAt(tab, i, en); + } + break; + } + pred = e; + if ((e = e.next) == null) { + val = v; + pred.next = new Node(h, k, val, null); + delta = 1; + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + break; + } + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (tab.length <= 64) + count = 2; + break; + } + } + } + if (delta != 0) { + counter.add((long)delta); + if (count > 1) + checkForResize(); + } + return val; + } + + /** Implementation for putAll */ + private final void internalPutAll(Map m) { + tryPresize(m.size()); + long delta = 0L; // number of uncommitted additions + boolean npe = false; // to throw exception on exit for nulls + try { // to clean up counts on other exceptions + for (Map.Entry entry : m.entrySet()) { + Object k, v; + if (entry == null || (k = entry.getKey()) == null || + (v = entry.getValue()) == null) { + npe = true; + break; + } + int h = spread(k.hashCode()); + for (Node[] tab = table;;) { + int i; Node f; int fh; Object fk; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null){ + if (casTabAt(tab, i, null, new Node(h, k, v, null))) { + ++delta; + break; + } + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + boolean validated = false; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + validated = true; + TreeNode p = t.getTreeNode(h, k, t.root); + if (p != null) + p.val = v; + else { + t.putTreeNode(h, k, v); + ++delta; + } + } + } finally { + t.release(0); + } + if (validated) + break; + } + else + tab = (Node[])fk; + } + else if ((fh & LOCKED) != 0) { + counter.add(delta); + delta = 0L; + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + int count = 0; + try { + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + e.val = v; + break; + } + Node last = e; + if ((e = e.next) == null) { + ++delta; + last.next = new Node(h, k, v, null); + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + break; + } + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (count > 1) { + counter.add(delta); + delta = 0L; + checkForResize(); + } + break; + } + } + } + } + } finally { + if (delta != 0) + counter.add(delta); + } + if (npe) + throw new NullPointerException(); + } + + /* ---------------- Table Initialization and Resizing -------------- */ + + /** + * Returns a power of two table size for the given desired capacity. + * See Hackers Delight, sec 3.2 + */ + private static final int tableSizeFor(int c) { + int n = c - 1; + n |= n >>> 1; + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; + } + + /** + * Initializes table, using the size recorded in sizeCtl. + */ + private final Node[] initTable() { + Node[] tab; int sc; + while ((tab = table) == null) { + if ((sc = sizeCtl) < 0) + Thread.yield(); // lost initialization race; just spin + else if (UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) { + try { + if ((tab = table) == null) { + int n = (sc > 0) ? sc : DEFAULT_CAPACITY; + tab = table = new Node[n]; + sc = n - (n >>> 2); + } + } finally { + sizeCtl = sc; + } + break; + } + } + return tab; + } + + /** + * If table is too small and not already resizing, creates next + * table and transfers bins. Rechecks occupancy after a transfer + * to see if another resize is already needed because resizings + * are lagging additions. + */ + private final void checkForResize() { + Node[] tab; int n, sc; + while ((tab = table) != null && + (n = tab.length) < MAXIMUM_CAPACITY && + (sc = sizeCtl) >= 0 && counter.sum() >= (long)sc && + UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) { + try { + if (tab == table) { + table = rebuild(tab); + sc = (n << 1) - (n >>> 1); + } + } finally { + sizeCtl = sc; + } + } + } + + /** + * Tries to presize table to accommodate the given number of elements. + * + * @param size number of elements (doesn't need to be perfectly accurate) + */ + private final void tryPresize(int size) { + int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY : + tableSizeFor(size + (size >>> 1) + 1); + int sc; + while ((sc = sizeCtl) >= 0) { + Node[] tab = table; int n; + if (tab == null || (n = tab.length) == 0) { + n = (sc > c) ? sc : c; + if (UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) { + try { + if (table == tab) { + table = new Node[n]; + sc = n - (n >>> 2); + } + } finally { + sizeCtl = sc; + } + } + } + else if (c <= sc || n >= MAXIMUM_CAPACITY) + break; + else if (UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) { + try { + if (table == tab) { + table = rebuild(tab); + sc = (n << 1) - (n >>> 1); + } + } finally { + sizeCtl = sc; + } + } + } + } + + /* + * Moves and/or copies the nodes in each bin to new table. See + * above for explanation. + * + * @return the new table + */ + private static final Node[] rebuild(Node[] tab) { + int n = tab.length; + Node[] nextTab = new Node[n << 1]; + Node fwd = new Node(MOVED, nextTab, null, null); + int[] buffer = null; // holds bins to revisit; null until needed + Node rev = null; // reverse forwarder; null until needed + int nbuffered = 0; // the number of bins in buffer list + int bufferIndex = 0; // buffer index of current buffered bin + int bin = n - 1; // current non-buffered bin or -1 if none + + for (int i = bin;;) { // start upwards sweep + int fh; Node f; + if ((f = tabAt(tab, i)) == null) { + if (bin >= 0) { // Unbuffered; no lock needed (or available) + if (!casTabAt(tab, i, f, fwd)) + continue; + } + else { // transiently use a locked forwarding node + Node g = new Node(MOVED|LOCKED, nextTab, null, null); + if (!casTabAt(tab, i, f, g)) + continue; + setTabAt(nextTab, i, null); + setTabAt(nextTab, i + n, null); + setTabAt(tab, i, fwd); + if (!g.casHash(MOVED|LOCKED, MOVED)) { + g.hash = MOVED; + synchronized (g) { g.notifyAll(); } + } + } + } + else if ((fh = f.hash) == MOVED) { + Object fk = f.key; + if (fk instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + boolean validated = false; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + validated = true; + splitTreeBin(nextTab, i, t); + setTabAt(tab, i, fwd); + } + } finally { + t.release(0); + } + if (!validated) + continue; + } + } + else if ((fh & LOCKED) == 0 && f.casHash(fh, fh|LOCKED)) { + boolean validated = false; + try { // split to lo and hi lists; copying as needed + if (tabAt(tab, i) == f) { + validated = true; + splitBin(nextTab, i, f); + setTabAt(tab, i, fwd); + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (!validated) + continue; + } + else { + if (buffer == null) // initialize buffer for revisits + buffer = new int[TRANSFER_BUFFER_SIZE]; + if (bin < 0 && bufferIndex > 0) { + int j = buffer[--bufferIndex]; + buffer[bufferIndex] = i; + i = j; // swap with another bin + continue; + } + if (bin < 0 || nbuffered >= TRANSFER_BUFFER_SIZE) { + f.tryAwaitLock(tab, i); + continue; // no other options -- block + } + if (rev == null) // initialize reverse-forwarder + rev = new Node(MOVED, tab, null, null); + if (tabAt(tab, i) != f || (f.hash & LOCKED) == 0) + continue; // recheck before adding to list + buffer[nbuffered++] = i; + setTabAt(nextTab, i, rev); // install place-holders + setTabAt(nextTab, i + n, rev); + } + + if (bin > 0) + i = --bin; + else if (buffer != null && nbuffered > 0) { + bin = -1; + i = buffer[bufferIndex = --nbuffered]; + } + else + return nextTab; + } + } + + /** + * Splits a normal bin with list headed by e into lo and hi parts; + * installs in given table. + */ + private static void splitBin(Node[] nextTab, int i, Node e) { + int bit = nextTab.length >>> 1; // bit to split on + int runBit = e.hash & bit; + Node lastRun = e, lo = null, hi = null; + for (Node p = e.next; p != null; p = p.next) { + int b = p.hash & bit; + if (b != runBit) { + runBit = b; + lastRun = p; + } + } + if (runBit == 0) + lo = lastRun; + else + hi = lastRun; + for (Node p = e; p != lastRun; p = p.next) { + int ph = p.hash & HASH_BITS; + Object pk = p.key, pv = p.val; + if ((ph & bit) == 0) + lo = new Node(ph, pk, pv, lo); + else + hi = new Node(ph, pk, pv, hi); + } + setTabAt(nextTab, i, lo); + setTabAt(nextTab, i + bit, hi); + } + + /** + * Splits a tree bin into lo and hi parts; installs in given table. + */ + private static void splitTreeBin(Node[] nextTab, int i, TreeBin t) { + int bit = nextTab.length >>> 1; + TreeBin lt = new TreeBin(); + TreeBin ht = new TreeBin(); + int lc = 0, hc = 0; + for (Node e = t.first; e != null; e = e.next) { + int h = e.hash & HASH_BITS; + Object k = e.key, v = e.val; + if ((h & bit) == 0) { + ++lc; + lt.putTreeNode(h, k, v); + } + else { + ++hc; + ht.putTreeNode(h, k, v); + } + } + Node ln, hn; // throw away trees if too small + if (lc <= (TREE_THRESHOLD >>> 1)) { + ln = null; + for (Node p = lt.first; p != null; p = p.next) + ln = new Node(p.hash, p.key, p.val, ln); + } + else + ln = new Node(MOVED, lt, null, null); + setTabAt(nextTab, i, ln); + if (hc <= (TREE_THRESHOLD >>> 1)) { + hn = null; + for (Node p = ht.first; p != null; p = p.next) + hn = new Node(p.hash, p.key, p.val, hn); + } + else + hn = new Node(MOVED, ht, null, null); + setTabAt(nextTab, i + bit, hn); + } + + /** + * Implementation for clear. Steps through each bin, removing all + * nodes. + */ + private final void internalClear() { + long delta = 0L; // negative number of deletions + int i = 0; + Node[] tab = table; + while (tab != null && i < tab.length) { + int fh; Object fk; + Node f = tabAt(tab, i); + if (f == null) + ++i; + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + for (Node p = t.first; p != null; p = p.next) { + if (p.val != null) { // (currently always true) + p.val = null; + --delta; + } + } + t.first = null; + t.root = null; + ++i; + } + } finally { + t.release(0); + } + } + else + tab = (Node[])fk; + } + else if ((fh & LOCKED) != 0) { + counter.add(delta); // opportunistically update count + delta = 0L; + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + try { + if (tabAt(tab, i) == f) { + for (Node e = f; e != null; e = e.next) { + if (e.val != null) { // (currently always true) + e.val = null; + --delta; + } + } + setTabAt(tab, i, null); + ++i; + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + } + } + if (delta != 0) + counter.add(delta); + } + + /* ----------------Table Traversal -------------- */ + + /** + * Encapsulates traversal for methods such as containsValue; also + * serves as a base class for other iterators and bulk tasks. + * + * At each step, the iterator snapshots the key ("nextKey") and + * value ("nextVal") of a valid node (i.e., one that, at point of + * snapshot, has a non-null user value). Because val fields can + * change (including to null, indicating deletion), field nextVal + * might not be accurate at point of use, but still maintains the + * weak consistency property of holding a value that was once + * valid. To support iterator.remove, the nextKey field is not + * updated (nulled out) when the iterator cannot advance. + * + * Internal traversals directly access these fields, as in: + * {@code while (it.advance() != null) { process(it.nextKey); }} + * + * Exported iterators must track whether the iterator has advanced + * (in hasNext vs next) (by setting/checking/nulling field + * nextVal), and then extract key, value, or key-value pairs as + * return values of next(). + * + * The iterator visits once each still-valid node that was + * reachable upon iterator construction. It might miss some that + * were added to a bin after the bin was visited, which is OK wrt + * consistency guarantees. Maintaining this property in the face + * of possible ongoing resizes requires a fair amount of + * bookkeeping state that is difficult to optimize away amidst + * volatile accesses. Even so, traversal maintains reasonable + * throughput. + * + * Normally, iteration proceeds bin-by-bin traversing lists. + * However, if the table has been resized, then all future steps + * must traverse both the bin at the current index as well as at + * (index + baseSize); and so on for further resizings. To + * paranoically cope with potential sharing by users of iterators + * across threads, iteration terminates if a bounds checks fails + * for a table read. + * + * This class extends ForkJoinTask to streamline parallel + * iteration in bulk operations (see BulkTask). This adds only an + * int of space overhead, which is close enough to negligible in + * cases where it is not needed to not worry about it. Because + * ForkJoinTask is Serializable, but iterators need not be, we + * need to add warning suppressions. + */ + @SuppressWarnings("serial") static class Traverser { + final ConcurrentHashMapV8 map; + Node next; // the next entry to use + K nextKey; // cached key field of next + V nextVal; // cached val field of next + Node[] tab; // current table; updated if resized + int index; // index of bin to use next + int baseIndex; // current index of initial table + int baseLimit; // index bound for initial table + int baseSize; // initial table size + + /** Creates iterator for all entries in the table. */ + Traverser(ConcurrentHashMapV8 map) { + this.map = map; + } + + /** Creates iterator for split() methods */ + Traverser(Traverser it) { + ConcurrentHashMapV8 m; Node[] t; + if ((m = this.map = it.map) == null) + t = null; + else if ((t = it.tab) == null && // force parent tab initialization + (t = it.tab = m.table) != null) + it.baseLimit = it.baseSize = t.length; + this.tab = t; + this.baseSize = it.baseSize; + it.baseLimit = this.index = this.baseIndex = + ((this.baseLimit = it.baseLimit) + it.baseIndex + 1) >>> 1; + } + + /** + * Advances next; returns nextVal or null if terminated. + * See above for explanation. + */ + final V advance() { + Node e = next; + V ev = null; + outer: do { + if (e != null) // advance past used/skipped node + e = e.next; + while (e == null) { // get to next non-null bin + ConcurrentHashMapV8 m; + Node[] t; int b, i, n; Object ek; // checks must use locals + if ((t = tab) != null) + n = t.length; + else if ((m = map) != null && (t = tab = m.table) != null) + n = baseLimit = baseSize = t.length; + else + break outer; + if ((b = baseIndex) >= baseLimit || + (i = index) < 0 || i >= n) + break outer; + if ((e = tabAt(t, i)) != null && e.hash == MOVED) { + if ((ek = e.key) instanceof TreeBin) + e = ((TreeBin)ek).first; + else { + tab = (Node[])ek; + continue; // restarts due to null val + } + } // visit upper slots if present + index = (i += baseSize) < n ? i : (baseIndex = b + 1); + } + nextKey = (K) e.key; + } while ((ev = (V) e.val) == null); // skip deleted or special nodes + next = e; + return nextVal = ev; + } + + public final void remove() { + Object k = nextKey; + if (k == null && (advance() == null || (k = nextKey) == null)) + throw new IllegalStateException(); + map.internalReplace(k, null, null); + } + + public final boolean hasNext() { + return nextVal != null || advance() != null; + } + + public final boolean hasMoreElements() { return hasNext(); } + public final void setRawResult(Object x) { } + public R getRawResult() { return null; } + public boolean exec() { return true; } + } + + /* ---------------- Public operations -------------- */ + + /** + * Creates a new, empty map with the default initial table size (16). + */ + public ConcurrentHashMapV8() { + this.counter = new LongAdder(); + } + + /** + * Creates a new, empty map with an initial table size + * accommodating the specified number of elements without the need + * to dynamically resize. + * + * @param initialCapacity The implementation performs internal + * sizing to accommodate this many elements. + * @throws IllegalArgumentException if the initial capacity of + * elements is negative + */ + public ConcurrentHashMapV8(int initialCapacity) { + if (initialCapacity < 0) + throw new IllegalArgumentException(); + int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ? + MAXIMUM_CAPACITY : + tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1)); + this.counter = new LongAdder(); + this.sizeCtl = cap; + } + + /** + * Creates a new map with the same mappings as the given map. + * + * @param m the map + */ + public ConcurrentHashMapV8(Map m) { + this.counter = new LongAdder(); + this.sizeCtl = DEFAULT_CAPACITY; + internalPutAll(m); + } + + /** + * Creates a new, empty map with an initial table size based on + * the given number of elements ({@code initialCapacity}) and + * initial table density ({@code loadFactor}). + * + * @param initialCapacity the initial capacity. The implementation + * performs internal sizing to accommodate this many elements, + * given the specified load factor. + * @param loadFactor the load factor (table density) for + * establishing the initial table size + * @throws IllegalArgumentException if the initial capacity of + * elements is negative or the load factor is nonpositive + * + * @since 1.6 + */ + public ConcurrentHashMapV8(int initialCapacity, float loadFactor) { + this(initialCapacity, loadFactor, 1); + } + + /** + * Creates a new, empty map with an initial table size based on + * the given number of elements ({@code initialCapacity}), table + * density ({@code loadFactor}), and number of concurrently + * updating threads ({@code concurrencyLevel}). + * + * @param initialCapacity the initial capacity. The implementation + * performs internal sizing to accommodate this many elements, + * given the specified load factor. + * @param loadFactor the load factor (table density) for + * establishing the initial table size + * @param concurrencyLevel the estimated number of concurrently + * updating threads. The implementation may use this value as + * a sizing hint. + * @throws IllegalArgumentException if the initial capacity is + * negative or the load factor or concurrencyLevel are + * nonpositive + */ + public ConcurrentHashMapV8(int initialCapacity, + float loadFactor, int concurrencyLevel) { + if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0) + throw new IllegalArgumentException(); + if (initialCapacity < concurrencyLevel) // Use at least as many bins + initialCapacity = concurrencyLevel; // as estimated threads + long size = (long)(1.0 + (long)initialCapacity / loadFactor); + int cap = (size >= (long)MAXIMUM_CAPACITY) ? + MAXIMUM_CAPACITY : tableSizeFor((int)size); + this.counter = new LongAdder(); + this.sizeCtl = cap; + } + + /** + * Creates a new {@link Set} backed by a ConcurrentHashMapV8 + * from the given type to {@code Boolean.TRUE}. + * + * @return the new set + */ + public static KeySetView newKeySet() { + return new KeySetView(new ConcurrentHashMapV8(), + Boolean.TRUE); + } + + /** + * Creates a new {@link Set} backed by a ConcurrentHashMapV8 + * from the given type to {@code Boolean.TRUE}. + * + * @param initialCapacity The implementation performs internal + * sizing to accommodate this many elements. + * @throws IllegalArgumentException if the initial capacity of + * elements is negative + * @return the new set + */ + public static KeySetView newKeySet(int initialCapacity) { + return new KeySetView(new ConcurrentHashMapV8(initialCapacity), + Boolean.TRUE); + } + + /** + * {@inheritDoc} + */ + public boolean isEmpty() { + return counter.sum() <= 0L; // ignore transient negative values + } + + /** + * {@inheritDoc} + */ + public int size() { + long n = counter.sum(); + return ((n < 0L) ? 0 : + (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE : + (int)n); + } + + /** + * Returns the number of mappings. This method should be used + * instead of {@link #size} because a ConcurrentHashMapV8 may + * contain more mappings than can be represented as an int. The + * value returned is a snapshot; the actual count may differ if + * there are ongoing concurrent insertions or removals. + * + * @return the number of mappings + */ + public long mappingCount() { + long n = counter.sum(); + return (n < 0L) ? 0L : n; // ignore transient negative values + } + + /** + * Returns the value to which the specified key is mapped, + * or {@code null} if this map contains no mapping for the key. + * + *

More formally, if this map contains a mapping from a key + * {@code k} to a value {@code v} such that {@code key.equals(k)}, + * then this method returns {@code v}; otherwise it returns + * {@code null}. (There can be at most one such mapping.) + * + * @throws NullPointerException if the specified key is null + */ + @SuppressWarnings("unchecked") public V get(Object key) { + if (key == null) + throw new NullPointerException(); + return (V)internalGet(key); + } + + /** + * Returns the value to which the specified key is mapped, + * or the given defaultValue if this map contains no mapping for the key. + * + * @param key the key + * @param defaultValue the value to return if this map contains + * no mapping for the given key + * @return the mapping for the key, if present; else the defaultValue + * @throws NullPointerException if the specified key is null + */ + @SuppressWarnings("unchecked") public V getValueOrDefault(Object key, V defaultValue) { + if (key == null) + throw new NullPointerException(); + V v = (V) internalGet(key); + return v == null ? defaultValue : v; + } + + /** + * Tests if the specified object is a key in this table. + * + * @param key possible key + * @return {@code true} if and only if the specified object + * is a key in this table, as determined by the + * {@code equals} method; {@code false} otherwise + * @throws NullPointerException if the specified key is null + */ + public boolean containsKey(Object key) { + if (key == null) + throw new NullPointerException(); + return internalGet(key) != null; + } + + /** + * Returns {@code true} if this map maps one or more keys to the + * specified value. Note: This method may require a full traversal + * of the map, and is much slower than method {@code containsKey}. + * + * @param value value whose presence in this map is to be tested + * @return {@code true} if this map maps one or more keys to the + * specified value + * @throws NullPointerException if the specified value is null + */ + public boolean containsValue(Object value) { + if (value == null) + throw new NullPointerException(); + Object v; + Traverser it = new Traverser(this); + while ((v = it.advance()) != null) { + if (v == value || value.equals(v)) + return true; + } + return false; + } + + public K findKey(Object value) { + if (value == null) + throw new NullPointerException(); + Object v; + Traverser it = new Traverser(this); + while ((v = it.advance()) != null) { + if (v == value || value.equals(v)) + return it.nextKey; + } + return null; + } + + /** + * Legacy method testing if some key maps into the specified value + * in this table. This method is identical in functionality to + * {@link #containsValue}, and exists solely to ensure + * full compatibility with class {@link java.util.Hashtable}, + * which supported this method prior to introduction of the + * Java Collections framework. + * + * @param value a value to search for + * @return {@code true} if and only if some key maps to the + * {@code value} argument in this table as + * determined by the {@code equals} method; + * {@code false} otherwise + * @throws NullPointerException if the specified value is null + */ + public boolean contains(Object value) { + return containsValue(value); + } + + /** + * Maps the specified key to the specified value in this table. + * Neither the key nor the value can be null. + * + *

The value can be retrieved by calling the {@code get} method + * with a key that is equal to the original key. + * + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @return the previous value associated with {@code key}, or + * {@code null} if there was no mapping for {@code key} + * @throws NullPointerException if the specified key or value is null + */ + @SuppressWarnings("unchecked") public V put(K key, V value) { + if (key == null || value == null) + throw new NullPointerException(); + return (V)internalPut(key, value); + } + + /** + * {@inheritDoc} + * + * @return the previous value associated with the specified key, + * or {@code null} if there was no mapping for the key + * @throws NullPointerException if the specified key or value is null + */ + @SuppressWarnings("unchecked") public V putIfAbsent(K key, V value) { + if (key == null || value == null) + throw new NullPointerException(); + return (V)internalPutIfAbsent(key, value); + } + + /** + * Copies all of the mappings from the specified map to this one. + * These mappings replace any mappings that this map had for any of the + * keys currently in the specified map. + * + * @param m mappings to be stored in this map + */ + public void putAll(Map m) { + internalPutAll(m); + } + + /** + * If the specified key is not already associated with a value, + * computes its value using the given mappingFunction and enters + * it into the map unless null. This is equivalent to + *

 {@code
+     * if (map.containsKey(key))
+     *   return map.get(key);
+     * value = mappingFunction.apply(key);
+     * if (value != null)
+     *   map.put(key, value);
+     * return value;}
+ * + * except that the action is performed atomically. If the + * function returns {@code null} no mapping is recorded. If the + * function itself throws an (unchecked) exception, the exception + * is rethrown to its caller, and no mapping is recorded. Some + * attempted update operations on this map by other threads may be + * blocked while computation is in progress, so the computation + * should be short and simple, and must not attempt to update any + * other mappings of this Map. The most appropriate usage is to + * construct a new object serving as an initial mapped value, or + * memoized result, as in: + * + *
 {@code
+     * map.computeIfAbsent(key, new Fun() {
+     *   public V map(K k) { return new Value(f(k)); }});}
+ * + * @param key key with which the specified value is to be associated + * @param mappingFunction the function to compute a value + * @return the current (existing or computed) value associated with + * the specified key, or null if the computed value is null + * @throws NullPointerException if the specified key or mappingFunction + * is null + * @throws IllegalStateException if the computation detectably + * attempts a recursive update to this map that would + * otherwise never complete + * @throws RuntimeException or Error if the mappingFunction does so, + * in which case the mapping is left unestablished + */ + @SuppressWarnings("unchecked") public V computeIfAbsent + (K key, Fun mappingFunction) { + if (key == null || mappingFunction == null) + throw new NullPointerException(); + return (V)internalComputeIfAbsent(key, mappingFunction); + } + + /** + * If the given key is present, computes a new mapping value given a key and + * its current mapped value. This is equivalent to + *
 {@code
+     *   if (map.containsKey(key)) {
+     *     value = remappingFunction.apply(key, map.get(key));
+     *     if (value != null)
+     *       map.put(key, value);
+     *     else
+     *       map.remove(key);
+     *   }
+     * }
+ * + * except that the action is performed atomically. If the + * function returns {@code null}, the mapping is removed. If the + * function itself throws an (unchecked) exception, the exception + * is rethrown to its caller, and the current mapping is left + * unchanged. Some attempted update operations on this map by + * other threads may be blocked while computation is in progress, + * so the computation should be short and simple, and must not + * attempt to update any other mappings of this Map. For example, + * to either create or append new messages to a value mapping: + * + * @param key key with which the specified value is to be associated + * @param remappingFunction the function to compute a value + * @return the new value associated with the specified key, or null if none + * @throws NullPointerException if the specified key or remappingFunction + * is null + * @throws IllegalStateException if the computation detectably + * attempts a recursive update to this map that would + * otherwise never complete + * @throws RuntimeException or Error if the remappingFunction does so, + * in which case the mapping is unchanged + */ + @SuppressWarnings("unchecked") public V computeIfPresent + (K key, BiFun remappingFunction) { + if (key == null || remappingFunction == null) + throw new NullPointerException(); + return (V)internalCompute(key, true, remappingFunction); + } + + /** + * Computes a new mapping value given a key and + * its current mapped value (or {@code null} if there is no current + * mapping). This is equivalent to + *
 {@code
+     *   value = remappingFunction.apply(key, map.get(key));
+     *   if (value != null)
+     *     map.put(key, value);
+     *   else
+     *     map.remove(key);
+     * }
+ * + * except that the action is performed atomically. If the + * function returns {@code null}, the mapping is removed. If the + * function itself throws an (unchecked) exception, the exception + * is rethrown to its caller, and the current mapping is left + * unchanged. Some attempted update operations on this map by + * other threads may be blocked while computation is in progress, + * so the computation should be short and simple, and must not + * attempt to update any other mappings of this Map. For example, + * to either create or append new messages to a value mapping: + * + *
 {@code
+     * Map map = ...;
+     * final String msg = ...;
+     * map.compute(key, new BiFun() {
+     *   public String apply(Key k, String v) {
+     *    return (v == null) ? msg : v + msg;});}}
+ * + * @param key key with which the specified value is to be associated + * @param remappingFunction the function to compute a value + * @return the new value associated with the specified key, or null if none + * @throws NullPointerException if the specified key or remappingFunction + * is null + * @throws IllegalStateException if the computation detectably + * attempts a recursive update to this map that would + * otherwise never complete + * @throws RuntimeException or Error if the remappingFunction does so, + * in which case the mapping is unchanged + */ + @SuppressWarnings("unchecked") public V compute + (K key, BiFun remappingFunction) { + if (key == null || remappingFunction == null) + throw new NullPointerException(); + return (V)internalCompute(key, false, remappingFunction); + } + + /** + * If the specified key is not already associated + * with a value, associate it with the given value. + * Otherwise, replace the value with the results of + * the given remapping function. This is equivalent to: + *
 {@code
+     *   if (!map.containsKey(key))
+     *     map.put(value);
+     *   else {
+     *     newValue = remappingFunction.apply(map.get(key), value);
+     *     if (value != null)
+     *       map.put(key, value);
+     *     else
+     *       map.remove(key);
+     *   }
+     * }
+ * except that the action is performed atomically. If the + * function returns {@code null}, the mapping is removed. If the + * function itself throws an (unchecked) exception, the exception + * is rethrown to its caller, and the current mapping is left + * unchanged. Some attempted update operations on this map by + * other threads may be blocked while computation is in progress, + * so the computation should be short and simple, and must not + * attempt to update any other mappings of this Map. + */ + @SuppressWarnings("unchecked") public V merge + (K key, V value, BiFun remappingFunction) { + if (key == null || value == null || remappingFunction == null) + throw new NullPointerException(); + return (V)internalMerge(key, value, remappingFunction); + } + + /** + * Removes the key (and its corresponding value) from this map. + * This method does nothing if the key is not in the map. + * + * @param key the key that needs to be removed + * @return the previous value associated with {@code key}, or + * {@code null} if there was no mapping for {@code key} + * @throws NullPointerException if the specified key is null + */ + @SuppressWarnings("unchecked") public V remove(Object key) { + if (key == null) + throw new NullPointerException(); + return (V)internalReplace(key, null, null); + } + + /** + * {@inheritDoc} + * + * @throws NullPointerException if the specified key is null + */ + public boolean remove(Object key, Object value) { + if (key == null) + throw new NullPointerException(); + if (value == null) + return false; + return internalReplace(key, null, value) != null; + } + + /** + * {@inheritDoc} + * + * @throws NullPointerException if any of the arguments are null + */ + public boolean replace(K key, V oldValue, V newValue) { + if (key == null || oldValue == null || newValue == null) + throw new NullPointerException(); + return internalReplace(key, newValue, oldValue) != null; + } + + /** + * {@inheritDoc} + * + * @return the previous value associated with the specified key, + * or {@code null} if there was no mapping for the key + * @throws NullPointerException if the specified key or value is null + */ + @SuppressWarnings("unchecked") public V replace(K key, V value) { + if (key == null || value == null) + throw new NullPointerException(); + return (V)internalReplace(key, value, null); + } + + /** + * Removes all of the mappings from this map. + */ + public void clear() { + internalClear(); + } + + /** + * Returns a {@link Set} view of the keys contained in this map. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. + * + * @return the set view + */ + public KeySetView keySet() { + KeySetView ks = keySet; + return (ks != null) ? ks : (keySet = new KeySetView(this, null)); + } + + /** + * Returns a {@link Set} view of the keys in this map, using the + * given common mapped value for any additions (i.e., {@link + * Collection#add} and {@link Collection#addAll}). This is of + * course only appropriate if it is acceptable to use the same + * value for all additions from this view. + * + * @param mappedValue the mapped value to use for any + * additions. + * @return the set view + * @throws NullPointerException if the mappedValue is null + */ + public KeySetView keySet(V mappedValue) { + if (mappedValue == null) + throw new NullPointerException(); + return new KeySetView(this, mappedValue); + } + + /** + * Returns a {@link Collection} view of the values contained in this map. + * The collection is backed by the map, so changes to the map are + * reflected in the collection, and vice-versa. + */ + public ValuesView values() { + ValuesView vs = values; + return (vs != null) ? vs : (values = new ValuesView(this)); + } + + /** + * Returns a {@link Set} view of the mappings contained in this map. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. The set supports element + * removal, which removes the corresponding mapping from the map, + * via the {@code Iterator.remove}, {@code Set.remove}, + * {@code removeAll}, {@code retainAll}, and {@code clear} + * operations. It does not support the {@code add} or + * {@code addAll} operations. + * + *

The view's {@code iterator} is a "weakly consistent" iterator + * that will never throw {@link ConcurrentModificationException}, + * and guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not guaranteed to) + * reflect any modifications subsequent to construction. + */ + public Set> entrySet() { + EntrySetView es = entrySet; + return (es != null) ? es : (entrySet = new EntrySetView(this)); + } + + /** + * Returns an enumeration of the keys in this table. + * + * @return an enumeration of the keys in this table + * @see #keySet() + */ + public Enumeration keys() { + return new KeyIterator(this); + } + + /** + * Returns an enumeration of the values in this table. + * + * @return an enumeration of the values in this table + * @see #values() + */ + public Enumeration elements() { + return new ValueIterator(this); + } + + /** + * Returns a partitionable iterator of the keys in this map. + * + * @return a partitionable iterator of the keys in this map + */ + public Spliterator keySpliterator() { + return new KeyIterator(this); + } + + /** + * Returns a partitionable iterator of the values in this map. + * + * @return a partitionable iterator of the values in this map + */ + public Spliterator valueSpliterator() { + return new ValueIterator(this); + } + + /** + * Returns a partitionable iterator of the entries in this map. + * + * @return a partitionable iterator of the entries in this map + */ + public Spliterator> entrySpliterator() { + return new EntryIterator(this); + } + + /** + * Returns the hash code value for this {@link Map}, i.e., + * the sum of, for each key-value pair in the map, + * {@code key.hashCode() ^ value.hashCode()}. + * + * @return the hash code value for this map + */ + public int hashCode() { + int h = 0; + Traverser it = new Traverser(this); + Object v; + while ((v = it.advance()) != null) { + h += it.nextKey.hashCode() ^ v.hashCode(); + } + return h; + } + + /** + * Returns a string representation of this map. The string + * representation consists of a list of key-value mappings (in no + * particular order) enclosed in braces ("{@code {}}"). Adjacent + * mappings are separated by the characters {@code ", "} (comma + * and space). Each key-value mapping is rendered as the key + * followed by an equals sign ("{@code =}") followed by the + * associated value. + * + * @return a string representation of this map + */ + public String toString() { + Traverser it = new Traverser(this); + StringBuilder sb = new StringBuilder(); + sb.append('{'); + Object v; + if ((v = it.advance()) != null) { + for (;;) { + Object k = it.nextKey; + sb.append(k == this ? "(this Map)" : k); + sb.append('='); + sb.append(v == this ? "(this Map)" : v); + if ((v = it.advance()) == null) + break; + sb.append(',').append(' '); + } + } + return sb.append('}').toString(); + } + + /** + * Compares the specified object with this map for equality. + * Returns {@code true} if the given object is a map with the same + * mappings as this map. This operation may return misleading + * results if either map is concurrently modified during execution + * of this method. + * + * @param o object to be compared for equality with this map + * @return {@code true} if the specified object is equal to this map + */ + public boolean equals(Object o) { + if (o != this) { + if (!(o instanceof Map)) + return false; + Map m = (Map) o; + Traverser it = new Traverser(this); + Object val; + while ((val = it.advance()) != null) { + Object v = m.get(it.nextKey); + if (v == null || (v != val && !v.equals(val))) + return false; + } + for (Map.Entry e : m.entrySet()) { + Object mk, mv, v; + if ((mk = e.getKey()) == null || + (mv = e.getValue()) == null || + (v = internalGet(mk)) == null || + (mv != v && !mv.equals(v))) + return false; + } + } + return true; + } + + /* ----------------Iterators -------------- */ + + @SuppressWarnings("serial") static final class KeyIterator extends Traverser + implements Spliterator, Enumeration { + KeyIterator(ConcurrentHashMapV8 map) { super(map); } + KeyIterator(Traverser it) { + super(it); + } + public KeyIterator split() { + if (nextKey != null) + throw new IllegalStateException(); + return new KeyIterator(this); + } + @SuppressWarnings("unchecked") public final K next() { + if (nextVal == null && advance() == null) + throw new NoSuchElementException(); + Object k = nextKey; + nextVal = null; + return (K) k; + } + + public final K nextElement() { return next(); } + } + + @SuppressWarnings("serial") static final class ValueIterator extends Traverser + implements Spliterator, Enumeration { + ValueIterator(ConcurrentHashMapV8 map) { super(map); } + ValueIterator(Traverser it) { + super(it); + } + public ValueIterator split() { + if (nextKey != null) + throw new IllegalStateException(); + return new ValueIterator(this); + } + + @SuppressWarnings("unchecked") public final V next() { + Object v; + if ((v = nextVal) == null && (v = advance()) == null) + throw new NoSuchElementException(); + nextVal = null; + return (V) v; + } + + public final V nextElement() { return next(); } + } + + @SuppressWarnings("serial") static final class EntryIterator extends Traverser + implements Spliterator> { + EntryIterator(ConcurrentHashMapV8 map) { super(map); } + EntryIterator(Traverser it) { + super(it); + } + public EntryIterator split() { + if (nextKey != null) + throw new IllegalStateException(); + return new EntryIterator(this); + } + + @SuppressWarnings("unchecked") public final Map.Entry next() { + Object v; + if ((v = nextVal) == null && (v = advance()) == null) + throw new NoSuchElementException(); + Object k = nextKey; + nextVal = null; + return new MapEntry((K)k, (V)v, map); + } + } + + /** + * Exported Entry for iterators + */ + static final class MapEntry implements Map.Entry { + final K key; // non-null + V val; // non-null + final ConcurrentHashMapV8 map; + MapEntry(K key, V val, ConcurrentHashMapV8 map) { + this.key = key; + this.val = val; + this.map = map; + } + public final K getKey() { return key; } + public final V getValue() { return val; } + public final int hashCode() { return key.hashCode() ^ val.hashCode(); } + public final String toString(){ return key + "=" + val; } + + public final boolean equals(Object o) { + Object k, v; Map.Entry e; + return ((o instanceof Map.Entry) && + (k = (e = (Map.Entry)o).getKey()) != null && + (v = e.getValue()) != null && + (k == key || k.equals(key)) && + (v == val || v.equals(val))); + } + + /** + * Sets our entry's value and writes through to the map. The + * value to return is somewhat arbitrary here. Since we do not + * necessarily track asynchronous changes, the most recent + * "previous" value could be different from what we return (or + * could even have been removed in which case the put will + * re-establish). We do not and cannot guarantee more. + */ + public final V setValue(V value) { + if (value == null) throw new NullPointerException(); + V v = val; + val = value; + map.put(key, value); + return v; + } + } + + /* ---------------- Serialization Support -------------- */ + + /** + * Stripped-down version of helper class used in previous version, + * declared for the sake of serialization compatibility + */ + static class Segment implements Serializable { + private static final long serialVersionUID = 2249069246763182397L; + final float loadFactor; + Segment(float lf) { this.loadFactor = lf; } + } + + /** + * Saves the state of the {@code ConcurrentHashMapV8} instance to a + * stream (i.e., serializes it). + * @param s the stream + * @serialData + * the key (Object) and value (Object) + * for each key-value mapping, followed by a null pair. + * The key-value mappings are emitted in no particular order. + */ + @SuppressWarnings("unchecked") private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + if (segments == null) { // for serialization compatibility + segments = (Segment[]) + new Segment[DEFAULT_CONCURRENCY_LEVEL]; + for (int i = 0; i < segments.length; ++i) + segments[i] = new Segment(LOAD_FACTOR); + } + s.defaultWriteObject(); + Traverser it = new Traverser(this); + Object v; + while ((v = it.advance()) != null) { + s.writeObject(it.nextKey); + s.writeObject(v); + } + s.writeObject(null); + s.writeObject(null); + segments = null; // throw away + } + + /** + * Reconstitutes the instance from a stream (that is, deserializes it). + * @param s the stream + */ + @SuppressWarnings("unchecked") private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + this.segments = null; // unneeded + // initialize transient final field + UNSAFE.putObjectVolatile(this, counterOffset, new LongAdder()); + + // Create all nodes, then place in table once size is known + long size = 0L; + Node p = null; + for (;;) { + K k = (K) s.readObject(); + V v = (V) s.readObject(); + if (k != null && v != null) { + int h = spread(k.hashCode()); + p = new Node(h, k, v, p); + ++size; + } + else + break; + } + if (p != null) { + boolean init = false; + int n; + if (size >= (long)(MAXIMUM_CAPACITY >>> 1)) + n = MAXIMUM_CAPACITY; + else { + int sz = (int)size; + n = tableSizeFor(sz + (sz >>> 1) + 1); + } + int sc = sizeCtl; + boolean collide = false; + if (n > sc && + UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) { + try { + if (table == null) { + init = true; + Node[] tab = new Node[n]; + int mask = n - 1; + while (p != null) { + int j = p.hash & mask; + Node next = p.next; + Node q = p.next = tabAt(tab, j); + setTabAt(tab, j, p); + if (!collide && q != null && q.hash == p.hash) + collide = true; + p = next; + } + table = tab; + counter.add(size); + sc = n - (n >>> 2); + } + } finally { + sizeCtl = sc; + } + if (collide) { // rescan and convert to TreeBins + Node[] tab = table; + for (int i = 0; i < tab.length; ++i) { + int c = 0; + for (Node e = tabAt(tab, i); e != null; e = e.next) { + if (++c > TREE_THRESHOLD && + (e.key instanceof Comparable)) { + replaceWithTreeBin(tab, i, e.key); + break; + } + } + } + } + } + if (!init) { // Can only happen if unsafely published. + while (p != null) { + internalPut(p.key, p.val); + p = p.next; + } + } + } + } + + + // ------------------------------------------------------- + + // Sams + /** Interface describing a void action of one argument */ + public interface Action { void apply(A a); } + /** Interface describing a void action of two arguments */ + public interface BiAction { void apply(A a, B b); } + /** Interface describing a function of one argument */ + public interface Generator { T apply(); } + /** Interface describing a function mapping its argument to a double */ + public interface ObjectToDouble { double apply(A a); } + /** Interface describing a function mapping its argument to a long */ + public interface ObjectToLong { long apply(A a); } + /** Interface describing a function mapping its argument to an int */ + public interface ObjectToInt {int apply(A a); } + /** Interface describing a function mapping two arguments to a double */ + public interface ObjectByObjectToDouble { double apply(A a, B b); } + /** Interface describing a function mapping two arguments to a long */ + public interface ObjectByObjectToLong { long apply(A a, B b); } + /** Interface describing a function mapping two arguments to an int */ + public interface ObjectByObjectToInt {int apply(A a, B b); } + /** Interface describing a function mapping a double to a double */ + public interface DoubleToDouble { double apply(double a); } + /** Interface describing a function mapping a long to a long */ + public interface LongToLong { long apply(long a); } + /** Interface describing a function mapping an int to an int */ + public interface IntToInt { int apply(int a); } + /** Interface describing a function mapping two doubles to a double */ + public interface DoubleByDoubleToDouble { double apply(double a, double b); } + /** Interface describing a function mapping two longs to a long */ + public interface LongByLongToLong { long apply(long a, long b); } + /** Interface describing a function mapping two ints to an int */ + public interface IntByIntToInt { int apply(int a, int b); } + + + /* ----------------Views -------------- */ + + /** + * Base class for views. + */ + static abstract class CHMView { + final ConcurrentHashMapV8 map; + CHMView(ConcurrentHashMapV8 map) { this.map = map; } + + /** + * Returns the map backing this view. + * + * @return the map backing this view + */ + public ConcurrentHashMapV8 getMap() { return map; } + + public final int size() { return map.size(); } + public final boolean isEmpty() { return map.isEmpty(); } + public final void clear() { map.clear(); } + + // implementations below rely on concrete classes supplying these + abstract public Iterator iterator(); + abstract public boolean contains(Object o); + abstract public boolean remove(Object o); + + private static final String oomeMsg = "Required array size too large"; + + public final Object[] toArray() { + long sz = map.mappingCount(); + if (sz > (long)(MAX_ARRAY_SIZE)) + throw new OutOfMemoryError(oomeMsg); + int n = (int)sz; + Object[] r = new Object[n]; + int i = 0; + Iterator it = iterator(); + while (it.hasNext()) { + if (i == n) { + if (n >= MAX_ARRAY_SIZE) + throw new OutOfMemoryError(oomeMsg); + if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) + n = MAX_ARRAY_SIZE; + else + n += (n >>> 1) + 1; + r = Arrays.copyOf(r, n); + } + r[i++] = it.next(); + } + return (i == n) ? r : Arrays.copyOf(r, i); + } + + @SuppressWarnings("unchecked") public final T[] toArray(T[] a) { + long sz = map.mappingCount(); + if (sz > (long)(MAX_ARRAY_SIZE)) + throw new OutOfMemoryError(oomeMsg); + int m = (int)sz; + T[] r = (a.length >= m) ? a : + (T[])java.lang.reflect.Array + .newInstance(a.getClass().getComponentType(), m); + int n = r.length; + int i = 0; + Iterator it = iterator(); + while (it.hasNext()) { + if (i == n) { + if (n >= MAX_ARRAY_SIZE) + throw new OutOfMemoryError(oomeMsg); + if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) + n = MAX_ARRAY_SIZE; + else + n += (n >>> 1) + 1; + r = Arrays.copyOf(r, n); + } + r[i++] = (T)it.next(); + } + if (a == r && i < n) { + r[i] = null; // null-terminate + return r; + } + return (i == n) ? r : Arrays.copyOf(r, i); + } + + public final int hashCode() { + int h = 0; + for (Iterator it = iterator(); it.hasNext();) + h += it.next().hashCode(); + return h; + } + + public final String toString() { + StringBuilder sb = new StringBuilder(); + sb.append('['); + Iterator it = iterator(); + if (it.hasNext()) { + for (;;) { + Object e = it.next(); + sb.append(e == this ? "(this Collection)" : e); + if (!it.hasNext()) + break; + sb.append(',').append(' '); + } + } + return sb.append(']').toString(); + } + + public final boolean containsAll(Collection c) { + if (c != this) { + for (Iterator it = c.iterator(); it.hasNext();) { + Object e = it.next(); + if (e == null || !contains(e)) + return false; + } + } + return true; + } + + public final boolean removeAll(Collection c) { + boolean modified = false; + for (Iterator it = iterator(); it.hasNext();) { + if (c.contains(it.next())) { + it.remove(); + modified = true; + } + } + return modified; + } + + public final boolean retainAll(Collection c) { + boolean modified = false; + for (Iterator it = iterator(); it.hasNext();) { + if (!c.contains(it.next())) { + it.remove(); + modified = true; + } + } + return modified; + } + + } + + /** + * A view of a ConcurrentHashMapV8 as a {@link Set} of keys, in + * which additions may optionally be enabled by mapping to a + * common value. This class cannot be directly instantiated. See + * {@link #keySet}, {@link #keySet(Object)}, {@link #newKeySet()}, + * {@link #newKeySet(int)}. + */ + public static class KeySetView extends CHMView implements Set, java.io.Serializable { + private static final long serialVersionUID = 7249069246763182397L; + private final V value; + KeySetView(ConcurrentHashMapV8 map, V value) { // non-public + super(map); + this.value = value; + } + + /** + * Returns the default mapped value for additions, + * or {@code null} if additions are not supported. + * + * @return the default mapped value for additions, or {@code null} + * if not supported. + */ + public V getMappedValue() { return value; } + + // implement Set API + + public boolean contains(Object o) { return map.containsKey(o); } + public boolean remove(Object o) { return map.remove(o) != null; } + + /** + * Returns a "weakly consistent" iterator that will never + * throw {@link ConcurrentModificationException}, and + * guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not + * guaranteed to) reflect any modifications subsequent to + * construction. + * + * @return an iterator over the keys of this map + */ + public Iterator iterator() { return new KeyIterator(map); } + public boolean add(K e) { + V v; + if ((v = value) == null) + throw new UnsupportedOperationException(); + if (e == null) + throw new NullPointerException(); + return map.internalPutIfAbsent(e, v) == null; + } + public boolean addAll(Collection c) { + boolean added = false; + V v; + if ((v = value) == null) + throw new UnsupportedOperationException(); + for (K e : c) { + if (e == null) + throw new NullPointerException(); + if (map.internalPutIfAbsent(e, v) == null) + added = true; + } + return added; + } + public boolean equals(Object o) { + Set c; + return ((o instanceof Set) && + ((c = (Set)o) == this || + (containsAll(c) && c.containsAll(this)))); + } + } + + /** + * A view of a ConcurrentHashMapV8 as a {@link Collection} of + * values, in which additions are disabled. This class cannot be + * directly instantiated. See {@link #values}, + * + *

The view's {@code iterator} is a "weakly consistent" iterator + * that will never throw {@link ConcurrentModificationException}, + * and guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not guaranteed to) + * reflect any modifications subsequent to construction. + */ + public static final class ValuesView extends CHMView + implements Collection { + ValuesView(ConcurrentHashMapV8 map) { super(map); } + public final boolean contains(Object o) { return map.containsValue(o); } + public final boolean remove(Object o) { + if (o != null) { + Iterator it = new ValueIterator(map); + while (it.hasNext()) { + if (o.equals(it.next())) { + it.remove(); + return true; + } + } + } + return false; + } + + /** + * Returns a "weakly consistent" iterator that will never + * throw {@link ConcurrentModificationException}, and + * guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not + * guaranteed to) reflect any modifications subsequent to + * construction. + * + * @return an iterator over the values of this map + */ + public final Iterator iterator() { + return new ValueIterator(map); + } + public final boolean add(V e) { + throw new UnsupportedOperationException(); + } + public final boolean addAll(Collection c) { + throw new UnsupportedOperationException(); + } + } + + /** + * A view of a ConcurrentHashMapV8 as a {@link Set} of (key, value) + * entries. This class cannot be directly instantiated. See + * {@link #entrySet}. + */ + public static final class EntrySetView extends CHMView + implements Set> { + EntrySetView(ConcurrentHashMapV8 map) { super(map); } + public final boolean contains(Object o) { + Object k, v, r; Map.Entry e; + return ((o instanceof Map.Entry) && + (k = (e = (Map.Entry)o).getKey()) != null && + (r = map.get(k)) != null && + (v = e.getValue()) != null && + (v == r || v.equals(r))); + } + public final boolean remove(Object o) { + Object k, v; Map.Entry e; + return ((o instanceof Map.Entry) && + (k = (e = (Map.Entry)o).getKey()) != null && + (v = e.getValue()) != null && + map.remove(k, v)); + } + + /** + * Returns a "weakly consistent" iterator that will never + * throw {@link ConcurrentModificationException}, and + * guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not + * guaranteed to) reflect any modifications subsequent to + * construction. + * + * @return an iterator over the entries of this map + */ + public final Iterator> iterator() { + return new EntryIterator(map); + } + + public final boolean add(Entry e) { + K key = e.getKey(); + V value = e.getValue(); + if (key == null || value == null) + throw new NullPointerException(); + return map.internalPut(key, value) == null; + } + public final boolean addAll(Collection> c) { + boolean added = false; + for (Entry e : c) { + if (add(e)) + added = true; + } + return added; + } + public boolean equals(Object o) { + Set c; + return ((o instanceof Set) && + ((c = (Set)o) == this || + (containsAll(c) && c.containsAll(this)))); + } + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long counterOffset; + private static final long sizeCtlOffset; + private static final long ABASE; + private static final int ASHIFT; + + static { + int ss; + try { + UNSAFE = getUnsafe(); + Class k = ConcurrentHashMapV8.class; + counterOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("counter")); + sizeCtlOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("sizeCtl")); + Class sc = Node[].class; + ABASE = UNSAFE.arrayBaseOffset(sc); + ss = UNSAFE.arrayIndexScale(sc); + } catch (Exception e) { + throw new Error(e); + } + if ((ss & (ss-1)) != 0) + throw new Error("data type scale not a power of two"); + ASHIFT = 31 - Integer.numberOfLeadingZeros(ss); + } + + /** + * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. + * Replace with a simple call to Unsafe.getUnsafe when integrating + * into a jdk. + * + * @return a sun.misc.Unsafe + */ + private static sun.misc.Unsafe getUnsafe() { + try { + return sun.misc.Unsafe.getUnsafe(); + } catch (SecurityException se) { + try { + return java.security.AccessController.doPrivileged + (new java.security + .PrivilegedExceptionAction() { + public sun.misc.Unsafe run() throws Exception { + java.lang.reflect.Field f = sun.misc + .Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (sun.misc.Unsafe) f.get(null); + }}); + } catch (java.security.PrivilegedActionException e) { + throw new RuntimeException("Could not initialize intrinsics", + e.getCause()); + } + } + } +} diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java new file mode 100644 index 0000000..47a923c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java @@ -0,0 +1,203 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This is based on 1.9 version. + +package com.concurrent_ruby.ext.jsr166e; +import java.util.concurrent.atomic.AtomicLong; +import java.io.IOException; +import java.io.Serializable; +import java.io.ObjectInputStream; + +/** + * One or more variables that together maintain an initially zero + * {@code long} sum. When updates (method {@link #add}) are contended + * across threads, the set of variables may grow dynamically to reduce + * contention. Method {@link #sum} (or, equivalently, {@link + * #longValue}) returns the current total combined across the + * variables maintaining the sum. + * + *

This class is usually preferable to {@link AtomicLong} when + * multiple threads update a common sum that is used for purposes such + * as collecting statistics, not for fine-grained synchronization + * control. Under low update contention, the two classes have similar + * characteristics. But under high contention, expected throughput of + * this class is significantly higher, at the expense of higher space + * consumption. + * + *

This class extends {@link Number}, but does not define + * methods such as {@code hashCode} and {@code compareTo} because + * instances are expected to be mutated, and so are not useful as + * collection keys. + * + *

jsr166e note: This class is targeted to be placed in + * java.util.concurrent.atomic. + * + * @since 1.8 + * @author Doug Lea + */ +public class LongAdder extends Striped64 implements Serializable { + private static final long serialVersionUID = 7249069246863182397L; + + /** + * Version of plus for use in retryUpdate + */ + final long fn(long v, long x) { return v + x; } + + /** + * Creates a new adder with initial sum of zero. + */ + public LongAdder() { + } + + /** + * Adds the given value. + * + * @param x the value to add + */ + public void add(long x) { + Cell[] as; long b, v; HashCode hc; Cell a; int n; + if ((as = cells) != null || !casBase(b = base, b + x)) { + boolean uncontended = true; + int h = (hc = threadHashCode.get()).code; + if (as == null || (n = as.length) < 1 || + (a = as[(n - 1) & h]) == null || + !(uncontended = a.cas(v = a.value, v + x))) + retryUpdate(x, hc, uncontended); + } + } + + /** + * Equivalent to {@code add(1)}. + */ + public void increment() { + add(1L); + } + + /** + * Equivalent to {@code add(-1)}. + */ + public void decrement() { + add(-1L); + } + + /** + * Returns the current sum. The returned value is NOT an + * atomic snapshot: Invocation in the absence of concurrent + * updates returns an accurate result, but concurrent updates that + * occur while the sum is being calculated might not be + * incorporated. + * + * @return the sum + */ + public long sum() { + long sum = base; + Cell[] as = cells; + if (as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if (a != null) + sum += a.value; + } + } + return sum; + } + + /** + * Resets variables maintaining the sum to zero. This method may + * be a useful alternative to creating a new adder, but is only + * effective if there are no concurrent updates. Because this + * method is intrinsically racy, it should only be used when it is + * known that no threads are concurrently updating. + */ + public void reset() { + internalReset(0L); + } + + /** + * Equivalent in effect to {@link #sum} followed by {@link + * #reset}. This method may apply for example during quiescent + * points between multithreaded computations. If there are + * updates concurrent with this method, the returned value is + * not guaranteed to be the final value occurring before + * the reset. + * + * @return the sum + */ + public long sumThenReset() { + long sum = base; + Cell[] as = cells; + base = 0L; + if (as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if (a != null) { + sum += a.value; + a.value = 0L; + } + } + } + return sum; + } + + /** + * Returns the String representation of the {@link #sum}. + * @return the String representation of the {@link #sum} + */ + public String toString() { + return Long.toString(sum()); + } + + /** + * Equivalent to {@link #sum}. + * + * @return the sum + */ + public long longValue() { + return sum(); + } + + /** + * Returns the {@link #sum} as an {@code int} after a narrowing + * primitive conversion. + */ + public int intValue() { + return (int)sum(); + } + + /** + * Returns the {@link #sum} as a {@code float} + * after a widening primitive conversion. + */ + public float floatValue() { + return (float)sum(); + } + + /** + * Returns the {@link #sum} as a {@code double} after a widening + * primitive conversion. + */ + public double doubleValue() { + return (double)sum(); + } + + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + s.defaultWriteObject(); + s.writeLong(sum()); + } + + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException { + s.defaultReadObject(); + busy = 0; + cells = null; + base = s.readLong(); + } + +} diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java new file mode 100644 index 0000000..93a277f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java @@ -0,0 +1,342 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This is based on 1.5 version. + +package com.concurrent_ruby.ext.jsr166e; +import java.util.Random; + +/** + * A package-local class holding common representation and mechanics + * for classes supporting dynamic striping on 64bit values. The class + * extends Number so that concrete subclasses must publicly do so. + */ +abstract class Striped64 extends Number { + /* + * This class maintains a lazily-initialized table of atomically + * updated variables, plus an extra "base" field. The table size + * is a power of two. Indexing uses masked per-thread hash codes. + * Nearly all declarations in this class are package-private, + * accessed directly by subclasses. + * + * Table entries are of class Cell; a variant of AtomicLong padded + * to reduce cache contention on most processors. Padding is + * overkill for most Atomics because they are usually irregularly + * scattered in memory and thus don't interfere much with each + * other. But Atomic objects residing in arrays will tend to be + * placed adjacent to each other, and so will most often share + * cache lines (with a huge negative performance impact) without + * this precaution. + * + * In part because Cells are relatively large, we avoid creating + * them until they are needed. When there is no contention, all + * updates are made to the base field. Upon first contention (a + * failed CAS on base update), the table is initialized to size 2. + * The table size is doubled upon further contention until + * reaching the nearest power of two greater than or equal to the + * number of CPUS. Table slots remain empty (null) until they are + * needed. + * + * A single spinlock ("busy") is used for initializing and + * resizing the table, as well as populating slots with new Cells. + * There is no need for a blocking lock: When the lock is not + * available, threads try other slots (or the base). During these + * retries, there is increased contention and reduced locality, + * which is still better than alternatives. + * + * Per-thread hash codes are initialized to random values. + * Contention and/or table collisions are indicated by failed + * CASes when performing an update operation (see method + * retryUpdate). Upon a collision, if the table size is less than + * the capacity, it is doubled in size unless some other thread + * holds the lock. If a hashed slot is empty, and lock is + * available, a new Cell is created. Otherwise, if the slot + * exists, a CAS is tried. Retries proceed by "double hashing", + * using a secondary hash (Marsaglia XorShift) to try to find a + * free slot. + * + * The table size is capped because, when there are more threads + * than CPUs, supposing that each thread were bound to a CPU, + * there would exist a perfect hash function mapping threads to + * slots that eliminates collisions. When we reach capacity, we + * search for this mapping by randomly varying the hash codes of + * colliding threads. Because search is random, and collisions + * only become known via CAS failures, convergence can be slow, + * and because threads are typically not bound to CPUS forever, + * may not occur at all. However, despite these limitations, + * observed contention rates are typically low in these cases. + * + * It is possible for a Cell to become unused when threads that + * once hashed to it terminate, as well as in the case where + * doubling the table causes no thread to hash to it under + * expanded mask. We do not try to detect or remove such cells, + * under the assumption that for long-running instances, observed + * contention levels will recur, so the cells will eventually be + * needed again; and for short-lived ones, it does not matter. + */ + + /** + * Padded variant of AtomicLong supporting only raw accesses plus CAS. + * The value field is placed between pads, hoping that the JVM doesn't + * reorder them. + * + * JVM intrinsics note: It would be possible to use a release-only + * form of CAS here, if it were provided. + */ + static final class Cell { + volatile long p0, p1, p2, p3, p4, p5, p6; + volatile long value; + volatile long q0, q1, q2, q3, q4, q5, q6; + Cell(long x) { value = x; } + + final boolean cas(long cmp, long val) { + return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val); + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long valueOffset; + static { + try { + UNSAFE = getUnsafe(); + Class ak = Cell.class; + valueOffset = UNSAFE.objectFieldOffset + (ak.getDeclaredField("value")); + } catch (Exception e) { + throw new Error(e); + } + } + + } + + /** + * Holder for the thread-local hash code. The code is initially + * random, but may be set to a different value upon collisions. + */ + static final class HashCode { + static final Random rng = new Random(); + int code; + HashCode() { + int h = rng.nextInt(); // Avoid zero to allow xorShift rehash + code = (h == 0) ? 1 : h; + } + } + + /** + * The corresponding ThreadLocal class + */ + static final class ThreadHashCode extends ThreadLocal { + public HashCode initialValue() { return new HashCode(); } + } + + /** + * Static per-thread hash codes. Shared across all instances to + * reduce ThreadLocal pollution and because adjustments due to + * collisions in one table are likely to be appropriate for + * others. + */ + static final ThreadHashCode threadHashCode = new ThreadHashCode(); + + /** Number of CPUS, to place bound on table size */ + static final int NCPU = Runtime.getRuntime().availableProcessors(); + + /** + * Table of cells. When non-null, size is a power of 2. + */ + transient volatile Cell[] cells; + + /** + * Base value, used mainly when there is no contention, but also as + * a fallback during table initialization races. Updated via CAS. + */ + transient volatile long base; + + /** + * Spinlock (locked via CAS) used when resizing and/or creating Cells. + */ + transient volatile int busy; + + /** + * Package-private default constructor + */ + Striped64() { + } + + /** + * CASes the base field. + */ + final boolean casBase(long cmp, long val) { + return UNSAFE.compareAndSwapLong(this, baseOffset, cmp, val); + } + + /** + * CASes the busy field from 0 to 1 to acquire lock. + */ + final boolean casBusy() { + return UNSAFE.compareAndSwapInt(this, busyOffset, 0, 1); + } + + /** + * Computes the function of current and new value. Subclasses + * should open-code this update function for most uses, but the + * virtualized form is needed within retryUpdate. + * + * @param currentValue the current value (of either base or a cell) + * @param newValue the argument from a user update call + * @return result of the update function + */ + abstract long fn(long currentValue, long newValue); + + /** + * Handles cases of updates involving initialization, resizing, + * creating new Cells, and/or contention. See above for + * explanation. This method suffers the usual non-modularity + * problems of optimistic retry code, relying on rechecked sets of + * reads. + * + * @param x the value + * @param hc the hash code holder + * @param wasUncontended false if CAS failed before call + */ + final void retryUpdate(long x, HashCode hc, boolean wasUncontended) { + int h = hc.code; + boolean collide = false; // True if last slot nonempty + for (;;) { + Cell[] as; Cell a; int n; long v; + if ((as = cells) != null && (n = as.length) > 0) { + if ((a = as[(n - 1) & h]) == null) { + if (busy == 0) { // Try to attach new Cell + Cell r = new Cell(x); // Optimistically create + if (busy == 0 && casBusy()) { + boolean created = false; + try { // Recheck under lock + Cell[] rs; int m, j; + if ((rs = cells) != null && + (m = rs.length) > 0 && + rs[j = (m - 1) & h] == null) { + rs[j] = r; + created = true; + } + } finally { + busy = 0; + } + if (created) + break; + continue; // Slot is now non-empty + } + } + collide = false; + } + else if (!wasUncontended) // CAS already known to fail + wasUncontended = true; // Continue after rehash + else if (a.cas(v = a.value, fn(v, x))) + break; + else if (n >= NCPU || cells != as) + collide = false; // At max size or stale + else if (!collide) + collide = true; + else if (busy == 0 && casBusy()) { + try { + if (cells == as) { // Expand table unless stale + Cell[] rs = new Cell[n << 1]; + for (int i = 0; i < n; ++i) + rs[i] = as[i]; + cells = rs; + } + } finally { + busy = 0; + } + collide = false; + continue; // Retry with expanded table + } + h ^= h << 13; // Rehash + h ^= h >>> 17; + h ^= h << 5; + } + else if (busy == 0 && cells == as && casBusy()) { + boolean init = false; + try { // Initialize table + if (cells == as) { + Cell[] rs = new Cell[2]; + rs[h & 1] = new Cell(x); + cells = rs; + init = true; + } + } finally { + busy = 0; + } + if (init) + break; + } + else if (casBase(v = base, fn(v, x))) + break; // Fall back on using base + } + hc.code = h; // Record index for next time + } + + + /** + * Sets base and all cells to the given value. + */ + final void internalReset(long initialValue) { + Cell[] as = cells; + base = initialValue; + if (as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if (a != null) + a.value = initialValue; + } + } + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long baseOffset; + private static final long busyOffset; + static { + try { + UNSAFE = getUnsafe(); + Class sk = Striped64.class; + baseOffset = UNSAFE.objectFieldOffset + (sk.getDeclaredField("base")); + busyOffset = UNSAFE.objectFieldOffset + (sk.getDeclaredField("busy")); + } catch (Exception e) { + throw new Error(e); + } + } + + /** + * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. + * Replace with a simple call to Unsafe.getUnsafe when integrating + * into a jdk. + * + * @return a sun.misc.Unsafe + */ + private static sun.misc.Unsafe getUnsafe() { + try { + return sun.misc.Unsafe.getUnsafe(); + } catch (SecurityException se) { + try { + return java.security.AccessController.doPrivileged + (new java.security + .PrivilegedExceptionAction() { + public sun.misc.Unsafe run() throws Exception { + java.lang.reflect.Field f = sun.misc + .Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (sun.misc.Unsafe) f.get(null); + }}); + } catch (java.security.PrivilegedActionException e) { + throw new RuntimeException("Could not initialize intrinsics", + e.getCause()); + } + } + } + +} diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java new file mode 100644 index 0000000..b7fc5a9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java @@ -0,0 +1,3800 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This is based on the 1.79 version. + +package com.concurrent_ruby.ext.jsr166e.nounsafe; + +import org.jruby.RubyClass; +import org.jruby.RubyNumeric; +import org.jruby.RubyObject; +import org.jruby.exceptions.RaiseException; +import com.concurrent_ruby.ext.jsr166e.ConcurrentHashMap; +import com.concurrent_ruby.ext.jsr166y.ThreadLocalRandom; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.Collection; +import java.util.Hashtable; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Enumeration; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.concurrent.locks.AbstractQueuedSynchronizer; + +import java.io.Serializable; + +/** + * A hash table supporting full concurrency of retrievals and + * high expected concurrency for updates. This class obeys the + * same functional specification as {@link java.util.Hashtable}, and + * includes versions of methods corresponding to each method of + * {@code Hashtable}. However, even though all operations are + * thread-safe, retrieval operations do not entail locking, + * and there is not any support for locking the entire table + * in a way that prevents all access. This class is fully + * interoperable with {@code Hashtable} in programs that rely on its + * thread safety but not on its synchronization details. + * + *

Retrieval operations (including {@code get}) generally do not + * block, so may overlap with update operations (including {@code put} + * and {@code remove}). Retrievals reflect the results of the most + * recently completed update operations holding upon their + * onset. (More formally, an update operation for a given key bears a + * happens-before relation with any (non-null) retrieval for + * that key reporting the updated value.) For aggregate operations + * such as {@code putAll} and {@code clear}, concurrent retrievals may + * reflect insertion or removal of only some entries. Similarly, + * Iterators and Enumerations return elements reflecting the state of + * the hash table at some point at or since the creation of the + * iterator/enumeration. They do not throw {@link + * ConcurrentModificationException}. However, iterators are designed + * to be used by only one thread at a time. Bear in mind that the + * results of aggregate status methods including {@code size}, {@code + * isEmpty}, and {@code containsValue} are typically useful only when + * a map is not undergoing concurrent updates in other threads. + * Otherwise the results of these methods reflect transient states + * that may be adequate for monitoring or estimation purposes, but not + * for program control. + * + *

The table is dynamically expanded when there are too many + * collisions (i.e., keys that have distinct hash codes but fall into + * the same slot modulo the table size), with the expected average + * effect of maintaining roughly two bins per mapping (corresponding + * to a 0.75 load factor threshold for resizing). There may be much + * variance around this average as mappings are added and removed, but + * overall, this maintains a commonly accepted time/space tradeoff for + * hash tables. However, resizing this or any other kind of hash + * table may be a relatively slow operation. When possible, it is a + * good idea to provide a size estimate as an optional {@code + * initialCapacity} constructor argument. An additional optional + * {@code loadFactor} constructor argument provides a further means of + * customizing initial table capacity by specifying the table density + * to be used in calculating the amount of space to allocate for the + * given number of elements. Also, for compatibility with previous + * versions of this class, constructors may optionally specify an + * expected {@code concurrencyLevel} as an additional hint for + * internal sizing. Note that using many keys with exactly the same + * {@code hashCode()} is a sure way to slow down performance of any + * hash table. + * + *

A {@link Set} projection of a ConcurrentHashMapV8 may be created + * (using {@link #newKeySet()} or {@link #newKeySet(int)}), or viewed + * (using {@link #keySet(Object)} when only keys are of interest, and the + * mapped values are (perhaps transiently) not used or all take the + * same mapping value. + * + *

A ConcurrentHashMapV8 can be used as scalable frequency map (a + * form of histogram or multiset) by using {@link LongAdder} values + * and initializing via {@link #computeIfAbsent}. For example, to add + * a count to a {@code ConcurrentHashMapV8 freqs}, you + * can use {@code freqs.computeIfAbsent(k -> new + * LongAdder()).increment();} + * + *

This class and its views and iterators implement all of the + * optional methods of the {@link Map} and {@link Iterator} + * interfaces. + * + *

Like {@link Hashtable} but unlike {@link HashMap}, this class + * does not allow {@code null} to be used as a key or value. + * + *

ConcurrentHashMapV8s support parallel operations using the {@link + * ForkJoinPool#commonPool}. (Tasks that may be used in other contexts + * are available in class {@link ForkJoinTasks}). These operations are + * designed to be safely, and often sensibly, applied even with maps + * that are being concurrently updated by other threads; for example, + * when computing a snapshot summary of the values in a shared + * registry. There are three kinds of operation, each with four + * forms, accepting functions with Keys, Values, Entries, and (Key, + * Value) arguments and/or return values. (The first three forms are + * also available via the {@link #keySet()}, {@link #values()} and + * {@link #entrySet()} views). Because the elements of a + * ConcurrentHashMapV8 are not ordered in any particular way, and may be + * processed in different orders in different parallel executions, the + * correctness of supplied functions should not depend on any + * ordering, or on any other objects or values that may transiently + * change while computation is in progress; and except for forEach + * actions, should ideally be side-effect-free. + * + *

+ * + *

The concurrency properties of bulk operations follow + * from those of ConcurrentHashMapV8: Any non-null result returned + * from {@code get(key)} and related access methods bears a + * happens-before relation with the associated insertion or + * update. The result of any bulk operation reflects the + * composition of these per-element relations (but is not + * necessarily atomic with respect to the map as a whole unless it + * is somehow known to be quiescent). Conversely, because keys + * and values in the map are never null, null serves as a reliable + * atomic indicator of the current lack of any result. To + * maintain this property, null serves as an implicit basis for + * all non-scalar reduction operations. For the double, long, and + * int versions, the basis should be one that, when combined with + * any other value, returns that other value (more formally, it + * should be the identity element for the reduction). Most common + * reductions have these properties; for example, computing a sum + * with basis 0 or a minimum with basis MAX_VALUE. + * + *

Search and transformation functions provided as arguments + * should similarly return null to indicate the lack of any result + * (in which case it is not used). In the case of mapped + * reductions, this also enables transformations to serve as + * filters, returning null (or, in the case of primitive + * specializations, the identity basis) if the element should not + * be combined. You can create compound transformations and + * filterings by composing them yourself under this "null means + * there is nothing there now" rule before using them in search or + * reduce operations. + * + *

Methods accepting and/or returning Entry arguments maintain + * key-value associations. They may be useful for example when + * finding the key for the greatest value. Note that "plain" Entry + * arguments can be supplied using {@code new + * AbstractMap.SimpleEntry(k,v)}. + * + *

Bulk operations may complete abruptly, throwing an + * exception encountered in the application of a supplied + * function. Bear in mind when handling such exceptions that other + * concurrently executing functions could also have thrown + * exceptions, or would have done so if the first exception had + * not occurred. + * + *

Parallel speedups for bulk operations compared to sequential + * processing are common but not guaranteed. Operations involving + * brief functions on small maps may execute more slowly than + * sequential loops if the underlying work to parallelize the + * computation is more expensive than the computation itself. + * Similarly, parallelization may not lead to much actual parallelism + * if all processors are busy performing unrelated tasks. + * + *

All arguments to all task methods must be non-null. + * + *

jsr166e note: During transition, this class + * uses nested functional interfaces with different names but the + * same forms as those expected for JDK8. + * + *

This class is a member of the + * + * Java Collections Framework. + * + * @since 1.5 + * @author Doug Lea + * @param the type of keys maintained by this map + * @param the type of mapped values + */ +public class ConcurrentHashMapV8 + implements ConcurrentMap, Serializable, ConcurrentHashMap { + private static final long serialVersionUID = 7249069246763182397L; + + /** + * A partitionable iterator. A Spliterator can be traversed + * directly, but can also be partitioned (before traversal) by + * creating another Spliterator that covers a non-overlapping + * portion of the elements, and so may be amenable to parallel + * execution. + * + *

This interface exports a subset of expected JDK8 + * functionality. + * + *

Sample usage: Here is one (of the several) ways to compute + * the sum of the values held in a map using the ForkJoin + * framework. As illustrated here, Spliterators are well suited to + * designs in which a task repeatedly splits off half its work + * into forked subtasks until small enough to process directly, + * and then joins these subtasks. Variants of this style can also + * be used in completion-based designs. + * + *

+     * {@code ConcurrentHashMapV8 m = ...
+     * // split as if have 8 * parallelism, for load balance
+     * int n = m.size();
+     * int p = aForkJoinPool.getParallelism() * 8;
+     * int split = (n < p)? n : p;
+     * long sum = aForkJoinPool.invoke(new SumValues(m.valueSpliterator(), split, null));
+     * // ...
+     * static class SumValues extends RecursiveTask {
+     *   final Spliterator s;
+     *   final int split;             // split while > 1
+     *   final SumValues nextJoin;    // records forked subtasks to join
+     *   SumValues(Spliterator s, int depth, SumValues nextJoin) {
+     *     this.s = s; this.depth = depth; this.nextJoin = nextJoin;
+     *   }
+     *   public Long compute() {
+     *     long sum = 0;
+     *     SumValues subtasks = null; // fork subtasks
+     *     for (int s = split >>> 1; s > 0; s >>>= 1)
+     *       (subtasks = new SumValues(s.split(), s, subtasks)).fork();
+     *     while (s.hasNext())        // directly process remaining elements
+     *       sum += s.next();
+     *     for (SumValues t = subtasks; t != null; t = t.nextJoin)
+     *       sum += t.join();         // collect subtask results
+     *     return sum;
+     *   }
+     * }
+     * }
+ */ + public static interface Spliterator extends Iterator { + /** + * Returns a Spliterator covering approximately half of the + * elements, guaranteed not to overlap with those subsequently + * returned by this Spliterator. After invoking this method, + * the current Spliterator will not produce any of + * the elements of the returned Spliterator, but the two + * Spliterators together will produce all of the elements that + * would have been produced by this Spliterator had this + * method not been called. The exact number of elements + * produced by the returned Spliterator is not guaranteed, and + * may be zero (i.e., with {@code hasNext()} reporting {@code + * false}) if this Spliterator cannot be further split. + * + * @return a Spliterator covering approximately half of the + * elements + * @throws IllegalStateException if this Spliterator has + * already commenced traversing elements + */ + Spliterator split(); + } + + + /* + * Overview: + * + * The primary design goal of this hash table is to maintain + * concurrent readability (typically method get(), but also + * iterators and related methods) while minimizing update + * contention. Secondary goals are to keep space consumption about + * the same or better than java.util.HashMap, and to support high + * initial insertion rates on an empty table by many threads. + * + * Each key-value mapping is held in a Node. Because Node fields + * can contain special values, they are defined using plain Object + * types. Similarly in turn, all internal methods that use them + * work off Object types. And similarly, so do the internal + * methods of auxiliary iterator and view classes. All public + * generic typed methods relay in/out of these internal methods, + * supplying null-checks and casts as needed. This also allows + * many of the public methods to be factored into a smaller number + * of internal methods (although sadly not so for the five + * variants of put-related operations). The validation-based + * approach explained below leads to a lot of code sprawl because + * retry-control precludes factoring into smaller methods. + * + * The table is lazily initialized to a power-of-two size upon the + * first insertion. Each bin in the table normally contains a + * list of Nodes (most often, the list has only zero or one Node). + * Table accesses require volatile/atomic reads, writes, and + * CASes. Because there is no other way to arrange this without + * adding further indirections, we use intrinsics + * (sun.misc.Unsafe) operations. The lists of nodes within bins + * are always accurately traversable under volatile reads, so long + * as lookups check hash code and non-nullness of value before + * checking key equality. + * + * We use the top two bits of Node hash fields for control + * purposes -- they are available anyway because of addressing + * constraints. As explained further below, these top bits are + * used as follows: + * 00 - Normal + * 01 - Locked + * 11 - Locked and may have a thread waiting for lock + * 10 - Node is a forwarding node + * + * The lower 30 bits of each Node's hash field contain a + * transformation of the key's hash code, except for forwarding + * nodes, for which the lower bits are zero (and so always have + * hash field == MOVED). + * + * Insertion (via put or its variants) of the first node in an + * empty bin is performed by just CASing it to the bin. This is + * by far the most common case for put operations under most + * key/hash distributions. Other update operations (insert, + * delete, and replace) require locks. We do not want to waste + * the space required to associate a distinct lock object with + * each bin, so instead use the first node of a bin list itself as + * a lock. Blocking support for these locks relies on the builtin + * "synchronized" monitors. However, we also need a tryLock + * construction, so we overlay these by using bits of the Node + * hash field for lock control (see above), and so normally use + * builtin monitors only for blocking and signalling using + * wait/notifyAll constructions. See Node.tryAwaitLock. + * + * Using the first node of a list as a lock does not by itself + * suffice though: When a node is locked, any update must first + * validate that it is still the first node after locking it, and + * retry if not. Because new nodes are always appended to lists, + * once a node is first in a bin, it remains first until deleted + * or the bin becomes invalidated (upon resizing). However, + * operations that only conditionally update may inspect nodes + * until the point of update. This is a converse of sorts to the + * lazy locking technique described by Herlihy & Shavit. + * + * The main disadvantage of per-bin locks is that other update + * operations on other nodes in a bin list protected by the same + * lock can stall, for example when user equals() or mapping + * functions take a long time. However, statistically, under + * random hash codes, this is not a common problem. Ideally, the + * frequency of nodes in bins follows a Poisson distribution + * (http://en.wikipedia.org/wiki/Poisson_distribution) with a + * parameter of about 0.5 on average, given the resizing threshold + * of 0.75, although with a large variance because of resizing + * granularity. Ignoring variance, the expected occurrences of + * list size k are (exp(-0.5) * pow(0.5, k) / factorial(k)). The + * first values are: + * + * 0: 0.60653066 + * 1: 0.30326533 + * 2: 0.07581633 + * 3: 0.01263606 + * 4: 0.00157952 + * 5: 0.00015795 + * 6: 0.00001316 + * 7: 0.00000094 + * 8: 0.00000006 + * more: less than 1 in ten million + * + * Lock contention probability for two threads accessing distinct + * elements is roughly 1 / (8 * #elements) under random hashes. + * + * Actual hash code distributions encountered in practice + * sometimes deviate significantly from uniform randomness. This + * includes the case when N > (1<<30), so some keys MUST collide. + * Similarly for dumb or hostile usages in which multiple keys are + * designed to have identical hash codes. Also, although we guard + * against the worst effects of this (see method spread), sets of + * hashes may differ only in bits that do not impact their bin + * index for a given power-of-two mask. So we use a secondary + * strategy that applies when the number of nodes in a bin exceeds + * a threshold, and at least one of the keys implements + * Comparable. These TreeBins use a balanced tree to hold nodes + * (a specialized form of red-black trees), bounding search time + * to O(log N). Each search step in a TreeBin is around twice as + * slow as in a regular list, but given that N cannot exceed + * (1<<64) (before running out of addresses) this bounds search + * steps, lock hold times, etc, to reasonable constants (roughly + * 100 nodes inspected per operation worst case) so long as keys + * are Comparable (which is very common -- String, Long, etc). + * TreeBin nodes (TreeNodes) also maintain the same "next" + * traversal pointers as regular nodes, so can be traversed in + * iterators in the same way. + * + * The table is resized when occupancy exceeds a percentage + * threshold (nominally, 0.75, but see below). Only a single + * thread performs the resize (using field "sizeCtl", to arrange + * exclusion), but the table otherwise remains usable for reads + * and updates. Resizing proceeds by transferring bins, one by + * one, from the table to the next table. Because we are using + * power-of-two expansion, the elements from each bin must either + * stay at same index, or move with a power of two offset. We + * eliminate unnecessary node creation by catching cases where old + * nodes can be reused because their next fields won't change. On + * average, only about one-sixth of them need cloning when a table + * doubles. The nodes they replace will be garbage collectable as + * soon as they are no longer referenced by any reader thread that + * may be in the midst of concurrently traversing table. Upon + * transfer, the old table bin contains only a special forwarding + * node (with hash field "MOVED") that contains the next table as + * its key. On encountering a forwarding node, access and update + * operations restart, using the new table. + * + * Each bin transfer requires its bin lock. However, unlike other + * cases, a transfer can skip a bin if it fails to acquire its + * lock, and revisit it later (unless it is a TreeBin). Method + * rebuild maintains a buffer of TRANSFER_BUFFER_SIZE bins that + * have been skipped because of failure to acquire a lock, and + * blocks only if none are available (i.e., only very rarely). + * The transfer operation must also ensure that all accessible + * bins in both the old and new table are usable by any traversal. + * When there are no lock acquisition failures, this is arranged + * simply by proceeding from the last bin (table.length - 1) up + * towards the first. Upon seeing a forwarding node, traversals + * (see class Iter) arrange to move to the new table + * without revisiting nodes. However, when any node is skipped + * during a transfer, all earlier table bins may have become + * visible, so are initialized with a reverse-forwarding node back + * to the old table until the new ones are established. (This + * sometimes requires transiently locking a forwarding node, which + * is possible under the above encoding.) These more expensive + * mechanics trigger only when necessary. + * + * The traversal scheme also applies to partial traversals of + * ranges of bins (via an alternate Traverser constructor) + * to support partitioned aggregate operations. Also, read-only + * operations give up if ever forwarded to a null table, which + * provides support for shutdown-style clearing, which is also not + * currently implemented. + * + * Lazy table initialization minimizes footprint until first use, + * and also avoids resizings when the first operation is from a + * putAll, constructor with map argument, or deserialization. + * These cases attempt to override the initial capacity settings, + * but harmlessly fail to take effect in cases of races. + * + * The element count is maintained using a LongAdder, which avoids + * contention on updates but can encounter cache thrashing if read + * too frequently during concurrent access. To avoid reading so + * often, resizing is attempted either when a bin lock is + * contended, or upon adding to a bin already holding two or more + * nodes (checked before adding in the xIfAbsent methods, after + * adding in others). Under uniform hash distributions, the + * probability of this occurring at threshold is around 13%, + * meaning that only about 1 in 8 puts check threshold (and after + * resizing, many fewer do so). But this approximation has high + * variance for small table sizes, so we check on any collision + * for sizes <= 64. The bulk putAll operation further reduces + * contention by only committing count updates upon these size + * checks. + * + * Maintaining API and serialization compatibility with previous + * versions of this class introduces several oddities. Mainly: We + * leave untouched but unused constructor arguments refering to + * concurrencyLevel. We accept a loadFactor constructor argument, + * but apply it only to initial table capacity (which is the only + * time that we can guarantee to honor it.) We also declare an + * unused "Segment" class that is instantiated in minimal form + * only when serializing. + */ + + /* ---------------- Constants -------------- */ + + /** + * The largest possible table capacity. This value must be + * exactly 1<<30 to stay within Java array allocation and indexing + * bounds for power of two table sizes, and is further required + * because the top two bits of 32bit hash fields are used for + * control purposes. + */ + private static final int MAXIMUM_CAPACITY = 1 << 30; + + /** + * The default initial table capacity. Must be a power of 2 + * (i.e., at least 1) and at most MAXIMUM_CAPACITY. + */ + private static final int DEFAULT_CAPACITY = 16; + + /** + * The largest possible (non-power of two) array size. + * Needed by toArray and related methods. + */ + static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + + /** + * The default concurrency level for this table. Unused but + * defined for compatibility with previous versions of this class. + */ + private static final int DEFAULT_CONCURRENCY_LEVEL = 16; + + /** + * The load factor for this table. Overrides of this value in + * constructors affect only the initial table capacity. The + * actual floating point value isn't normally used -- it is + * simpler to use expressions such as {@code n - (n >>> 2)} for + * the associated resizing threshold. + */ + private static final float LOAD_FACTOR = 0.75f; + + /** + * The buffer size for skipped bins during transfers. The + * value is arbitrary but should be large enough to avoid + * most locking stalls during resizes. + */ + private static final int TRANSFER_BUFFER_SIZE = 32; + + /** + * The bin count threshold for using a tree rather than list for a + * bin. The value reflects the approximate break-even point for + * using tree-based operations. + * Note that Doug's version defaults to 8, but when dealing with + * Ruby objects it is actually beneficial to avoid TreeNodes + * as long as possible as it usually means going into Ruby land. + */ + private static final int TREE_THRESHOLD = 16; + + /* + * Encodings for special uses of Node hash fields. See above for + * explanation. + */ + static final int MOVED = 0x80000000; // hash field for forwarding nodes + static final int LOCKED = 0x40000000; // set/tested only as a bit + static final int WAITING = 0xc0000000; // both bits set/tested together + static final int HASH_BITS = 0x3fffffff; // usable bits of normal node hash + + /* ---------------- Fields -------------- */ + + /** + * The array of bins. Lazily initialized upon first insertion. + * Size is always a power of two. Accessed directly by iterators. + */ + transient volatile AtomicReferenceArray table; + + /** + * The counter maintaining number of elements. + */ + private transient LongAdder counter; + + /** + * Table initialization and resizing control. When negative, the + * table is being initialized or resized. Otherwise, when table is + * null, holds the initial table size to use upon creation, or 0 + * for default. After initialization, holds the next element count + * value upon which to resize the table. + */ + private transient volatile int sizeCtl; + + // views + private transient KeySetView keySet; + private transient ValuesView values; + private transient EntrySetView entrySet; + + /** For serialization compatibility. Null unless serialized; see below */ + private Segment[] segments; + + static AtomicIntegerFieldUpdater SIZE_CTRL_UPDATER = AtomicIntegerFieldUpdater.newUpdater(ConcurrentHashMapV8.class, "sizeCtl"); + + /* ---------------- Table element access -------------- */ + + /* + * Volatile access methods are used for table elements as well as + * elements of in-progress next table while resizing. Uses are + * null checked by callers, and implicitly bounds-checked, relying + * on the invariants that tab arrays have non-zero size, and all + * indices are masked with (tab.length - 1) which is never + * negative and always less than length. Note that, to be correct + * wrt arbitrary concurrency errors by users, bounds checks must + * operate on local variables, which accounts for some odd-looking + * inline assignments below. + */ + + static final Node tabAt(AtomicReferenceArray tab, int i) { // used by Iter + return tab.get(i); + } + + private static final boolean casTabAt(AtomicReferenceArray tab, int i, Node c, Node v) { + return tab.compareAndSet(i, c, v); + } + + private static final void setTabAt(AtomicReferenceArray tab, int i, Node v) { + tab.set(i, v); + } + + /* ---------------- Nodes -------------- */ + + /** + * Key-value entry. Note that this is never exported out as a + * user-visible Map.Entry (see MapEntry below). Nodes with a hash + * field of MOVED are special, and do not contain user keys or + * values. Otherwise, keys are never null, and null val fields + * indicate that a node is in the process of being deleted or + * created. For purposes of read-only access, a key may be read + * before a val, but can only be used after checking val to be + * non-null. + */ + static class Node { + volatile int hash; + final Object key; + volatile Object val; + volatile Node next; + + static AtomicIntegerFieldUpdater HASH_UPDATER = AtomicIntegerFieldUpdater.newUpdater(Node.class, "hash"); + + Node(int hash, Object key, Object val, Node next) { + this.hash = hash; + this.key = key; + this.val = val; + this.next = next; + } + + /** CompareAndSet the hash field */ + final boolean casHash(int cmp, int val) { + return HASH_UPDATER.compareAndSet(this, cmp, val); + } + + /** The number of spins before blocking for a lock */ + static final int MAX_SPINS = + Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1; + + /** + * Spins a while if LOCKED bit set and this node is the first + * of its bin, and then sets WAITING bits on hash field and + * blocks (once) if they are still set. It is OK for this + * method to return even if lock is not available upon exit, + * which enables these simple single-wait mechanics. + * + * The corresponding signalling operation is performed within + * callers: Upon detecting that WAITING has been set when + * unlocking lock (via a failed CAS from non-waiting LOCKED + * state), unlockers acquire the sync lock and perform a + * notifyAll. + * + * The initial sanity check on tab and bounds is not currently + * necessary in the only usages of this method, but enables + * use in other future contexts. + */ + final void tryAwaitLock(AtomicReferenceArray tab, int i) { + if (tab != null && i >= 0 && i < tab.length()) { // sanity check + int r = ThreadLocalRandom.current().nextInt(); // randomize spins + int spins = MAX_SPINS, h; + while (tabAt(tab, i) == this && ((h = hash) & LOCKED) != 0) { + if (spins >= 0) { + r ^= r << 1; r ^= r >>> 3; r ^= r << 10; // xorshift + if (r >= 0 && --spins == 0) + Thread.yield(); // yield before block + } + else if (casHash(h, h | WAITING)) { + synchronized (this) { + if (tabAt(tab, i) == this && + (hash & WAITING) == WAITING) { + try { + wait(); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + } + else + notifyAll(); // possibly won race vs signaller + } + break; + } + } + } + } + } + + /* ---------------- TreeBins -------------- */ + + /** + * Nodes for use in TreeBins + */ + static final class TreeNode extends Node { + TreeNode parent; // red-black tree links + TreeNode left; + TreeNode right; + TreeNode prev; // needed to unlink next upon deletion + boolean red; + + TreeNode(int hash, Object key, Object val, Node next, TreeNode parent) { + super(hash, key, val, next); + this.parent = parent; + } + } + + /** + * A specialized form of red-black tree for use in bins + * whose size exceeds a threshold. + * + * TreeBins use a special form of comparison for search and + * related operations (which is the main reason we cannot use + * existing collections such as TreeMaps). TreeBins contain + * Comparable elements, but may contain others, as well as + * elements that are Comparable but not necessarily Comparable + * for the same T, so we cannot invoke compareTo among them. To + * handle this, the tree is ordered primarily by hash value, then + * by getClass().getName() order, and then by Comparator order + * among elements of the same class. On lookup at a node, if + * elements are not comparable or compare as 0, both left and + * right children may need to be searched in the case of tied hash + * values. (This corresponds to the full list search that would be + * necessary if all elements were non-Comparable and had tied + * hashes.) The red-black balancing code is updated from + * pre-jdk-collections + * (http://gee.cs.oswego.edu/dl/classes/collections/RBCell.java) + * based in turn on Cormen, Leiserson, and Rivest "Introduction to + * Algorithms" (CLR). + * + * TreeBins also maintain a separate locking discipline than + * regular bins. Because they are forwarded via special MOVED + * nodes at bin heads (which can never change once established), + * we cannot use those nodes as locks. Instead, TreeBin + * extends AbstractQueuedSynchronizer to support a simple form of + * read-write lock. For update operations and table validation, + * the exclusive form of lock behaves in the same way as bin-head + * locks. However, lookups use shared read-lock mechanics to allow + * multiple readers in the absence of writers. Additionally, + * these lookups do not ever block: While the lock is not + * available, they proceed along the slow traversal path (via + * next-pointers) until the lock becomes available or the list is + * exhausted, whichever comes first. (These cases are not fast, + * but maximize aggregate expected throughput.) The AQS mechanics + * for doing this are straightforward. The lock state is held as + * AQS getState(). Read counts are negative; the write count (1) + * is positive. There are no signalling preferences among readers + * and writers. Since we don't need to export full Lock API, we + * just override the minimal AQS methods and use them directly. + */ + static final class TreeBin extends AbstractQueuedSynchronizer { + private static final long serialVersionUID = 2249069246763182397L; + transient TreeNode root; // root of tree + transient TreeNode first; // head of next-pointer list + + /* AQS overrides */ + public final boolean isHeldExclusively() { return getState() > 0; } + public final boolean tryAcquire(int ignore) { + if (compareAndSetState(0, 1)) { + setExclusiveOwnerThread(Thread.currentThread()); + return true; + } + return false; + } + public final boolean tryRelease(int ignore) { + setExclusiveOwnerThread(null); + setState(0); + return true; + } + public final int tryAcquireShared(int ignore) { + for (int c;;) { + if ((c = getState()) > 0) + return -1; + if (compareAndSetState(c, c -1)) + return 1; + } + } + public final boolean tryReleaseShared(int ignore) { + int c; + do {} while (!compareAndSetState(c = getState(), c + 1)); + return c == -1; + } + + /** From CLR */ + private void rotateLeft(TreeNode p) { + if (p != null) { + TreeNode r = p.right, pp, rl; + if ((rl = p.right = r.left) != null) + rl.parent = p; + if ((pp = r.parent = p.parent) == null) + root = r; + else if (pp.left == p) + pp.left = r; + else + pp.right = r; + r.left = p; + p.parent = r; + } + } + + /** From CLR */ + private void rotateRight(TreeNode p) { + if (p != null) { + TreeNode l = p.left, pp, lr; + if ((lr = p.left = l.right) != null) + lr.parent = p; + if ((pp = l.parent = p.parent) == null) + root = l; + else if (pp.right == p) + pp.right = l; + else + pp.left = l; + l.right = p; + p.parent = l; + } + } + + @SuppressWarnings("unchecked") final TreeNode getTreeNode + (int h, Object k, TreeNode p) { + return getTreeNode(h, (RubyObject)k, p); + } + + /** + * Returns the TreeNode (or null if not found) for the given key + * starting at given root. + */ + @SuppressWarnings("unchecked") final TreeNode getTreeNode + (int h, RubyObject k, TreeNode p) { + RubyClass c = k.getMetaClass(); boolean kNotComparable = !k.respondsTo("<=>"); + while (p != null) { + int dir, ph; RubyObject pk; RubyClass pc; + if ((ph = p.hash) == h) { + if ((pk = (RubyObject)p.key) == k || k.equals(pk)) + return p; + if (c != (pc = (RubyClass)pk.getMetaClass()) || + kNotComparable || + (dir = rubyCompare(k, pk)) == 0) { + dir = (c == pc) ? 0 : c.getName().compareTo(pc.getName()); + if (dir == 0) { // if still stuck, need to check both sides + TreeNode r = null, pl, pr; + // try to recurse on the right + if ((pr = p.right) != null && h >= pr.hash && (r = getTreeNode(h, k, pr)) != null) + return r; + // try to continue iterating on the left side + else if ((pl = p.left) != null && h <= pl.hash) + dir = -1; + else // no matching node found + return null; + } + } + } + else + dir = (h < ph) ? -1 : 1; + p = (dir > 0) ? p.right : p.left; + } + return null; + } + + int rubyCompare(RubyObject l, RubyObject r) { + ThreadContext context = l.getMetaClass().getRuntime().getCurrentContext(); + IRubyObject result; + try { + result = l.callMethod(context, "<=>", r); + } catch (RaiseException e) { + // handle objects "lying" about responding to <=>, ie: an Array containing non-comparable keys + if (context.runtime.getNoMethodError().isInstance(e.getException())) { + return 0; + } + throw e; + } + + return result.isNil() ? 0 : RubyNumeric.num2int(result.convertToInteger()); + } + + /** + * Wrapper for getTreeNode used by CHM.get. Tries to obtain + * read-lock to call getTreeNode, but during failure to get + * lock, searches along next links. + */ + final Object getValue(int h, Object k) { + Node r = null; + int c = getState(); // Must read lock state first + for (Node e = first; e != null; e = e.next) { + if (c <= 0 && compareAndSetState(c, c - 1)) { + try { + r = getTreeNode(h, k, root); + } finally { + releaseShared(0); + } + break; + } + else if ((e.hash & HASH_BITS) == h && k.equals(e.key)) { + r = e; + break; + } + else + c = getState(); + } + return r == null ? null : r.val; + } + + @SuppressWarnings("unchecked") final TreeNode putTreeNode + (int h, Object k, Object v) { + return putTreeNode(h, (RubyObject)k, v); + } + + /** + * Finds or adds a node. + * @return null if added + */ + @SuppressWarnings("unchecked") final TreeNode putTreeNode + (int h, RubyObject k, Object v) { + RubyClass c = k.getMetaClass(); + boolean kNotComparable = !k.respondsTo("<=>"); + TreeNode pp = root, p = null; + int dir = 0; + while (pp != null) { // find existing node or leaf to insert at + int ph; RubyObject pk; RubyClass pc; + p = pp; + if ((ph = p.hash) == h) { + if ((pk = (RubyObject)p.key) == k || k.equals(pk)) + return p; + if (c != (pc = pk.getMetaClass()) || + kNotComparable || + (dir = rubyCompare(k, pk)) == 0) { + dir = (c == pc) ? 0 : c.getName().compareTo(pc.getName()); + if (dir == 0) { // if still stuck, need to check both sides + TreeNode r = null, pr; + // try to recurse on the right + if ((pr = p.right) != null && h >= pr.hash && (r = getTreeNode(h, k, pr)) != null) + return r; + else // continue descending down the left subtree + dir = -1; + } + } + } + else + dir = (h < ph) ? -1 : 1; + pp = (dir > 0) ? p.right : p.left; + } + + TreeNode f = first; + TreeNode x = first = new TreeNode(h, (Object)k, v, f, p); + if (p == null) + root = x; + else { // attach and rebalance; adapted from CLR + TreeNode xp, xpp; + if (f != null) + f.prev = x; + if (dir <= 0) + p.left = x; + else + p.right = x; + x.red = true; + while (x != null && (xp = x.parent) != null && xp.red && + (xpp = xp.parent) != null) { + TreeNode xppl = xpp.left; + if (xp == xppl) { + TreeNode y = xpp.right; + if (y != null && y.red) { + y.red = false; + xp.red = false; + xpp.red = true; + x = xpp; + } + else { + if (x == xp.right) { + rotateLeft(x = xp); + xpp = (xp = x.parent) == null ? null : xp.parent; + } + if (xp != null) { + xp.red = false; + if (xpp != null) { + xpp.red = true; + rotateRight(xpp); + } + } + } + } + else { + TreeNode y = xppl; + if (y != null && y.red) { + y.red = false; + xp.red = false; + xpp.red = true; + x = xpp; + } + else { + if (x == xp.left) { + rotateRight(x = xp); + xpp = (xp = x.parent) == null ? null : xp.parent; + } + if (xp != null) { + xp.red = false; + if (xpp != null) { + xpp.red = true; + rotateLeft(xpp); + } + } + } + } + } + TreeNode r = root; + if (r != null && r.red) + r.red = false; + } + return null; + } + + /** + * Removes the given node, that must be present before this + * call. This is messier than typical red-black deletion code + * because we cannot swap the contents of an interior node + * with a leaf successor that is pinned by "next" pointers + * that are accessible independently of lock. So instead we + * swap the tree linkages. + */ + final void deleteTreeNode(TreeNode p) { + TreeNode next = (TreeNode)p.next; // unlink traversal pointers + TreeNode pred = p.prev; + if (pred == null) + first = next; + else + pred.next = next; + if (next != null) + next.prev = pred; + TreeNode replacement; + TreeNode pl = p.left; + TreeNode pr = p.right; + if (pl != null && pr != null) { + TreeNode s = pr, sl; + while ((sl = s.left) != null) // find successor + s = sl; + boolean c = s.red; s.red = p.red; p.red = c; // swap colors + TreeNode sr = s.right; + TreeNode pp = p.parent; + if (s == pr) { // p was s's direct parent + p.parent = s; + s.right = p; + } + else { + TreeNode sp = s.parent; + if ((p.parent = sp) != null) { + if (s == sp.left) + sp.left = p; + else + sp.right = p; + } + if ((s.right = pr) != null) + pr.parent = s; + } + p.left = null; + if ((p.right = sr) != null) + sr.parent = p; + if ((s.left = pl) != null) + pl.parent = s; + if ((s.parent = pp) == null) + root = s; + else if (p == pp.left) + pp.left = s; + else + pp.right = s; + replacement = sr; + } + else + replacement = (pl != null) ? pl : pr; + TreeNode pp = p.parent; + if (replacement == null) { + if (pp == null) { + root = null; + return; + } + replacement = p; + } + else { + replacement.parent = pp; + if (pp == null) + root = replacement; + else if (p == pp.left) + pp.left = replacement; + else + pp.right = replacement; + p.left = p.right = p.parent = null; + } + if (!p.red) { // rebalance, from CLR + TreeNode x = replacement; + while (x != null) { + TreeNode xp, xpl; + if (x.red || (xp = x.parent) == null) { + x.red = false; + break; + } + if (x == (xpl = xp.left)) { + TreeNode sib = xp.right; + if (sib != null && sib.red) { + sib.red = false; + xp.red = true; + rotateLeft(xp); + sib = (xp = x.parent) == null ? null : xp.right; + } + if (sib == null) + x = xp; + else { + TreeNode sl = sib.left, sr = sib.right; + if ((sr == null || !sr.red) && + (sl == null || !sl.red)) { + sib.red = true; + x = xp; + } + else { + if (sr == null || !sr.red) { + if (sl != null) + sl.red = false; + sib.red = true; + rotateRight(sib); + sib = (xp = x.parent) == null ? null : xp.right; + } + if (sib != null) { + sib.red = (xp == null) ? false : xp.red; + if ((sr = sib.right) != null) + sr.red = false; + } + if (xp != null) { + xp.red = false; + rotateLeft(xp); + } + x = root; + } + } + } + else { // symmetric + TreeNode sib = xpl; + if (sib != null && sib.red) { + sib.red = false; + xp.red = true; + rotateRight(xp); + sib = (xp = x.parent) == null ? null : xp.left; + } + if (sib == null) + x = xp; + else { + TreeNode sl = sib.left, sr = sib.right; + if ((sl == null || !sl.red) && + (sr == null || !sr.red)) { + sib.red = true; + x = xp; + } + else { + if (sl == null || !sl.red) { + if (sr != null) + sr.red = false; + sib.red = true; + rotateLeft(sib); + sib = (xp = x.parent) == null ? null : xp.left; + } + if (sib != null) { + sib.red = (xp == null) ? false : xp.red; + if ((sl = sib.left) != null) + sl.red = false; + } + if (xp != null) { + xp.red = false; + rotateRight(xp); + } + x = root; + } + } + } + } + } + if (p == replacement && (pp = p.parent) != null) { + if (p == pp.left) // detach pointers + pp.left = null; + else if (p == pp.right) + pp.right = null; + p.parent = null; + } + } + } + + /* ---------------- Collision reduction methods -------------- */ + + /** + * Spreads higher bits to lower, and also forces top 2 bits to 0. + * Because the table uses power-of-two masking, sets of hashes + * that vary only in bits above the current mask will always + * collide. (Among known examples are sets of Float keys holding + * consecutive whole numbers in small tables.) To counter this, + * we apply a transform that spreads the impact of higher bits + * downward. There is a tradeoff between speed, utility, and + * quality of bit-spreading. Because many common sets of hashes + * are already reasonably distributed across bits (so don't benefit + * from spreading), and because we use trees to handle large sets + * of collisions in bins, we don't need excessively high quality. + */ + private static final int spread(int h) { + h ^= (h >>> 18) ^ (h >>> 12); + return (h ^ (h >>> 10)) & HASH_BITS; + } + + /** + * Replaces a list bin with a tree bin. Call only when locked. + * Fails to replace if the given key is non-comparable or table + * is, or needs, resizing. + */ + private final void replaceWithTreeBin(AtomicReferenceArray tab, int index, Object key) { + if ((key instanceof Comparable) && + (tab.length() >= MAXIMUM_CAPACITY || counter.sum() < (long)sizeCtl)) { + TreeBin t = new TreeBin(); + for (Node e = tabAt(tab, index); e != null; e = e.next) + t.putTreeNode(e.hash & HASH_BITS, e.key, e.val); + setTabAt(tab, index, new Node(MOVED, t, null, null)); + } + } + + /* ---------------- Internal access and update methods -------------- */ + + /** Implementation for get and containsKey */ + private final Object internalGet(Object k) { + int h = spread(k.hashCode()); + retry: for (AtomicReferenceArray tab = table; tab != null;) { + Node e, p; Object ek, ev; int eh; // locals to read fields once + for (e = tabAt(tab, (tab.length() - 1) & h); e != null; e = e.next) { + if ((eh = e.hash) == MOVED) { + if ((ek = e.key) instanceof TreeBin) // search TreeBin + return ((TreeBin)ek).getValue(h, k); + else { // restart with new table + tab = (AtomicReferenceArray)ek; + continue retry; + } + } + else if ((eh & HASH_BITS) == h && (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) + return ev; + } + break; + } + return null; + } + + /** + * Implementation for the four public remove/replace methods: + * Replaces node value with v, conditional upon match of cv if + * non-null. If resulting value is null, delete. + */ + private final Object internalReplace(Object k, Object v, Object cv) { + int h = spread(k.hashCode()); + Object oldVal = null; + for (AtomicReferenceArray tab = table;;) { + Node f; int i, fh; Object fk; + if (tab == null || + (f = tabAt(tab, i = (tab.length() - 1) & h)) == null) + break; + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + boolean validated = false; + boolean deleted = false; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + validated = true; + TreeNode p = t.getTreeNode(h, k, t.root); + if (p != null) { + Object pv = p.val; + if (cv == null || cv == pv || cv.equals(pv)) { + oldVal = pv; + if ((p.val = v) == null) { + deleted = true; + t.deleteTreeNode(p); + } + } + } + } + } finally { + t.release(0); + } + if (validated) { + if (deleted) + counter.add(-1L); + break; + } + } + else + tab = (AtomicReferenceArray)fk; + } + else if ((fh & HASH_BITS) != h && f.next == null) // precheck + break; // rules out possible existence + else if ((fh & LOCKED) != 0) { + checkForResize(); // try resizing if can't get lock + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + boolean validated = false; + boolean deleted = false; + try { + if (tabAt(tab, i) == f) { + validated = true; + for (Node e = f, pred = null;;) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + ((ev = e.val) != null) && + ((ek = e.key) == k || k.equals(ek))) { + if (cv == null || cv == ev || cv.equals(ev)) { + oldVal = ev; + if ((e.val = v) == null) { + deleted = true; + Node en = e.next; + if (pred != null) + pred.next = en; + else + setTabAt(tab, i, en); + } + } + break; + } + pred = e; + if ((e = e.next) == null) + break; + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (validated) { + if (deleted) + counter.add(-1L); + break; + } + } + } + return oldVal; + } + + /* + * Internal versions of the six insertion methods, each a + * little more complicated than the last. All have + * the same basic structure as the first (internalPut): + * 1. If table uninitialized, create + * 2. If bin empty, try to CAS new node + * 3. If bin stale, use new table + * 4. if bin converted to TreeBin, validate and relay to TreeBin methods + * 5. Lock and validate; if valid, scan and add or update + * + * The others interweave other checks and/or alternative actions: + * * Plain put checks for and performs resize after insertion. + * * putIfAbsent prescans for mapping without lock (and fails to add + * if present), which also makes pre-emptive resize checks worthwhile. + * * computeIfAbsent extends form used in putIfAbsent with additional + * mechanics to deal with, calls, potential exceptions and null + * returns from function call. + * * compute uses the same function-call mechanics, but without + * the prescans + * * merge acts as putIfAbsent in the absent case, but invokes the + * update function if present + * * putAll attempts to pre-allocate enough table space + * and more lazily performs count updates and checks. + * + * Someday when details settle down a bit more, it might be worth + * some factoring to reduce sprawl. + */ + + /** Implementation for put */ + private final Object internalPut(Object k, Object v) { + int h = spread(k.hashCode()); + int count = 0; + for (AtomicReferenceArray tab = table;;) { + int i; Node f; int fh; Object fk; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null) { + if (casTabAt(tab, i, null, new Node(h, k, v, null))) + break; // no lock when adding to empty bin + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + Object oldVal = null; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + count = 2; + TreeNode p = t.putTreeNode(h, k, v); + if (p != null) { + oldVal = p.val; + p.val = v; + } + } + } finally { + t.release(0); + } + if (count != 0) { + if (oldVal != null) + return oldVal; + break; + } + } + else + tab = (AtomicReferenceArray)fk; + } + else if ((fh & LOCKED) != 0) { + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + Object oldVal = null; + try { // needed in case equals() throws + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + oldVal = ev; + e.val = v; + break; + } + Node last = e; + if ((e = e.next) == null) { + last.next = new Node(h, k, v, null); + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + break; + } + } + } + } finally { // unlock and signal if needed + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (oldVal != null) + return oldVal; + if (tab.length() <= 64) + count = 2; + break; + } + } + } + counter.add(1L); + if (count > 1) + checkForResize(); + return null; + } + + /** Implementation for putIfAbsent */ + private final Object internalPutIfAbsent(Object k, Object v) { + int h = spread(k.hashCode()); + int count = 0; + for (AtomicReferenceArray tab = table;;) { + int i; Node f; int fh; Object fk, fv; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null) { + if (casTabAt(tab, i, null, new Node(h, k, v, null))) + break; + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + Object oldVal = null; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + count = 2; + TreeNode p = t.putTreeNode(h, k, v); + if (p != null) + oldVal = p.val; + } + } finally { + t.release(0); + } + if (count != 0) { + if (oldVal != null) + return oldVal; + break; + } + } + else + tab = (AtomicReferenceArray)fk; + } + else if ((fh & HASH_BITS) == h && (fv = f.val) != null && + ((fk = f.key) == k || k.equals(fk))) + return fv; + else { + Node g = f.next; + if (g != null) { // at least 2 nodes -- search and maybe resize + for (Node e = g;;) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) + return ev; + if ((e = e.next) == null) { + checkForResize(); + break; + } + } + } + if (((fh = f.hash) & LOCKED) != 0) { + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (tabAt(tab, i) == f && f.casHash(fh, fh | LOCKED)) { + Object oldVal = null; + try { + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + oldVal = ev; + break; + } + Node last = e; + if ((e = e.next) == null) { + last.next = new Node(h, k, v, null); + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + break; + } + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (oldVal != null) + return oldVal; + if (tab.length() <= 64) + count = 2; + break; + } + } + } + } + counter.add(1L); + if (count > 1) + checkForResize(); + return null; + } + + /** Implementation for computeIfAbsent */ + private final Object internalComputeIfAbsent(K k, + Fun mf) { + int h = spread(k.hashCode()); + Object val = null; + int count = 0; + for (AtomicReferenceArray tab = table;;) { + Node f; int i, fh; Object fk, fv; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null) { + Node node = new Node(fh = h | LOCKED, k, null, null); + if (casTabAt(tab, i, null, node)) { + count = 1; + try { + if ((val = mf.apply(k)) != null) + node.val = val; + } finally { + if (val == null) + setTabAt(tab, i, null); + if (!node.casHash(fh, h)) { + node.hash = h; + synchronized (node) { node.notifyAll(); }; + } + } + } + if (count != 0) + break; + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + boolean added = false; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + count = 1; + TreeNode p = t.getTreeNode(h, k, t.root); + if (p != null) + val = p.val; + else if ((val = mf.apply(k)) != null) { + added = true; + count = 2; + t.putTreeNode(h, k, val); + } + } + } finally { + t.release(0); + } + if (count != 0) { + if (!added) + return val; + break; + } + } + else + tab = (AtomicReferenceArray)fk; + } + else if ((fh & HASH_BITS) == h && (fv = f.val) != null && + ((fk = f.key) == k || k.equals(fk))) + return fv; + else { + Node g = f.next; + if (g != null) { + for (Node e = g;;) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) + return ev; + if ((e = e.next) == null) { + checkForResize(); + break; + } + } + } + if (((fh = f.hash) & LOCKED) != 0) { + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (tabAt(tab, i) == f && f.casHash(fh, fh | LOCKED)) { + boolean added = false; + try { + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + val = ev; + break; + } + Node last = e; + if ((e = e.next) == null) { + if ((val = mf.apply(k)) != null) { + added = true; + last.next = new Node(h, k, val, null); + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + } + break; + } + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (!added) + return val; + if (tab.length() <= 64) + count = 2; + break; + } + } + } + } + if (val != null) { + counter.add(1L); + if (count > 1) + checkForResize(); + } + return val; + } + + /** Implementation for compute */ + @SuppressWarnings("unchecked") private final Object internalCompute + (K k, boolean onlyIfPresent, BiFun mf) { + int h = spread(k.hashCode()); + Object val = null; + int delta = 0; + int count = 0; + for (AtomicReferenceArray tab = table;;) { + Node f; int i, fh; Object fk; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null) { + if (onlyIfPresent) + break; + Node node = new Node(fh = h | LOCKED, k, null, null); + if (casTabAt(tab, i, null, node)) { + try { + count = 1; + if ((val = mf.apply(k, null)) != null) { + node.val = val; + delta = 1; + } + } finally { + if (delta == 0) + setTabAt(tab, i, null); + if (!node.casHash(fh, h)) { + node.hash = h; + synchronized (node) { node.notifyAll(); }; + } + } + } + if (count != 0) + break; + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + count = 1; + TreeNode p = t.getTreeNode(h, k, t.root); + Object pv; + if (p == null) { + if (onlyIfPresent) + break; + pv = null; + } else + pv = p.val; + if ((val = mf.apply(k, (V)pv)) != null) { + if (p != null) + p.val = val; + else { + count = 2; + delta = 1; + t.putTreeNode(h, k, val); + } + } + else if (p != null) { + delta = -1; + t.deleteTreeNode(p); + } + } + } finally { + t.release(0); + } + if (count != 0) + break; + } + else + tab = (AtomicReferenceArray)fk; + } + else if ((fh & LOCKED) != 0) { + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + try { + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f, pred = null;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + val = mf.apply(k, (V)ev); + if (val != null) + e.val = val; + else { + delta = -1; + Node en = e.next; + if (pred != null) + pred.next = en; + else + setTabAt(tab, i, en); + } + break; + } + pred = e; + if ((e = e.next) == null) { + if (!onlyIfPresent && (val = mf.apply(k, null)) != null) { + pred.next = new Node(h, k, val, null); + delta = 1; + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + } + break; + } + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (tab.length() <= 64) + count = 2; + break; + } + } + } + if (delta != 0) { + counter.add((long)delta); + if (count > 1) + checkForResize(); + } + return val; + } + + /** Implementation for merge */ + @SuppressWarnings("unchecked") private final Object internalMerge + (K k, V v, BiFun mf) { + int h = spread(k.hashCode()); + Object val = null; + int delta = 0; + int count = 0; + for (AtomicReferenceArray tab = table;;) { + int i; Node f; int fh; Object fk, fv; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null) { + if (casTabAt(tab, i, null, new Node(h, k, v, null))) { + delta = 1; + val = v; + break; + } + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + count = 1; + TreeNode p = t.getTreeNode(h, k, t.root); + val = (p == null) ? v : mf.apply((V)p.val, v); + if (val != null) { + if (p != null) + p.val = val; + else { + count = 2; + delta = 1; + t.putTreeNode(h, k, val); + } + } + else if (p != null) { + delta = -1; + t.deleteTreeNode(p); + } + } + } finally { + t.release(0); + } + if (count != 0) + break; + } + else + tab = (AtomicReferenceArray)fk; + } + else if ((fh & LOCKED) != 0) { + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + try { + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f, pred = null;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + val = mf.apply((V)ev, v); + if (val != null) + e.val = val; + else { + delta = -1; + Node en = e.next; + if (pred != null) + pred.next = en; + else + setTabAt(tab, i, en); + } + break; + } + pred = e; + if ((e = e.next) == null) { + val = v; + pred.next = new Node(h, k, val, null); + delta = 1; + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + break; + } + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (tab.length() <= 64) + count = 2; + break; + } + } + } + if (delta != 0) { + counter.add((long)delta); + if (count > 1) + checkForResize(); + } + return val; + } + + /** Implementation for putAll */ + private final void internalPutAll(Map m) { + tryPresize(m.size()); + long delta = 0L; // number of uncommitted additions + boolean npe = false; // to throw exception on exit for nulls + try { // to clean up counts on other exceptions + for (Map.Entry entry : m.entrySet()) { + Object k, v; + if (entry == null || (k = entry.getKey()) == null || + (v = entry.getValue()) == null) { + npe = true; + break; + } + int h = spread(k.hashCode()); + for (AtomicReferenceArray tab = table;;) { + int i; Node f; int fh; Object fk; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null){ + if (casTabAt(tab, i, null, new Node(h, k, v, null))) { + ++delta; + break; + } + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + boolean validated = false; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + validated = true; + TreeNode p = t.getTreeNode(h, k, t.root); + if (p != null) + p.val = v; + else { + t.putTreeNode(h, k, v); + ++delta; + } + } + } finally { + t.release(0); + } + if (validated) + break; + } + else + tab = (AtomicReferenceArray)fk; + } + else if ((fh & LOCKED) != 0) { + counter.add(delta); + delta = 0L; + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + int count = 0; + try { + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + e.val = v; + break; + } + Node last = e; + if ((e = e.next) == null) { + ++delta; + last.next = new Node(h, k, v, null); + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + break; + } + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (count > 1) { + counter.add(delta); + delta = 0L; + checkForResize(); + } + break; + } + } + } + } + } finally { + if (delta != 0) + counter.add(delta); + } + if (npe) + throw new NullPointerException(); + } + + /* ---------------- Table Initialization and Resizing -------------- */ + + /** + * Returns a power of two table size for the given desired capacity. + * See Hackers Delight, sec 3.2 + */ + private static final int tableSizeFor(int c) { + int n = c - 1; + n |= n >>> 1; + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; + } + + /** + * Initializes table, using the size recorded in sizeCtl. + */ + private final AtomicReferenceArray initTable() { + AtomicReferenceArray tab; int sc; + while ((tab = table) == null) { + if ((sc = sizeCtl) < 0) + Thread.yield(); // lost initialization race; just spin + else if (SIZE_CTRL_UPDATER.compareAndSet(this, sc, -1)) { + try { + if ((tab = table) == null) { + int n = (sc > 0) ? sc : DEFAULT_CAPACITY; + tab = table = new AtomicReferenceArray(n); + sc = n - (n >>> 2); + } + } finally { + sizeCtl = sc; + } + break; + } + } + return tab; + } + + /** + * If table is too small and not already resizing, creates next + * table and transfers bins. Rechecks occupancy after a transfer + * to see if another resize is already needed because resizings + * are lagging additions. + */ + private final void checkForResize() { + AtomicReferenceArray tab; int n, sc; + while ((tab = table) != null && + (n = tab.length()) < MAXIMUM_CAPACITY && + (sc = sizeCtl) >= 0 && counter.sum() >= (long)sc && + SIZE_CTRL_UPDATER.compareAndSet(this, sc, -1)) { + try { + if (tab == table) { + table = rebuild(tab); + sc = (n << 1) - (n >>> 1); + } + } finally { + sizeCtl = sc; + } + } + } + + /** + * Tries to presize table to accommodate the given number of elements. + * + * @param size number of elements (doesn't need to be perfectly accurate) + */ + private final void tryPresize(int size) { + int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY : + tableSizeFor(size + (size >>> 1) + 1); + int sc; + while ((sc = sizeCtl) >= 0) { + AtomicReferenceArray tab = table; int n; + if (tab == null || (n = tab.length()) == 0) { + n = (sc > c) ? sc : c; + if (SIZE_CTRL_UPDATER.compareAndSet(this, sc, -1)) { + try { + if (table == tab) { + table = new AtomicReferenceArray(n); + sc = n - (n >>> 2); + } + } finally { + sizeCtl = sc; + } + } + } + else if (c <= sc || n >= MAXIMUM_CAPACITY) + break; + else if (SIZE_CTRL_UPDATER.compareAndSet(this, sc, -1)) { + try { + if (table == tab) { + table = rebuild(tab); + sc = (n << 1) - (n >>> 1); + } + } finally { + sizeCtl = sc; + } + } + } + } + + /* + * Moves and/or copies the nodes in each bin to new table. See + * above for explanation. + * + * @return the new table + */ + private static final AtomicReferenceArray rebuild(AtomicReferenceArray tab) { + int n = tab.length(); + AtomicReferenceArray nextTab = new AtomicReferenceArray(n << 1); + Node fwd = new Node(MOVED, nextTab, null, null); + int[] buffer = null; // holds bins to revisit; null until needed + Node rev = null; // reverse forwarder; null until needed + int nbuffered = 0; // the number of bins in buffer list + int bufferIndex = 0; // buffer index of current buffered bin + int bin = n - 1; // current non-buffered bin or -1 if none + + for (int i = bin;;) { // start upwards sweep + int fh; Node f; + if ((f = tabAt(tab, i)) == null) { + if (bin >= 0) { // Unbuffered; no lock needed (or available) + if (!casTabAt(tab, i, f, fwd)) + continue; + } + else { // transiently use a locked forwarding node + Node g = new Node(MOVED|LOCKED, nextTab, null, null); + if (!casTabAt(tab, i, f, g)) + continue; + setTabAt(nextTab, i, null); + setTabAt(nextTab, i + n, null); + setTabAt(tab, i, fwd); + if (!g.casHash(MOVED|LOCKED, MOVED)) { + g.hash = MOVED; + synchronized (g) { g.notifyAll(); } + } + } + } + else if ((fh = f.hash) == MOVED) { + Object fk = f.key; + if (fk instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + boolean validated = false; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + validated = true; + splitTreeBin(nextTab, i, t); + setTabAt(tab, i, fwd); + } + } finally { + t.release(0); + } + if (!validated) + continue; + } + } + else if ((fh & LOCKED) == 0 && f.casHash(fh, fh|LOCKED)) { + boolean validated = false; + try { // split to lo and hi lists; copying as needed + if (tabAt(tab, i) == f) { + validated = true; + splitBin(nextTab, i, f); + setTabAt(tab, i, fwd); + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (!validated) + continue; + } + else { + if (buffer == null) // initialize buffer for revisits + buffer = new int[TRANSFER_BUFFER_SIZE]; + if (bin < 0 && bufferIndex > 0) { + int j = buffer[--bufferIndex]; + buffer[bufferIndex] = i; + i = j; // swap with another bin + continue; + } + if (bin < 0 || nbuffered >= TRANSFER_BUFFER_SIZE) { + f.tryAwaitLock(tab, i); + continue; // no other options -- block + } + if (rev == null) // initialize reverse-forwarder + rev = new Node(MOVED, tab, null, null); + if (tabAt(tab, i) != f || (f.hash & LOCKED) == 0) + continue; // recheck before adding to list + buffer[nbuffered++] = i; + setTabAt(nextTab, i, rev); // install place-holders + setTabAt(nextTab, i + n, rev); + } + + if (bin > 0) + i = --bin; + else if (buffer != null && nbuffered > 0) { + bin = -1; + i = buffer[bufferIndex = --nbuffered]; + } + else + return nextTab; + } + } + + /** + * Splits a normal bin with list headed by e into lo and hi parts; + * installs in given table. + */ + private static void splitBin(AtomicReferenceArray nextTab, int i, Node e) { + int bit = nextTab.length() >>> 1; // bit to split on + int runBit = e.hash & bit; + Node lastRun = e, lo = null, hi = null; + for (Node p = e.next; p != null; p = p.next) { + int b = p.hash & bit; + if (b != runBit) { + runBit = b; + lastRun = p; + } + } + if (runBit == 0) + lo = lastRun; + else + hi = lastRun; + for (Node p = e; p != lastRun; p = p.next) { + int ph = p.hash & HASH_BITS; + Object pk = p.key, pv = p.val; + if ((ph & bit) == 0) + lo = new Node(ph, pk, pv, lo); + else + hi = new Node(ph, pk, pv, hi); + } + setTabAt(nextTab, i, lo); + setTabAt(nextTab, i + bit, hi); + } + + /** + * Splits a tree bin into lo and hi parts; installs in given table. + */ + private static void splitTreeBin(AtomicReferenceArray nextTab, int i, TreeBin t) { + int bit = nextTab.length() >>> 1; + TreeBin lt = new TreeBin(); + TreeBin ht = new TreeBin(); + int lc = 0, hc = 0; + for (Node e = t.first; e != null; e = e.next) { + int h = e.hash & HASH_BITS; + Object k = e.key, v = e.val; + if ((h & bit) == 0) { + ++lc; + lt.putTreeNode(h, k, v); + } + else { + ++hc; + ht.putTreeNode(h, k, v); + } + } + Node ln, hn; // throw away trees if too small + if (lc <= (TREE_THRESHOLD >>> 1)) { + ln = null; + for (Node p = lt.first; p != null; p = p.next) + ln = new Node(p.hash, p.key, p.val, ln); + } + else + ln = new Node(MOVED, lt, null, null); + setTabAt(nextTab, i, ln); + if (hc <= (TREE_THRESHOLD >>> 1)) { + hn = null; + for (Node p = ht.first; p != null; p = p.next) + hn = new Node(p.hash, p.key, p.val, hn); + } + else + hn = new Node(MOVED, ht, null, null); + setTabAt(nextTab, i + bit, hn); + } + + /** + * Implementation for clear. Steps through each bin, removing all + * nodes. + */ + private final void internalClear() { + long delta = 0L; // negative number of deletions + int i = 0; + AtomicReferenceArray tab = table; + while (tab != null && i < tab.length()) { + int fh; Object fk; + Node f = tabAt(tab, i); + if (f == null) + ++i; + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + for (Node p = t.first; p != null; p = p.next) { + if (p.val != null) { // (currently always true) + p.val = null; + --delta; + } + } + t.first = null; + t.root = null; + ++i; + } + } finally { + t.release(0); + } + } + else + tab = (AtomicReferenceArray)fk; + } + else if ((fh & LOCKED) != 0) { + counter.add(delta); // opportunistically update count + delta = 0L; + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + try { + if (tabAt(tab, i) == f) { + for (Node e = f; e != null; e = e.next) { + if (e.val != null) { // (currently always true) + e.val = null; + --delta; + } + } + setTabAt(tab, i, null); + ++i; + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + } + } + if (delta != 0) + counter.add(delta); + } + + /* ----------------Table Traversal -------------- */ + + /** + * Encapsulates traversal for methods such as containsValue; also + * serves as a base class for other iterators and bulk tasks. + * + * At each step, the iterator snapshots the key ("nextKey") and + * value ("nextVal") of a valid node (i.e., one that, at point of + * snapshot, has a non-null user value). Because val fields can + * change (including to null, indicating deletion), field nextVal + * might not be accurate at point of use, but still maintains the + * weak consistency property of holding a value that was once + * valid. To support iterator.remove, the nextKey field is not + * updated (nulled out) when the iterator cannot advance. + * + * Internal traversals directly access these fields, as in: + * {@code while (it.advance() != null) { process(it.nextKey); }} + * + * Exported iterators must track whether the iterator has advanced + * (in hasNext vs next) (by setting/checking/nulling field + * nextVal), and then extract key, value, or key-value pairs as + * return values of next(). + * + * The iterator visits once each still-valid node that was + * reachable upon iterator construction. It might miss some that + * were added to a bin after the bin was visited, which is OK wrt + * consistency guarantees. Maintaining this property in the face + * of possible ongoing resizes requires a fair amount of + * bookkeeping state that is difficult to optimize away amidst + * volatile accesses. Even so, traversal maintains reasonable + * throughput. + * + * Normally, iteration proceeds bin-by-bin traversing lists. + * However, if the table has been resized, then all future steps + * must traverse both the bin at the current index as well as at + * (index + baseSize); and so on for further resizings. To + * paranoically cope with potential sharing by users of iterators + * across threads, iteration terminates if a bounds checks fails + * for a table read. + * + * This class extends ForkJoinTask to streamline parallel + * iteration in bulk operations (see BulkTask). This adds only an + * int of space overhead, which is close enough to negligible in + * cases where it is not needed to not worry about it. Because + * ForkJoinTask is Serializable, but iterators need not be, we + * need to add warning suppressions. + */ + @SuppressWarnings("serial") static class Traverser { + final ConcurrentHashMapV8 map; + Node next; // the next entry to use + K nextKey; // cached key field of next + V nextVal; // cached val field of next + AtomicReferenceArray tab; // current table; updated if resized + int index; // index of bin to use next + int baseIndex; // current index of initial table + int baseLimit; // index bound for initial table + int baseSize; // initial table size + + /** Creates iterator for all entries in the table. */ + Traverser(ConcurrentHashMapV8 map) { + this.map = map; + } + + /** Creates iterator for split() methods */ + Traverser(Traverser it) { + ConcurrentHashMapV8 m; AtomicReferenceArray t; + if ((m = this.map = it.map) == null) + t = null; + else if ((t = it.tab) == null && // force parent tab initialization + (t = it.tab = m.table) != null) + it.baseLimit = it.baseSize = t.length(); + this.tab = t; + this.baseSize = it.baseSize; + it.baseLimit = this.index = this.baseIndex = + ((this.baseLimit = it.baseLimit) + it.baseIndex + 1) >>> 1; + } + + /** + * Advances next; returns nextVal or null if terminated. + * See above for explanation. + */ + final V advance() { + Node e = next; + V ev = null; + outer: do { + if (e != null) // advance past used/skipped node + e = e.next; + while (e == null) { // get to next non-null bin + ConcurrentHashMapV8 m; + AtomicReferenceArray t; int b, i, n; Object ek; // checks must use locals + if ((t = tab) != null) + n = t.length(); + else if ((m = map) != null && (t = tab = m.table) != null) + n = baseLimit = baseSize = t.length(); + else + break outer; + if ((b = baseIndex) >= baseLimit || + (i = index) < 0 || i >= n) + break outer; + if ((e = tabAt(t, i)) != null && e.hash == MOVED) { + if ((ek = e.key) instanceof TreeBin) + e = ((TreeBin)ek).first; + else { + tab = (AtomicReferenceArray)ek; + continue; // restarts due to null val + } + } // visit upper slots if present + index = (i += baseSize) < n ? i : (baseIndex = b + 1); + } + nextKey = (K) e.key; + } while ((ev = (V) e.val) == null); // skip deleted or special nodes + next = e; + return nextVal = ev; + } + + public final void remove() { + Object k = nextKey; + if (k == null && (advance() == null || (k = nextKey) == null)) + throw new IllegalStateException(); + map.internalReplace(k, null, null); + } + + public final boolean hasNext() { + return nextVal != null || advance() != null; + } + + public final boolean hasMoreElements() { return hasNext(); } + public final void setRawResult(Object x) { } + public R getRawResult() { return null; } + public boolean exec() { return true; } + } + + /* ---------------- Public operations -------------- */ + + /** + * Creates a new, empty map with the default initial table size (16). + */ + public ConcurrentHashMapV8() { + this.counter = new LongAdder(); + } + + /** + * Creates a new, empty map with an initial table size + * accommodating the specified number of elements without the need + * to dynamically resize. + * + * @param initialCapacity The implementation performs internal + * sizing to accommodate this many elements. + * @throws IllegalArgumentException if the initial capacity of + * elements is negative + */ + public ConcurrentHashMapV8(int initialCapacity) { + if (initialCapacity < 0) + throw new IllegalArgumentException(); + int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ? + MAXIMUM_CAPACITY : + tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1)); + this.counter = new LongAdder(); + this.sizeCtl = cap; + } + + /** + * Creates a new map with the same mappings as the given map. + * + * @param m the map + */ + public ConcurrentHashMapV8(Map m) { + this.counter = new LongAdder(); + this.sizeCtl = DEFAULT_CAPACITY; + internalPutAll(m); + } + + /** + * Creates a new, empty map with an initial table size based on + * the given number of elements ({@code initialCapacity}) and + * initial table density ({@code loadFactor}). + * + * @param initialCapacity the initial capacity. The implementation + * performs internal sizing to accommodate this many elements, + * given the specified load factor. + * @param loadFactor the load factor (table density) for + * establishing the initial table size + * @throws IllegalArgumentException if the initial capacity of + * elements is negative or the load factor is nonpositive + * + * @since 1.6 + */ + public ConcurrentHashMapV8(int initialCapacity, float loadFactor) { + this(initialCapacity, loadFactor, 1); + } + + /** + * Creates a new, empty map with an initial table size based on + * the given number of elements ({@code initialCapacity}), table + * density ({@code loadFactor}), and number of concurrently + * updating threads ({@code concurrencyLevel}). + * + * @param initialCapacity the initial capacity. The implementation + * performs internal sizing to accommodate this many elements, + * given the specified load factor. + * @param loadFactor the load factor (table density) for + * establishing the initial table size + * @param concurrencyLevel the estimated number of concurrently + * updating threads. The implementation may use this value as + * a sizing hint. + * @throws IllegalArgumentException if the initial capacity is + * negative or the load factor or concurrencyLevel are + * nonpositive + */ + public ConcurrentHashMapV8(int initialCapacity, + float loadFactor, int concurrencyLevel) { + if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0) + throw new IllegalArgumentException(); + if (initialCapacity < concurrencyLevel) // Use at least as many bins + initialCapacity = concurrencyLevel; // as estimated threads + long size = (long)(1.0 + (long)initialCapacity / loadFactor); + int cap = (size >= (long)MAXIMUM_CAPACITY) ? + MAXIMUM_CAPACITY : tableSizeFor((int)size); + this.counter = new LongAdder(); + this.sizeCtl = cap; + } + + /** + * Creates a new {@link Set} backed by a ConcurrentHashMapV8 + * from the given type to {@code Boolean.TRUE}. + * + * @return the new set + */ + public static KeySetView newKeySet() { + return new KeySetView(new ConcurrentHashMapV8(), + Boolean.TRUE); + } + + /** + * Creates a new {@link Set} backed by a ConcurrentHashMapV8 + * from the given type to {@code Boolean.TRUE}. + * + * @param initialCapacity The implementation performs internal + * sizing to accommodate this many elements. + * @throws IllegalArgumentException if the initial capacity of + * elements is negative + * @return the new set + */ + public static KeySetView newKeySet(int initialCapacity) { + return new KeySetView(new ConcurrentHashMapV8(initialCapacity), + Boolean.TRUE); + } + + /** + * {@inheritDoc} + */ + public boolean isEmpty() { + return counter.sum() <= 0L; // ignore transient negative values + } + + /** + * {@inheritDoc} + */ + public int size() { + long n = counter.sum(); + return ((n < 0L) ? 0 : + (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE : + (int)n); + } + + /** + * Returns the number of mappings. This method should be used + * instead of {@link #size} because a ConcurrentHashMapV8 may + * contain more mappings than can be represented as an int. The + * value returned is a snapshot; the actual count may differ if + * there are ongoing concurrent insertions or removals. + * + * @return the number of mappings + */ + public long mappingCount() { + long n = counter.sum(); + return (n < 0L) ? 0L : n; // ignore transient negative values + } + + /** + * Returns the value to which the specified key is mapped, + * or {@code null} if this map contains no mapping for the key. + * + *

More formally, if this map contains a mapping from a key + * {@code k} to a value {@code v} such that {@code key.equals(k)}, + * then this method returns {@code v}; otherwise it returns + * {@code null}. (There can be at most one such mapping.) + * + * @throws NullPointerException if the specified key is null + */ + @SuppressWarnings("unchecked") public V get(Object key) { + if (key == null) + throw new NullPointerException(); + return (V)internalGet(key); + } + + /** + * Returns the value to which the specified key is mapped, + * or the given defaultValue if this map contains no mapping for the key. + * + * @param key the key + * @param defaultValue the value to return if this map contains + * no mapping for the given key + * @return the mapping for the key, if present; else the defaultValue + * @throws NullPointerException if the specified key is null + */ + @SuppressWarnings("unchecked") public V getValueOrDefault(Object key, V defaultValue) { + if (key == null) + throw new NullPointerException(); + V v = (V) internalGet(key); + return v == null ? defaultValue : v; + } + + /** + * Tests if the specified object is a key in this table. + * + * @param key possible key + * @return {@code true} if and only if the specified object + * is a key in this table, as determined by the + * {@code equals} method; {@code false} otherwise + * @throws NullPointerException if the specified key is null + */ + public boolean containsKey(Object key) { + if (key == null) + throw new NullPointerException(); + return internalGet(key) != null; + } + + /** + * Returns {@code true} if this map maps one or more keys to the + * specified value. Note: This method may require a full traversal + * of the map, and is much slower than method {@code containsKey}. + * + * @param value value whose presence in this map is to be tested + * @return {@code true} if this map maps one or more keys to the + * specified value + * @throws NullPointerException if the specified value is null + */ + public boolean containsValue(Object value) { + if (value == null) + throw new NullPointerException(); + Object v; + Traverser it = new Traverser(this); + while ((v = it.advance()) != null) { + if (v == value || value.equals(v)) + return true; + } + return false; + } + + public K findKey(Object value) { + if (value == null) + throw new NullPointerException(); + Object v; + Traverser it = new Traverser(this); + while ((v = it.advance()) != null) { + if (v == value || value.equals(v)) + return it.nextKey; + } + return null; + } + + /** + * Legacy method testing if some key maps into the specified value + * in this table. This method is identical in functionality to + * {@link #containsValue}, and exists solely to ensure + * full compatibility with class {@link java.util.Hashtable}, + * which supported this method prior to introduction of the + * Java Collections framework. + * + * @param value a value to search for + * @return {@code true} if and only if some key maps to the + * {@code value} argument in this table as + * determined by the {@code equals} method; + * {@code false} otherwise + * @throws NullPointerException if the specified value is null + */ + public boolean contains(Object value) { + return containsValue(value); + } + + /** + * Maps the specified key to the specified value in this table. + * Neither the key nor the value can be null. + * + *

The value can be retrieved by calling the {@code get} method + * with a key that is equal to the original key. + * + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @return the previous value associated with {@code key}, or + * {@code null} if there was no mapping for {@code key} + * @throws NullPointerException if the specified key or value is null + */ + @SuppressWarnings("unchecked") public V put(K key, V value) { + if (key == null || value == null) + throw new NullPointerException(); + return (V)internalPut(key, value); + } + + /** + * {@inheritDoc} + * + * @return the previous value associated with the specified key, + * or {@code null} if there was no mapping for the key + * @throws NullPointerException if the specified key or value is null + */ + @SuppressWarnings("unchecked") public V putIfAbsent(K key, V value) { + if (key == null || value == null) + throw new NullPointerException(); + return (V)internalPutIfAbsent(key, value); + } + + /** + * Copies all of the mappings from the specified map to this one. + * These mappings replace any mappings that this map had for any of the + * keys currently in the specified map. + * + * @param m mappings to be stored in this map + */ + public void putAll(Map m) { + internalPutAll(m); + } + + /** + * If the specified key is not already associated with a value, + * computes its value using the given mappingFunction and enters + * it into the map unless null. This is equivalent to + *

 {@code
+     * if (map.containsKey(key))
+     *   return map.get(key);
+     * value = mappingFunction.apply(key);
+     * if (value != null)
+     *   map.put(key, value);
+     * return value;}
+ * + * except that the action is performed atomically. If the + * function returns {@code null} no mapping is recorded. If the + * function itself throws an (unchecked) exception, the exception + * is rethrown to its caller, and no mapping is recorded. Some + * attempted update operations on this map by other threads may be + * blocked while computation is in progress, so the computation + * should be short and simple, and must not attempt to update any + * other mappings of this Map. The most appropriate usage is to + * construct a new object serving as an initial mapped value, or + * memoized result, as in: + * + *
 {@code
+     * map.computeIfAbsent(key, new Fun() {
+     *   public V map(K k) { return new Value(f(k)); }});}
+ * + * @param key key with which the specified value is to be associated + * @param mappingFunction the function to compute a value + * @return the current (existing or computed) value associated with + * the specified key, or null if the computed value is null + * @throws NullPointerException if the specified key or mappingFunction + * is null + * @throws IllegalStateException if the computation detectably + * attempts a recursive update to this map that would + * otherwise never complete + * @throws RuntimeException or Error if the mappingFunction does so, + * in which case the mapping is left unestablished + */ + @SuppressWarnings("unchecked") public V computeIfAbsent + (K key, Fun mappingFunction) { + if (key == null || mappingFunction == null) + throw new NullPointerException(); + return (V)internalComputeIfAbsent(key, mappingFunction); + } + + /** + * If the given key is present, computes a new mapping value given a key and + * its current mapped value. This is equivalent to + *
 {@code
+     *   if (map.containsKey(key)) {
+     *     value = remappingFunction.apply(key, map.get(key));
+     *     if (value != null)
+     *       map.put(key, value);
+     *     else
+     *       map.remove(key);
+     *   }
+     * }
+ * + * except that the action is performed atomically. If the + * function returns {@code null}, the mapping is removed. If the + * function itself throws an (unchecked) exception, the exception + * is rethrown to its caller, and the current mapping is left + * unchanged. Some attempted update operations on this map by + * other threads may be blocked while computation is in progress, + * so the computation should be short and simple, and must not + * attempt to update any other mappings of this Map. For example, + * to either create or append new messages to a value mapping: + * + * @param key key with which the specified value is to be associated + * @param remappingFunction the function to compute a value + * @return the new value associated with the specified key, or null if none + * @throws NullPointerException if the specified key or remappingFunction + * is null + * @throws IllegalStateException if the computation detectably + * attempts a recursive update to this map that would + * otherwise never complete + * @throws RuntimeException or Error if the remappingFunction does so, + * in which case the mapping is unchanged + */ + @SuppressWarnings("unchecked") public V computeIfPresent + (K key, BiFun remappingFunction) { + if (key == null || remappingFunction == null) + throw new NullPointerException(); + return (V)internalCompute(key, true, remappingFunction); + } + + /** + * Computes a new mapping value given a key and + * its current mapped value (or {@code null} if there is no current + * mapping). This is equivalent to + *
 {@code
+     *   value = remappingFunction.apply(key, map.get(key));
+     *   if (value != null)
+     *     map.put(key, value);
+     *   else
+     *     map.remove(key);
+     * }
+ * + * except that the action is performed atomically. If the + * function returns {@code null}, the mapping is removed. If the + * function itself throws an (unchecked) exception, the exception + * is rethrown to its caller, and the current mapping is left + * unchanged. Some attempted update operations on this map by + * other threads may be blocked while computation is in progress, + * so the computation should be short and simple, and must not + * attempt to update any other mappings of this Map. For example, + * to either create or append new messages to a value mapping: + * + *
 {@code
+     * Map map = ...;
+     * final String msg = ...;
+     * map.compute(key, new BiFun() {
+     *   public String apply(Key k, String v) {
+     *    return (v == null) ? msg : v + msg;});}}
+ * + * @param key key with which the specified value is to be associated + * @param remappingFunction the function to compute a value + * @return the new value associated with the specified key, or null if none + * @throws NullPointerException if the specified key or remappingFunction + * is null + * @throws IllegalStateException if the computation detectably + * attempts a recursive update to this map that would + * otherwise never complete + * @throws RuntimeException or Error if the remappingFunction does so, + * in which case the mapping is unchanged + */ + @SuppressWarnings("unchecked") public V compute + (K key, BiFun remappingFunction) { + if (key == null || remappingFunction == null) + throw new NullPointerException(); + return (V)internalCompute(key, false, remappingFunction); + } + + /** + * If the specified key is not already associated + * with a value, associate it with the given value. + * Otherwise, replace the value with the results of + * the given remapping function. This is equivalent to: + *
 {@code
+     *   if (!map.containsKey(key))
+     *     map.put(value);
+     *   else {
+     *     newValue = remappingFunction.apply(map.get(key), value);
+     *     if (value != null)
+     *       map.put(key, value);
+     *     else
+     *       map.remove(key);
+     *   }
+     * }
+ * except that the action is performed atomically. If the + * function returns {@code null}, the mapping is removed. If the + * function itself throws an (unchecked) exception, the exception + * is rethrown to its caller, and the current mapping is left + * unchanged. Some attempted update operations on this map by + * other threads may be blocked while computation is in progress, + * so the computation should be short and simple, and must not + * attempt to update any other mappings of this Map. + */ + @SuppressWarnings("unchecked") public V merge + (K key, V value, BiFun remappingFunction) { + if (key == null || value == null || remappingFunction == null) + throw new NullPointerException(); + return (V)internalMerge(key, value, remappingFunction); + } + + /** + * Removes the key (and its corresponding value) from this map. + * This method does nothing if the key is not in the map. + * + * @param key the key that needs to be removed + * @return the previous value associated with {@code key}, or + * {@code null} if there was no mapping for {@code key} + * @throws NullPointerException if the specified key is null + */ + @SuppressWarnings("unchecked") public V remove(Object key) { + if (key == null) + throw new NullPointerException(); + return (V)internalReplace(key, null, null); + } + + /** + * {@inheritDoc} + * + * @throws NullPointerException if the specified key is null + */ + public boolean remove(Object key, Object value) { + if (key == null) + throw new NullPointerException(); + if (value == null) + return false; + return internalReplace(key, null, value) != null; + } + + /** + * {@inheritDoc} + * + * @throws NullPointerException if any of the arguments are null + */ + public boolean replace(K key, V oldValue, V newValue) { + if (key == null || oldValue == null || newValue == null) + throw new NullPointerException(); + return internalReplace(key, newValue, oldValue) != null; + } + + /** + * {@inheritDoc} + * + * @return the previous value associated with the specified key, + * or {@code null} if there was no mapping for the key + * @throws NullPointerException if the specified key or value is null + */ + @SuppressWarnings("unchecked") public V replace(K key, V value) { + if (key == null || value == null) + throw new NullPointerException(); + return (V)internalReplace(key, value, null); + } + + /** + * Removes all of the mappings from this map. + */ + public void clear() { + internalClear(); + } + + /** + * Returns a {@link Set} view of the keys contained in this map. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. + * + * @return the set view + */ + public KeySetView keySet() { + KeySetView ks = keySet; + return (ks != null) ? ks : (keySet = new KeySetView(this, null)); + } + + /** + * Returns a {@link Set} view of the keys in this map, using the + * given common mapped value for any additions (i.e., {@link + * Collection#add} and {@link Collection#addAll}). This is of + * course only appropriate if it is acceptable to use the same + * value for all additions from this view. + * + * @param mappedValue the mapped value to use for any + * additions. + * @return the set view + * @throws NullPointerException if the mappedValue is null + */ + public KeySetView keySet(V mappedValue) { + if (mappedValue == null) + throw new NullPointerException(); + return new KeySetView(this, mappedValue); + } + + /** + * Returns a {@link Collection} view of the values contained in this map. + * The collection is backed by the map, so changes to the map are + * reflected in the collection, and vice-versa. + */ + public ValuesView values() { + ValuesView vs = values; + return (vs != null) ? vs : (values = new ValuesView(this)); + } + + /** + * Returns a {@link Set} view of the mappings contained in this map. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. The set supports element + * removal, which removes the corresponding mapping from the map, + * via the {@code Iterator.remove}, {@code Set.remove}, + * {@code removeAll}, {@code retainAll}, and {@code clear} + * operations. It does not support the {@code add} or + * {@code addAll} operations. + * + *

The view's {@code iterator} is a "weakly consistent" iterator + * that will never throw {@link ConcurrentModificationException}, + * and guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not guaranteed to) + * reflect any modifications subsequent to construction. + */ + public Set> entrySet() { + EntrySetView es = entrySet; + return (es != null) ? es : (entrySet = new EntrySetView(this)); + } + + /** + * Returns an enumeration of the keys in this table. + * + * @return an enumeration of the keys in this table + * @see #keySet() + */ + public Enumeration keys() { + return new KeyIterator(this); + } + + /** + * Returns an enumeration of the values in this table. + * + * @return an enumeration of the values in this table + * @see #values() + */ + public Enumeration elements() { + return new ValueIterator(this); + } + + /** + * Returns a partitionable iterator of the keys in this map. + * + * @return a partitionable iterator of the keys in this map + */ + public Spliterator keySpliterator() { + return new KeyIterator(this); + } + + /** + * Returns a partitionable iterator of the values in this map. + * + * @return a partitionable iterator of the values in this map + */ + public Spliterator valueSpliterator() { + return new ValueIterator(this); + } + + /** + * Returns a partitionable iterator of the entries in this map. + * + * @return a partitionable iterator of the entries in this map + */ + public Spliterator> entrySpliterator() { + return new EntryIterator(this); + } + + /** + * Returns the hash code value for this {@link Map}, i.e., + * the sum of, for each key-value pair in the map, + * {@code key.hashCode() ^ value.hashCode()}. + * + * @return the hash code value for this map + */ + public int hashCode() { + int h = 0; + Traverser it = new Traverser(this); + Object v; + while ((v = it.advance()) != null) { + h += it.nextKey.hashCode() ^ v.hashCode(); + } + return h; + } + + /** + * Returns a string representation of this map. The string + * representation consists of a list of key-value mappings (in no + * particular order) enclosed in braces ("{@code {}}"). Adjacent + * mappings are separated by the characters {@code ", "} (comma + * and space). Each key-value mapping is rendered as the key + * followed by an equals sign ("{@code =}") followed by the + * associated value. + * + * @return a string representation of this map + */ + public String toString() { + Traverser it = new Traverser(this); + StringBuilder sb = new StringBuilder(); + sb.append('{'); + Object v; + if ((v = it.advance()) != null) { + for (;;) { + Object k = it.nextKey; + sb.append(k == this ? "(this Map)" : k); + sb.append('='); + sb.append(v == this ? "(this Map)" : v); + if ((v = it.advance()) == null) + break; + sb.append(',').append(' '); + } + } + return sb.append('}').toString(); + } + + /** + * Compares the specified object with this map for equality. + * Returns {@code true} if the given object is a map with the same + * mappings as this map. This operation may return misleading + * results if either map is concurrently modified during execution + * of this method. + * + * @param o object to be compared for equality with this map + * @return {@code true} if the specified object is equal to this map + */ + public boolean equals(Object o) { + if (o != this) { + if (!(o instanceof Map)) + return false; + Map m = (Map) o; + Traverser it = new Traverser(this); + Object val; + while ((val = it.advance()) != null) { + Object v = m.get(it.nextKey); + if (v == null || (v != val && !v.equals(val))) + return false; + } + for (Map.Entry e : m.entrySet()) { + Object mk, mv, v; + if ((mk = e.getKey()) == null || + (mv = e.getValue()) == null || + (v = internalGet(mk)) == null || + (mv != v && !mv.equals(v))) + return false; + } + } + return true; + } + + /* ----------------Iterators -------------- */ + + @SuppressWarnings("serial") static final class KeyIterator extends Traverser + implements Spliterator, Enumeration { + KeyIterator(ConcurrentHashMapV8 map) { super(map); } + KeyIterator(Traverser it) { + super(it); + } + public KeyIterator split() { + if (nextKey != null) + throw new IllegalStateException(); + return new KeyIterator(this); + } + @SuppressWarnings("unchecked") public final K next() { + if (nextVal == null && advance() == null) + throw new NoSuchElementException(); + Object k = nextKey; + nextVal = null; + return (K) k; + } + + public final K nextElement() { return next(); } + } + + @SuppressWarnings("serial") static final class ValueIterator extends Traverser + implements Spliterator, Enumeration { + ValueIterator(ConcurrentHashMapV8 map) { super(map); } + ValueIterator(Traverser it) { + super(it); + } + public ValueIterator split() { + if (nextKey != null) + throw new IllegalStateException(); + return new ValueIterator(this); + } + + @SuppressWarnings("unchecked") public final V next() { + Object v; + if ((v = nextVal) == null && (v = advance()) == null) + throw new NoSuchElementException(); + nextVal = null; + return (V) v; + } + + public final V nextElement() { return next(); } + } + + @SuppressWarnings("serial") static final class EntryIterator extends Traverser + implements Spliterator> { + EntryIterator(ConcurrentHashMapV8 map) { super(map); } + EntryIterator(Traverser it) { + super(it); + } + public EntryIterator split() { + if (nextKey != null) + throw new IllegalStateException(); + return new EntryIterator(this); + } + + @SuppressWarnings("unchecked") public final Map.Entry next() { + Object v; + if ((v = nextVal) == null && (v = advance()) == null) + throw new NoSuchElementException(); + Object k = nextKey; + nextVal = null; + return new MapEntry((K)k, (V)v, map); + } + } + + /** + * Exported Entry for iterators + */ + static final class MapEntry implements Map.Entry { + final K key; // non-null + V val; // non-null + final ConcurrentHashMapV8 map; + MapEntry(K key, V val, ConcurrentHashMapV8 map) { + this.key = key; + this.val = val; + this.map = map; + } + public final K getKey() { return key; } + public final V getValue() { return val; } + public final int hashCode() { return key.hashCode() ^ val.hashCode(); } + public final String toString(){ return key + "=" + val; } + + public final boolean equals(Object o) { + Object k, v; Map.Entry e; + return ((o instanceof Map.Entry) && + (k = (e = (Map.Entry)o).getKey()) != null && + (v = e.getValue()) != null && + (k == key || k.equals(key)) && + (v == val || v.equals(val))); + } + + /** + * Sets our entry's value and writes through to the map. The + * value to return is somewhat arbitrary here. Since we do not + * necessarily track asynchronous changes, the most recent + * "previous" value could be different from what we return (or + * could even have been removed in which case the put will + * re-establish). We do not and cannot guarantee more. + */ + public final V setValue(V value) { + if (value == null) throw new NullPointerException(); + V v = val; + val = value; + map.put(key, value); + return v; + } + } + + /* ---------------- Serialization Support -------------- */ + + /** + * Stripped-down version of helper class used in previous version, + * declared for the sake of serialization compatibility + */ + static class Segment implements Serializable { + private static final long serialVersionUID = 2249069246763182397L; + final float loadFactor; + Segment(float lf) { this.loadFactor = lf; } + } + + /** + * Saves the state of the {@code ConcurrentHashMapV8} instance to a + * stream (i.e., serializes it). + * @param s the stream + * @serialData + * the key (Object) and value (Object) + * for each key-value mapping, followed by a null pair. + * The key-value mappings are emitted in no particular order. + */ + @SuppressWarnings("unchecked") private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + if (segments == null) { // for serialization compatibility + segments = (Segment[]) + new Segment[DEFAULT_CONCURRENCY_LEVEL]; + for (int i = 0; i < segments.length; ++i) + segments[i] = new Segment(LOAD_FACTOR); + } + s.defaultWriteObject(); + Traverser it = new Traverser(this); + Object v; + while ((v = it.advance()) != null) { + s.writeObject(it.nextKey); + s.writeObject(v); + } + s.writeObject(null); + s.writeObject(null); + segments = null; // throw away + } + + /** + * Reconstitutes the instance from a stream (that is, deserializes it). + * @param s the stream + */ + @SuppressWarnings("unchecked") private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + this.segments = null; // unneeded + // initialize transient final field + this.counter = new LongAdder(); + + // Create all nodes, then place in table once size is known + long size = 0L; + Node p = null; + for (;;) { + K k = (K) s.readObject(); + V v = (V) s.readObject(); + if (k != null && v != null) { + int h = spread(k.hashCode()); + p = new Node(h, k, v, p); + ++size; + } + else + break; + } + if (p != null) { + boolean init = false; + int n; + if (size >= (long)(MAXIMUM_CAPACITY >>> 1)) + n = MAXIMUM_CAPACITY; + else { + int sz = (int)size; + n = tableSizeFor(sz + (sz >>> 1) + 1); + } + int sc = sizeCtl; + boolean collide = false; + if (n > sc && + SIZE_CTRL_UPDATER.compareAndSet(this, sc, -1)) { + try { + if (table == null) { + init = true; + AtomicReferenceArray tab = new AtomicReferenceArray(n); + int mask = n - 1; + while (p != null) { + int j = p.hash & mask; + Node next = p.next; + Node q = p.next = tabAt(tab, j); + setTabAt(tab, j, p); + if (!collide && q != null && q.hash == p.hash) + collide = true; + p = next; + } + table = tab; + counter.add(size); + sc = n - (n >>> 2); + } + } finally { + sizeCtl = sc; + } + if (collide) { // rescan and convert to TreeBins + AtomicReferenceArray tab = table; + for (int i = 0; i < tab.length(); ++i) { + int c = 0; + for (Node e = tabAt(tab, i); e != null; e = e.next) { + if (++c > TREE_THRESHOLD && + (e.key instanceof Comparable)) { + replaceWithTreeBin(tab, i, e.key); + break; + } + } + } + } + } + if (!init) { // Can only happen if unsafely published. + while (p != null) { + internalPut(p.key, p.val); + p = p.next; + } + } + } + } + + + // ------------------------------------------------------- + + // Sams + /** Interface describing a void action of one argument */ + public interface Action { void apply(A a); } + /** Interface describing a void action of two arguments */ + public interface BiAction { void apply(A a, B b); } + /** Interface describing a function of one argument */ + public interface Generator { T apply(); } + /** Interface describing a function mapping its argument to a double */ + public interface ObjectToDouble { double apply(A a); } + /** Interface describing a function mapping its argument to a long */ + public interface ObjectToLong { long apply(A a); } + /** Interface describing a function mapping its argument to an int */ + public interface ObjectToInt {int apply(A a); } + /** Interface describing a function mapping two arguments to a double */ + public interface ObjectByObjectToDouble { double apply(A a, B b); } + /** Interface describing a function mapping two arguments to a long */ + public interface ObjectByObjectToLong { long apply(A a, B b); } + /** Interface describing a function mapping two arguments to an int */ + public interface ObjectByObjectToInt {int apply(A a, B b); } + /** Interface describing a function mapping a double to a double */ + public interface DoubleToDouble { double apply(double a); } + /** Interface describing a function mapping a long to a long */ + public interface LongToLong { long apply(long a); } + /** Interface describing a function mapping an int to an int */ + public interface IntToInt { int apply(int a); } + /** Interface describing a function mapping two doubles to a double */ + public interface DoubleByDoubleToDouble { double apply(double a, double b); } + /** Interface describing a function mapping two longs to a long */ + public interface LongByLongToLong { long apply(long a, long b); } + /** Interface describing a function mapping two ints to an int */ + public interface IntByIntToInt { int apply(int a, int b); } + + + /* ----------------Views -------------- */ + + /** + * Base class for views. + */ + static abstract class CHMView { + final ConcurrentHashMapV8 map; + CHMView(ConcurrentHashMapV8 map) { this.map = map; } + + /** + * Returns the map backing this view. + * + * @return the map backing this view + */ + public ConcurrentHashMapV8 getMap() { return map; } + + public final int size() { return map.size(); } + public final boolean isEmpty() { return map.isEmpty(); } + public final void clear() { map.clear(); } + + // implementations below rely on concrete classes supplying these + abstract public Iterator iterator(); + abstract public boolean contains(Object o); + abstract public boolean remove(Object o); + + private static final String oomeMsg = "Required array size too large"; + + public final Object[] toArray() { + long sz = map.mappingCount(); + if (sz > (long)(MAX_ARRAY_SIZE)) + throw new OutOfMemoryError(oomeMsg); + int n = (int)sz; + Object[] r = new Object[n]; + int i = 0; + Iterator it = iterator(); + while (it.hasNext()) { + if (i == n) { + if (n >= MAX_ARRAY_SIZE) + throw new OutOfMemoryError(oomeMsg); + if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) + n = MAX_ARRAY_SIZE; + else + n += (n >>> 1) + 1; + r = Arrays.copyOf(r, n); + } + r[i++] = it.next(); + } + return (i == n) ? r : Arrays.copyOf(r, i); + } + + @SuppressWarnings("unchecked") public final T[] toArray(T[] a) { + long sz = map.mappingCount(); + if (sz > (long)(MAX_ARRAY_SIZE)) + throw new OutOfMemoryError(oomeMsg); + int m = (int)sz; + T[] r = (a.length >= m) ? a : + (T[])java.lang.reflect.Array + .newInstance(a.getClass().getComponentType(), m); + int n = r.length; + int i = 0; + Iterator it = iterator(); + while (it.hasNext()) { + if (i == n) { + if (n >= MAX_ARRAY_SIZE) + throw new OutOfMemoryError(oomeMsg); + if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) + n = MAX_ARRAY_SIZE; + else + n += (n >>> 1) + 1; + r = Arrays.copyOf(r, n); + } + r[i++] = (T)it.next(); + } + if (a == r && i < n) { + r[i] = null; // null-terminate + return r; + } + return (i == n) ? r : Arrays.copyOf(r, i); + } + + public final int hashCode() { + int h = 0; + for (Iterator it = iterator(); it.hasNext();) + h += it.next().hashCode(); + return h; + } + + public final String toString() { + StringBuilder sb = new StringBuilder(); + sb.append('['); + Iterator it = iterator(); + if (it.hasNext()) { + for (;;) { + Object e = it.next(); + sb.append(e == this ? "(this Collection)" : e); + if (!it.hasNext()) + break; + sb.append(',').append(' '); + } + } + return sb.append(']').toString(); + } + + public final boolean containsAll(Collection c) { + if (c != this) { + for (Iterator it = c.iterator(); it.hasNext();) { + Object e = it.next(); + if (e == null || !contains(e)) + return false; + } + } + return true; + } + + public final boolean removeAll(Collection c) { + boolean modified = false; + for (Iterator it = iterator(); it.hasNext();) { + if (c.contains(it.next())) { + it.remove(); + modified = true; + } + } + return modified; + } + + public final boolean retainAll(Collection c) { + boolean modified = false; + for (Iterator it = iterator(); it.hasNext();) { + if (!c.contains(it.next())) { + it.remove(); + modified = true; + } + } + return modified; + } + + } + + /** + * A view of a ConcurrentHashMapV8 as a {@link Set} of keys, in + * which additions may optionally be enabled by mapping to a + * common value. This class cannot be directly instantiated. See + * {@link #keySet}, {@link #keySet(Object)}, {@link #newKeySet()}, + * {@link #newKeySet(int)}. + */ + public static class KeySetView extends CHMView implements Set, java.io.Serializable { + private static final long serialVersionUID = 7249069246763182397L; + private final V value; + KeySetView(ConcurrentHashMapV8 map, V value) { // non-public + super(map); + this.value = value; + } + + /** + * Returns the default mapped value for additions, + * or {@code null} if additions are not supported. + * + * @return the default mapped value for additions, or {@code null} + * if not supported. + */ + public V getMappedValue() { return value; } + + // implement Set API + + public boolean contains(Object o) { return map.containsKey(o); } + public boolean remove(Object o) { return map.remove(o) != null; } + + /** + * Returns a "weakly consistent" iterator that will never + * throw {@link ConcurrentModificationException}, and + * guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not + * guaranteed to) reflect any modifications subsequent to + * construction. + * + * @return an iterator over the keys of this map + */ + public Iterator iterator() { return new KeyIterator(map); } + public boolean add(K e) { + V v; + if ((v = value) == null) + throw new UnsupportedOperationException(); + if (e == null) + throw new NullPointerException(); + return map.internalPutIfAbsent(e, v) == null; + } + public boolean addAll(Collection c) { + boolean added = false; + V v; + if ((v = value) == null) + throw new UnsupportedOperationException(); + for (K e : c) { + if (e == null) + throw new NullPointerException(); + if (map.internalPutIfAbsent(e, v) == null) + added = true; + } + return added; + } + public boolean equals(Object o) { + Set c; + return ((o instanceof Set) && + ((c = (Set)o) == this || + (containsAll(c) && c.containsAll(this)))); + } + } + + /** + * A view of a ConcurrentHashMapV8 as a {@link Collection} of + * values, in which additions are disabled. This class cannot be + * directly instantiated. See {@link #values}, + * + *

The view's {@code iterator} is a "weakly consistent" iterator + * that will never throw {@link ConcurrentModificationException}, + * and guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not guaranteed to) + * reflect any modifications subsequent to construction. + */ + public static final class ValuesView extends CHMView + implements Collection { + ValuesView(ConcurrentHashMapV8 map) { super(map); } + public final boolean contains(Object o) { return map.containsValue(o); } + public final boolean remove(Object o) { + if (o != null) { + Iterator it = new ValueIterator(map); + while (it.hasNext()) { + if (o.equals(it.next())) { + it.remove(); + return true; + } + } + } + return false; + } + + /** + * Returns a "weakly consistent" iterator that will never + * throw {@link ConcurrentModificationException}, and + * guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not + * guaranteed to) reflect any modifications subsequent to + * construction. + * + * @return an iterator over the values of this map + */ + public final Iterator iterator() { + return new ValueIterator(map); + } + public final boolean add(V e) { + throw new UnsupportedOperationException(); + } + public final boolean addAll(Collection c) { + throw new UnsupportedOperationException(); + } + } + + /** + * A view of a ConcurrentHashMapV8 as a {@link Set} of (key, value) + * entries. This class cannot be directly instantiated. See + * {@link #entrySet}. + */ + public static final class EntrySetView extends CHMView + implements Set> { + EntrySetView(ConcurrentHashMapV8 map) { super(map); } + public final boolean contains(Object o) { + Object k, v, r; Map.Entry e; + return ((o instanceof Map.Entry) && + (k = (e = (Map.Entry)o).getKey()) != null && + (r = map.get(k)) != null && + (v = e.getValue()) != null && + (v == r || v.equals(r))); + } + public final boolean remove(Object o) { + Object k, v; Map.Entry e; + return ((o instanceof Map.Entry) && + (k = (e = (Map.Entry)o).getKey()) != null && + (v = e.getValue()) != null && + map.remove(k, v)); + } + + /** + * Returns a "weakly consistent" iterator that will never + * throw {@link ConcurrentModificationException}, and + * guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not + * guaranteed to) reflect any modifications subsequent to + * construction. + * + * @return an iterator over the entries of this map + */ + public final Iterator> iterator() { + return new EntryIterator(map); + } + + public final boolean add(Entry e) { + K key = e.getKey(); + V value = e.getValue(); + if (key == null || value == null) + throw new NullPointerException(); + return map.internalPut(key, value) == null; + } + public final boolean addAll(Collection> c) { + boolean added = false; + for (Entry e : c) { + if (add(e)) + added = true; + } + return added; + } + public boolean equals(Object o) { + Set c; + return ((o instanceof Set) && + ((c = (Set)o) == this || + (containsAll(c) && c.containsAll(this)))); + } + } +} diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java new file mode 100644 index 0000000..ecf552a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java @@ -0,0 +1,204 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This is based on 1.9 version. + +package com.concurrent_ruby.ext.jsr166e.nounsafe; + +import java.util.concurrent.atomic.AtomicLong; +import java.io.IOException; +import java.io.Serializable; +import java.io.ObjectInputStream; + +/** + * One or more variables that together maintain an initially zero + * {@code long} sum. When updates (method {@link #add}) are contended + * across threads, the set of variables may grow dynamically to reduce + * contention. Method {@link #sum} (or, equivalently, {@link + * #longValue}) returns the current total combined across the + * variables maintaining the sum. + * + *

This class is usually preferable to {@link AtomicLong} when + * multiple threads update a common sum that is used for purposes such + * as collecting statistics, not for fine-grained synchronization + * control. Under low update contention, the two classes have similar + * characteristics. But under high contention, expected throughput of + * this class is significantly higher, at the expense of higher space + * consumption. + * + *

This class extends {@link Number}, but does not define + * methods such as {@code hashCode} and {@code compareTo} because + * instances are expected to be mutated, and so are not useful as + * collection keys. + * + *

jsr166e note: This class is targeted to be placed in + * java.util.concurrent.atomic. + * + * @since 1.8 + * @author Doug Lea + */ +public class LongAdder extends Striped64 implements Serializable { + private static final long serialVersionUID = 7249069246863182397L; + + /** + * Version of plus for use in retryUpdate + */ + final long fn(long v, long x) { return v + x; } + + /** + * Creates a new adder with initial sum of zero. + */ + public LongAdder() { + } + + /** + * Adds the given value. + * + * @param x the value to add + */ + public void add(long x) { + Cell[] as; long b, v; HashCode hc; Cell a; int n; + if ((as = cells) != null || !casBase(b = base, b + x)) { + boolean uncontended = true; + int h = (hc = threadHashCode.get()).code; + if (as == null || (n = as.length) < 1 || + (a = as[(n - 1) & h]) == null || + !(uncontended = a.cas(v = a.value, v + x))) + retryUpdate(x, hc, uncontended); + } + } + + /** + * Equivalent to {@code add(1)}. + */ + public void increment() { + add(1L); + } + + /** + * Equivalent to {@code add(-1)}. + */ + public void decrement() { + add(-1L); + } + + /** + * Returns the current sum. The returned value is NOT an + * atomic snapshot: Invocation in the absence of concurrent + * updates returns an accurate result, but concurrent updates that + * occur while the sum is being calculated might not be + * incorporated. + * + * @return the sum + */ + public long sum() { + long sum = base; + Cell[] as = cells; + if (as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if (a != null) + sum += a.value; + } + } + return sum; + } + + /** + * Resets variables maintaining the sum to zero. This method may + * be a useful alternative to creating a new adder, but is only + * effective if there are no concurrent updates. Because this + * method is intrinsically racy, it should only be used when it is + * known that no threads are concurrently updating. + */ + public void reset() { + internalReset(0L); + } + + /** + * Equivalent in effect to {@link #sum} followed by {@link + * #reset}. This method may apply for example during quiescent + * points between multithreaded computations. If there are + * updates concurrent with this method, the returned value is + * not guaranteed to be the final value occurring before + * the reset. + * + * @return the sum + */ + public long sumThenReset() { + long sum = base; + Cell[] as = cells; + base = 0L; + if (as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if (a != null) { + sum += a.value; + a.value = 0L; + } + } + } + return sum; + } + + /** + * Returns the String representation of the {@link #sum}. + * @return the String representation of the {@link #sum} + */ + public String toString() { + return Long.toString(sum()); + } + + /** + * Equivalent to {@link #sum}. + * + * @return the sum + */ + public long longValue() { + return sum(); + } + + /** + * Returns the {@link #sum} as an {@code int} after a narrowing + * primitive conversion. + */ + public int intValue() { + return (int)sum(); + } + + /** + * Returns the {@link #sum} as a {@code float} + * after a widening primitive conversion. + */ + public float floatValue() { + return (float)sum(); + } + + /** + * Returns the {@link #sum} as a {@code double} after a widening + * primitive conversion. + */ + public double doubleValue() { + return (double)sum(); + } + + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + s.defaultWriteObject(); + s.writeLong(sum()); + } + + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException { + s.defaultReadObject(); + busy = 0; + cells = null; + base = s.readLong(); + } + +} diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java new file mode 100644 index 0000000..f521642 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java @@ -0,0 +1,291 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This is based on 1.5 version. + +package com.concurrent_ruby.ext.jsr166e.nounsafe; + +import java.util.Random; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import java.util.concurrent.atomic.AtomicLongFieldUpdater; + +/** + * A package-local class holding common representation and mechanics + * for classes supporting dynamic striping on 64bit values. The class + * extends Number so that concrete subclasses must publicly do so. + */ +abstract class Striped64 extends Number { + /* + * This class maintains a lazily-initialized table of atomically + * updated variables, plus an extra "base" field. The table size + * is a power of two. Indexing uses masked per-thread hash codes. + * Nearly all declarations in this class are package-private, + * accessed directly by subclasses. + * + * Table entries are of class Cell; a variant of AtomicLong padded + * to reduce cache contention on most processors. Padding is + * overkill for most Atomics because they are usually irregularly + * scattered in memory and thus don't interfere much with each + * other. But Atomic objects residing in arrays will tend to be + * placed adjacent to each other, and so will most often share + * cache lines (with a huge negative performance impact) without + * this precaution. + * + * In part because Cells are relatively large, we avoid creating + * them until they are needed. When there is no contention, all + * updates are made to the base field. Upon first contention (a + * failed CAS on base update), the table is initialized to size 2. + * The table size is doubled upon further contention until + * reaching the nearest power of two greater than or equal to the + * number of CPUS. Table slots remain empty (null) until they are + * needed. + * + * A single spinlock ("busy") is used for initializing and + * resizing the table, as well as populating slots with new Cells. + * There is no need for a blocking lock: When the lock is not + * available, threads try other slots (or the base). During these + * retries, there is increased contention and reduced locality, + * which is still better than alternatives. + * + * Per-thread hash codes are initialized to random values. + * Contention and/or table collisions are indicated by failed + * CASes when performing an update operation (see method + * retryUpdate). Upon a collision, if the table size is less than + * the capacity, it is doubled in size unless some other thread + * holds the lock. If a hashed slot is empty, and lock is + * available, a new Cell is created. Otherwise, if the slot + * exists, a CAS is tried. Retries proceed by "double hashing", + * using a secondary hash (Marsaglia XorShift) to try to find a + * free slot. + * + * The table size is capped because, when there are more threads + * than CPUs, supposing that each thread were bound to a CPU, + * there would exist a perfect hash function mapping threads to + * slots that eliminates collisions. When we reach capacity, we + * search for this mapping by randomly varying the hash codes of + * colliding threads. Because search is random, and collisions + * only become known via CAS failures, convergence can be slow, + * and because threads are typically not bound to CPUS forever, + * may not occur at all. However, despite these limitations, + * observed contention rates are typically low in these cases. + * + * It is possible for a Cell to become unused when threads that + * once hashed to it terminate, as well as in the case where + * doubling the table causes no thread to hash to it under + * expanded mask. We do not try to detect or remove such cells, + * under the assumption that for long-running instances, observed + * contention levels will recur, so the cells will eventually be + * needed again; and for short-lived ones, it does not matter. + */ + + /** + * Padded variant of AtomicLong supporting only raw accesses plus CAS. + * The value field is placed between pads, hoping that the JVM doesn't + * reorder them. + * + * JVM intrinsics note: It would be possible to use a release-only + * form of CAS here, if it were provided. + */ + static final class Cell { + volatile long p0, p1, p2, p3, p4, p5, p6; + volatile long value; + volatile long q0, q1, q2, q3, q4, q5, q6; + + static AtomicLongFieldUpdater VALUE_UPDATER = AtomicLongFieldUpdater.newUpdater(Cell.class, "value"); + + Cell(long x) { value = x; } + + final boolean cas(long cmp, long val) { + return VALUE_UPDATER.compareAndSet(this, cmp, val); + } + + } + + /** + * Holder for the thread-local hash code. The code is initially + * random, but may be set to a different value upon collisions. + */ + static final class HashCode { + static final Random rng = new Random(); + int code; + HashCode() { + int h = rng.nextInt(); // Avoid zero to allow xorShift rehash + code = (h == 0) ? 1 : h; + } + } + + /** + * The corresponding ThreadLocal class + */ + static final class ThreadHashCode extends ThreadLocal { + public HashCode initialValue() { return new HashCode(); } + } + + /** + * Static per-thread hash codes. Shared across all instances to + * reduce ThreadLocal pollution and because adjustments due to + * collisions in one table are likely to be appropriate for + * others. + */ + static final ThreadHashCode threadHashCode = new ThreadHashCode(); + + /** Number of CPUS, to place bound on table size */ + static final int NCPU = Runtime.getRuntime().availableProcessors(); + + /** + * Table of cells. When non-null, size is a power of 2. + */ + transient volatile Cell[] cells; + + /** + * Base value, used mainly when there is no contention, but also as + * a fallback during table initialization races. Updated via CAS. + */ + transient volatile long base; + + /** + * Spinlock (locked via CAS) used when resizing and/or creating Cells. + */ + transient volatile int busy; + + AtomicLongFieldUpdater BASE_UPDATER = AtomicLongFieldUpdater.newUpdater(Striped64.class, "base"); + AtomicIntegerFieldUpdater BUSY_UPDATER = AtomicIntegerFieldUpdater.newUpdater(Striped64.class, "busy"); + + /** + * Package-private default constructor + */ + Striped64() { + } + + /** + * CASes the base field. + */ + final boolean casBase(long cmp, long val) { + return BASE_UPDATER.compareAndSet(this, cmp, val); + } + + /** + * CASes the busy field from 0 to 1 to acquire lock. + */ + final boolean casBusy() { + return BUSY_UPDATER.compareAndSet(this, 0, 1); + } + + /** + * Computes the function of current and new value. Subclasses + * should open-code this update function for most uses, but the + * virtualized form is needed within retryUpdate. + * + * @param currentValue the current value (of either base or a cell) + * @param newValue the argument from a user update call + * @return result of the update function + */ + abstract long fn(long currentValue, long newValue); + + /** + * Handles cases of updates involving initialization, resizing, + * creating new Cells, and/or contention. See above for + * explanation. This method suffers the usual non-modularity + * problems of optimistic retry code, relying on rechecked sets of + * reads. + * + * @param x the value + * @param hc the hash code holder + * @param wasUncontended false if CAS failed before call + */ + final void retryUpdate(long x, HashCode hc, boolean wasUncontended) { + int h = hc.code; + boolean collide = false; // True if last slot nonempty + for (;;) { + Cell[] as; Cell a; int n; long v; + if ((as = cells) != null && (n = as.length) > 0) { + if ((a = as[(n - 1) & h]) == null) { + if (busy == 0) { // Try to attach new Cell + Cell r = new Cell(x); // Optimistically create + if (busy == 0 && casBusy()) { + boolean created = false; + try { // Recheck under lock + Cell[] rs; int m, j; + if ((rs = cells) != null && + (m = rs.length) > 0 && + rs[j = (m - 1) & h] == null) { + rs[j] = r; + created = true; + } + } finally { + busy = 0; + } + if (created) + break; + continue; // Slot is now non-empty + } + } + collide = false; + } + else if (!wasUncontended) // CAS already known to fail + wasUncontended = true; // Continue after rehash + else if (a.cas(v = a.value, fn(v, x))) + break; + else if (n >= NCPU || cells != as) + collide = false; // At max size or stale + else if (!collide) + collide = true; + else if (busy == 0 && casBusy()) { + try { + if (cells == as) { // Expand table unless stale + Cell[] rs = new Cell[n << 1]; + for (int i = 0; i < n; ++i) + rs[i] = as[i]; + cells = rs; + } + } finally { + busy = 0; + } + collide = false; + continue; // Retry with expanded table + } + h ^= h << 13; // Rehash + h ^= h >>> 17; + h ^= h << 5; + } + else if (busy == 0 && cells == as && casBusy()) { + boolean init = false; + try { // Initialize table + if (cells == as) { + Cell[] rs = new Cell[2]; + rs[h & 1] = new Cell(x); + cells = rs; + init = true; + } + } finally { + busy = 0; + } + if (init) + break; + } + else if (casBase(v = base, fn(v, x))) + break; // Fall back on using base + } + hc.code = h; // Record index for next time + } + + + /** + * Sets base and all cells to the given value. + */ + final void internalReset(long initialValue) { + Cell[] as = cells; + base = initialValue; + if (as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if (a != null) + a.value = initialValue; + } + } + } +} diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java new file mode 100644 index 0000000..3ea409f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java @@ -0,0 +1,199 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This is based on 1.16 version + +package com.concurrent_ruby.ext.jsr166y; + +import java.util.Random; + +/** + * A random number generator isolated to the current thread. Like the + * global {@link java.util.Random} generator used by the {@link + * java.lang.Math} class, a {@code ThreadLocalRandom} is initialized + * with an internally generated seed that may not otherwise be + * modified. When applicable, use of {@code ThreadLocalRandom} rather + * than shared {@code Random} objects in concurrent programs will + * typically encounter much less overhead and contention. Use of + * {@code ThreadLocalRandom} is particularly appropriate when multiple + * tasks (for example, each a {@link ForkJoinTask}) use random numbers + * in parallel in thread pools. + * + *

Usages of this class should typically be of the form: + * {@code ThreadLocalRandom.current().nextX(...)} (where + * {@code X} is {@code Int}, {@code Long}, etc). + * When all usages are of this form, it is never possible to + * accidently share a {@code ThreadLocalRandom} across multiple threads. + * + *

This class also provides additional commonly used bounded random + * generation methods. + * + * @since 1.7 + * @author Doug Lea + */ +public class ThreadLocalRandom extends Random { + // same constants as Random, but must be redeclared because private + private static final long multiplier = 0x5DEECE66DL; + private static final long addend = 0xBL; + private static final long mask = (1L << 48) - 1; + + /** + * The random seed. We can't use super.seed. + */ + private long rnd; + + /** + * Initialization flag to permit calls to setSeed to succeed only + * while executing the Random constructor. We can't allow others + * since it would cause setting seed in one part of a program to + * unintentionally impact other usages by the thread. + */ + boolean initialized; + + // Padding to help avoid memory contention among seed updates in + // different TLRs in the common case that they are located near + // each other. + private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7; + + /** + * The actual ThreadLocal + */ + private static final ThreadLocal localRandom = + new ThreadLocal() { + protected ThreadLocalRandom initialValue() { + return new ThreadLocalRandom(); + } + }; + + + /** + * Constructor called only by localRandom.initialValue. + */ + ThreadLocalRandom() { + super(); + initialized = true; + } + + /** + * Returns the current thread's {@code ThreadLocalRandom}. + * + * @return the current thread's {@code ThreadLocalRandom} + */ + public static ThreadLocalRandom current() { + return localRandom.get(); + } + + /** + * Throws {@code UnsupportedOperationException}. Setting seeds in + * this generator is not supported. + * + * @throws UnsupportedOperationException always + */ + public void setSeed(long seed) { + if (initialized) + throw new UnsupportedOperationException(); + rnd = (seed ^ multiplier) & mask; + } + + protected int next(int bits) { + rnd = (rnd * multiplier + addend) & mask; + return (int) (rnd >>> (48-bits)); + } + + /** + * Returns a pseudorandom, uniformly distributed value between the + * given least value (inclusive) and bound (exclusive). + * + * @param least the least value returned + * @param bound the upper bound (exclusive) + * @throws IllegalArgumentException if least greater than or equal + * to bound + * @return the next value + */ + public int nextInt(int least, int bound) { + if (least >= bound) + throw new IllegalArgumentException(); + return nextInt(bound - least) + least; + } + + /** + * Returns a pseudorandom, uniformly distributed value + * between 0 (inclusive) and the specified value (exclusive). + * + * @param n the bound on the random number to be returned. Must be + * positive. + * @return the next value + * @throws IllegalArgumentException if n is not positive + */ + public long nextLong(long n) { + if (n <= 0) + throw new IllegalArgumentException("n must be positive"); + // Divide n by two until small enough for nextInt. On each + // iteration (at most 31 of them but usually much less), + // randomly choose both whether to include high bit in result + // (offset) and whether to continue with the lower vs upper + // half (which makes a difference only if odd). + long offset = 0; + while (n >= Integer.MAX_VALUE) { + int bits = next(2); + long half = n >>> 1; + long nextn = ((bits & 2) == 0) ? half : n - half; + if ((bits & 1) == 0) + offset += n - nextn; + n = nextn; + } + return offset + nextInt((int) n); + } + + /** + * Returns a pseudorandom, uniformly distributed value between the + * given least value (inclusive) and bound (exclusive). + * + * @param least the least value returned + * @param bound the upper bound (exclusive) + * @return the next value + * @throws IllegalArgumentException if least greater than or equal + * to bound + */ + public long nextLong(long least, long bound) { + if (least >= bound) + throw new IllegalArgumentException(); + return nextLong(bound - least) + least; + } + + /** + * Returns a pseudorandom, uniformly distributed {@code double} value + * between 0 (inclusive) and the specified value (exclusive). + * + * @param n the bound on the random number to be returned. Must be + * positive. + * @return the next value + * @throws IllegalArgumentException if n is not positive + */ + public double nextDouble(double n) { + if (n <= 0) + throw new IllegalArgumentException("n must be positive"); + return nextDouble() * n; + } + + /** + * Returns a pseudorandom, uniformly distributed value between the + * given least value (inclusive) and bound (exclusive). + * + * @param least the least value returned + * @param bound the upper bound (exclusive) + * @return the next value + * @throws IllegalArgumentException if least greater than or equal + * to bound + */ + public double nextDouble(double least, double bound) { + if (least >= bound) + throw new IllegalArgumentException(); + return nextDouble() * (bound - least) + least; + } + + private static final long serialVersionUID = -5851777807851030925L; +} diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent-ruby.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent-ruby.rb new file mode 100644 index 0000000..e9a3dea --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent-ruby.rb @@ -0,0 +1,5 @@ +# This file is here so that there is a file with the same name as the gem that +# can be required by Bundler.require. Applications should normally +# require 'concurrent'. + +require_relative "concurrent" diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent.rb new file mode 100644 index 0000000..87de46f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent.rb @@ -0,0 +1,134 @@ +require 'concurrent/version' +require 'concurrent/constants' +require 'concurrent/errors' +require 'concurrent/configuration' + +require 'concurrent/atomics' +require 'concurrent/executors' +require 'concurrent/synchronization' + +require 'concurrent/atomic/atomic_markable_reference' +require 'concurrent/atomic/atomic_reference' +require 'concurrent/agent' +require 'concurrent/atom' +require 'concurrent/array' +require 'concurrent/hash' +require 'concurrent/set' +require 'concurrent/map' +require 'concurrent/tuple' +require 'concurrent/async' +require 'concurrent/dataflow' +require 'concurrent/delay' +require 'concurrent/exchanger' +require 'concurrent/future' +require 'concurrent/immutable_struct' +require 'concurrent/ivar' +require 'concurrent/maybe' +require 'concurrent/mutable_struct' +require 'concurrent/mvar' +require 'concurrent/promise' +require 'concurrent/scheduled_task' +require 'concurrent/settable_struct' +require 'concurrent/timer_task' +require 'concurrent/tvar' +require 'concurrent/promises' + +require 'concurrent/thread_safe/synchronized_delegator' +require 'concurrent/thread_safe/util' + +require 'concurrent/options' + +# @!macro internal_implementation_note +# +# @note **Private Implementation:** This abstraction is a private, internal +# implementation detail. It should never be used directly. + +# @!macro monotonic_clock_warning +# +# @note Time calculations on all platforms and languages are sensitive to +# changes to the system clock. To alleviate the potential problems +# associated with changing the system clock while an application is running, +# most modern operating systems provide a monotonic clock that operates +# independently of the system clock. A monotonic clock cannot be used to +# determine human-friendly clock times. A monotonic clock is used exclusively +# for calculating time intervals. Not all Ruby platforms provide access to an +# operating system monotonic clock. On these platforms a pure-Ruby monotonic +# clock will be used as a fallback. An operating system monotonic clock is both +# faster and more reliable than the pure-Ruby implementation. The pure-Ruby +# implementation should be fast and reliable enough for most non-realtime +# operations. At this time the common Ruby platforms that provide access to an +# operating system monotonic clock are MRI 2.1 and above and JRuby (all versions). +# +# @see http://linux.die.net/man/3/clock_gettime Linux clock_gettime(3) + +# @!macro copy_options +# +# ## Copy Options +# +# Object references in Ruby are mutable. This can lead to serious +# problems when the {#value} of an object is a mutable reference. Which +# is always the case unless the value is a `Fixnum`, `Symbol`, or similar +# "primitive" data type. Each instance can be configured with a few +# options that can help protect the program from potentially dangerous +# operations. Each of these options can be optionally set when the object +# instance is created: +# +# * `:dup_on_deref` When true the object will call the `#dup` method on +# the `value` object every time the `#value` method is called +# (default: false) +# * `:freeze_on_deref` When true the object will call the `#freeze` +# method on the `value` object every time the `#value` method is called +# (default: false) +# * `:copy_on_deref` When given a `Proc` object the `Proc` will be run +# every time the `#value` method is called. The `Proc` will be given +# the current `value` as its only argument and the result returned by +# the block will be the return value of the `#value` call. When `nil` +# this option will be ignored (default: nil) +# +# When multiple deref options are set the order of operations is strictly defined. +# The order of deref operations is: +# * `:copy_on_deref` +# * `:dup_on_deref` +# * `:freeze_on_deref` +# +# Because of this ordering there is no need to `#freeze` an object created by a +# provided `:copy_on_deref` block. Simply set `:freeze_on_deref` to `true`. +# Setting both `:dup_on_deref` to `true` and `:freeze_on_deref` to `true` is +# as close to the behavior of a "pure" functional language (like Erlang, Clojure, +# or Haskell) as we are likely to get in Ruby. + +# @!macro deref_options +# +# @option opts [Boolean] :dup_on_deref (false) Call `#dup` before +# returning the data from {#value} +# @option opts [Boolean] :freeze_on_deref (false) Call `#freeze` before +# returning the data from {#value} +# @option opts [Proc] :copy_on_deref (nil) When calling the {#value} +# method, call the given proc passing the internal value as the sole +# argument then return the new value returned from the proc. + +# @!macro executor_and_deref_options +# +# @param [Hash] opts the options used to define the behavior at update and deref +# and to specify the executor on which to perform actions +# @option opts [Executor] :executor when set use the given `Executor` instance. +# Three special values are also supported: `:io` returns the global pool for +# long, blocking (IO) tasks, `:fast` returns the global pool for short, fast +# operations, and `:immediate` returns the global `ImmediateExecutor` object. +# @!macro deref_options + +# @!macro warn.edge +# @api Edge +# @note **Edge Features** are under active development and may change frequently. +# +# - Deprecations are not added before incompatible changes. +# - Edge version: _major_ is always 0, _minor_ bump means incompatible change, +# _patch_ bump means compatible change. +# - Edge features may also lack tests and documentation. +# - Features developed in `concurrent-ruby-edge` are expected to move +# to `concurrent-ruby` when finalised. + + +# {include:file:README.md} +module Concurrent +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/agent.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/agent.rb new file mode 100644 index 0000000..815dca0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/agent.rb @@ -0,0 +1,587 @@ +require 'concurrent/configuration' +require 'concurrent/atomic/atomic_reference' +require 'concurrent/atomic/thread_local_var' +require 'concurrent/collection/copy_on_write_observer_set' +require 'concurrent/concern/observable' +require 'concurrent/synchronization' + +module Concurrent + + # `Agent` is inspired by Clojure's [agent](http://clojure.org/agents) + # function. An agent is a shared, mutable variable providing independent, + # uncoordinated, *asynchronous* change of individual values. Best used when + # the value will undergo frequent, complex updates. Suitable when the result + # of an update does not need to be known immediately. `Agent` is (mostly) + # functionally equivalent to Clojure's agent, except where the runtime + # prevents parity. + # + # Agents are reactive, not autonomous - there is no imperative message loop + # and no blocking receive. The state of an Agent should be itself immutable + # and the `#value` of an Agent is always immediately available for reading by + # any thread without any messages, i.e. observation does not require + # cooperation or coordination. + # + # Agent action dispatches are made using the various `#send` methods. These + # methods always return immediately. At some point later, in another thread, + # the following will happen: + # + # 1. The given `action` will be applied to the state of the Agent and the + # `args`, if any were supplied. + # 2. The return value of `action` will be passed to the validator lambda, + # if one has been set on the Agent. + # 3. If the validator succeeds or if no validator was given, the return value + # of the given `action` will become the new `#value` of the Agent. See + # `#initialize` for details. + # 4. If any observers were added to the Agent, they will be notified. See + # `#add_observer` for details. + # 5. If during the `action` execution any other dispatches are made (directly + # or indirectly), they will be held until after the `#value` of the Agent + # has been changed. + # + # If any exceptions are thrown by an action function, no nested dispatches + # will occur, and the exception will be cached in the Agent itself. When an + # Agent has errors cached, any subsequent interactions will immediately throw + # an exception, until the agent's errors are cleared. Agent errors can be + # examined with `#error` and the agent restarted with `#restart`. + # + # The actions of all Agents get interleaved amongst threads in a thread pool. + # At any point in time, at most one action for each Agent is being executed. + # Actions dispatched to an agent from another single agent or thread will + # occur in the order they were sent, potentially interleaved with actions + # dispatched to the same agent from other sources. The `#send` method should + # be used for actions that are CPU limited, while the `#send_off` method is + # appropriate for actions that may block on IO. + # + # Unlike in Clojure, `Agent` cannot participate in `Concurrent::TVar` transactions. + # + # ## Example + # + # ``` + # def next_fibonacci(set = nil) + # return [0, 1] if set.nil? + # set + [set[-2..-1].reduce{|sum,x| sum + x }] + # end + # + # # create an agent with an initial value + # agent = Concurrent::Agent.new(next_fibonacci) + # + # # send a few update requests + # 5.times do + # agent.send{|set| next_fibonacci(set) } + # end + # + # # wait for them to complete + # agent.await + # + # # get the current value + # agent.value #=> [0, 1, 1, 2, 3, 5, 8] + # ``` + # + # ## Observation + # + # Agents support observers through the {Concurrent::Observable} mixin module. + # Notification of observers occurs every time an action dispatch returns and + # the new value is successfully validated. Observation will *not* occur if the + # action raises an exception, if validation fails, or when a {#restart} occurs. + # + # When notified the observer will receive three arguments: `time`, `old_value`, + # and `new_value`. The `time` argument is the time at which the value change + # occurred. The `old_value` is the value of the Agent when the action began + # processing. The `new_value` is the value to which the Agent was set when the + # action completed. Note that `old_value` and `new_value` may be the same. + # This is not an error. It simply means that the action returned the same + # value. + # + # ## Nested Actions + # + # It is possible for an Agent action to post further actions back to itself. + # The nested actions will be enqueued normally then processed *after* the + # outer action completes, in the order they were sent, possibly interleaved + # with action dispatches from other threads. Nested actions never deadlock + # with one another and a failure in a nested action will never affect the + # outer action. + # + # Nested actions can be called using the Agent reference from the enclosing + # scope or by passing the reference in as a "send" argument. Nested actions + # cannot be post using `self` from within the action block/proc/lambda; `self` + # in this context will not reference the Agent. The preferred method for + # dispatching nested actions is to pass the Agent as an argument. This allows + # Ruby to more effectively manage the closing scope. + # + # Prefer this: + # + # ``` + # agent = Concurrent::Agent.new(0) + # agent.send(agent) do |value, this| + # this.send {|v| v + 42 } + # 3.14 + # end + # agent.value #=> 45.14 + # ``` + # + # Over this: + # + # ``` + # agent = Concurrent::Agent.new(0) + # agent.send do |value| + # agent.send {|v| v + 42 } + # 3.14 + # end + # ``` + # + # @!macro agent_await_warning + # + # **NOTE** Never, *under any circumstances*, call any of the "await" methods + # ({#await}, {#await_for}, {#await_for!}, and {#wait}) from within an action + # block/proc/lambda. The call will block the Agent and will always fail. + # Calling either {#await} or {#wait} (with a timeout of `nil`) will + # hopelessly deadlock the Agent with no possibility of recovery. + # + # @!macro thread_safe_variable_comparison + # + # @see http://clojure.org/Agents Clojure Agents + # @see http://clojure.org/state Values and Change - Clojure's approach to Identity and State + class Agent < Synchronization::LockableObject + include Concern::Observable + + ERROR_MODES = [:continue, :fail].freeze + private_constant :ERROR_MODES + + AWAIT_FLAG = ::Object.new + private_constant :AWAIT_FLAG + + AWAIT_ACTION = ->(value, latch) { latch.count_down; AWAIT_FLAG } + private_constant :AWAIT_ACTION + + DEFAULT_ERROR_HANDLER = ->(agent, error) { nil } + private_constant :DEFAULT_ERROR_HANDLER + + DEFAULT_VALIDATOR = ->(value) { true } + private_constant :DEFAULT_VALIDATOR + + Job = Struct.new(:action, :args, :executor, :caller) + private_constant :Job + + # Raised during action processing or any other time in an Agent's lifecycle. + class Error < StandardError + def initialize(message = nil) + message ||= 'agent must be restarted before jobs can post' + super(message) + end + end + + # Raised when a new value obtained during action processing or at `#restart` + # fails validation. + class ValidationError < Error + def initialize(message = nil) + message ||= 'invalid value' + super(message) + end + end + + # The error mode this Agent is operating in. See {#initialize} for details. + attr_reader :error_mode + + # Create a new `Agent` with the given initial value and options. + # + # The `:validator` option must be `nil` or a side-effect free proc/lambda + # which takes one argument. On any intended value change the validator, if + # provided, will be called. If the new value is invalid the validator should + # return `false` or raise an error. + # + # The `:error_handler` option must be `nil` or a proc/lambda which takes two + # arguments. When an action raises an error or validation fails, either by + # returning false or raising an error, the error handler will be called. The + # arguments to the error handler will be a reference to the agent itself and + # the error object which was raised. + # + # The `:error_mode` may be either `:continue` (the default if an error + # handler is given) or `:fail` (the default if error handler nil or not + # given). + # + # If an action being run by the agent throws an error or doesn't pass + # validation the error handler, if present, will be called. After the + # handler executes if the error mode is `:continue` the Agent will continue + # as if neither the action that caused the error nor the error itself ever + # happened. + # + # If the mode is `:fail` the Agent will become {#failed?} and will stop + # accepting new action dispatches. Any previously queued actions will be + # held until {#restart} is called. The {#value} method will still work, + # returning the value of the Agent before the error. + # + # @param [Object] initial the initial value + # @param [Hash] opts the configuration options + # + # @option opts [Symbol] :error_mode either `:continue` or `:fail` + # @option opts [nil, Proc] :error_handler the (optional) error handler + # @option opts [nil, Proc] :validator the (optional) validation procedure + def initialize(initial, opts = {}) + super() + synchronize { ns_initialize(initial, opts) } + end + + # The current value (state) of the Agent, irrespective of any pending or + # in-progress actions. The value is always available and is non-blocking. + # + # @return [Object] the current value + def value + @current.value # TODO (pitr 12-Sep-2015): broken unsafe read? + end + + alias_method :deref, :value + + # When {#failed?} and {#error_mode} is `:fail`, returns the error object + # which caused the failure, else `nil`. When {#error_mode} is `:continue` + # will *always* return `nil`. + # + # @return [nil, Error] the error which caused the failure when {#failed?} + def error + @error.value + end + + alias_method :reason, :error + + # @!macro agent_send + # + # Dispatches an action to the Agent and returns immediately. Subsequently, + # in a thread from a thread pool, the {#value} will be set to the return + # value of the action. Action dispatches are only allowed when the Agent + # is not {#failed?}. + # + # The action must be a block/proc/lambda which takes 1 or more arguments. + # The first argument is the current {#value} of the Agent. Any arguments + # passed to the send method via the `args` parameter will be passed to the + # action as the remaining arguments. The action must return the new value + # of the Agent. + # + # * {#send} and {#send!} should be used for actions that are CPU limited + # * {#send_off}, {#send_off!}, and {#<<} are appropriate for actions that + # may block on IO + # * {#send_via} and {#send_via!} are used when a specific executor is to + # be used for the action + # + # @param [Array] args zero or more arguments to be passed to + # the action + # @param [Proc] action the action dispatch to be enqueued + # + # @yield [agent, value, *args] process the old value and return the new + # @yieldparam [Object] value the current {#value} of the Agent + # @yieldparam [Array] args zero or more arguments to pass to the + # action + # @yieldreturn [Object] the new value of the Agent + # + # @!macro send_return + # @return [Boolean] true if the action is successfully enqueued, false if + # the Agent is {#failed?} + def send(*args, &action) + enqueue_action_job(action, args, Concurrent.global_fast_executor) + end + + # @!macro agent_send + # + # @!macro send_bang_return_and_raise + # @return [Boolean] true if the action is successfully enqueued + # @raise [Concurrent::Agent::Error] if the Agent is {#failed?} + def send!(*args, &action) + raise Error.new unless send(*args, &action) + true + end + + # @!macro agent_send + # @!macro send_return + def send_off(*args, &action) + enqueue_action_job(action, args, Concurrent.global_io_executor) + end + + alias_method :post, :send_off + + # @!macro agent_send + # @!macro send_bang_return_and_raise + def send_off!(*args, &action) + raise Error.new unless send_off(*args, &action) + true + end + + # @!macro agent_send + # @!macro send_return + # @param [Concurrent::ExecutorService] executor the executor on which the + # action is to be dispatched + def send_via(executor, *args, &action) + enqueue_action_job(action, args, executor) + end + + # @!macro agent_send + # @!macro send_bang_return_and_raise + # @param [Concurrent::ExecutorService] executor the executor on which the + # action is to be dispatched + def send_via!(executor, *args, &action) + raise Error.new unless send_via(executor, *args, &action) + true + end + + # Dispatches an action to the Agent and returns immediately. Subsequently, + # in a thread from a thread pool, the {#value} will be set to the return + # value of the action. Appropriate for actions that may block on IO. + # + # @param [Proc] action the action dispatch to be enqueued + # @return [Concurrent::Agent] self + # @see #send_off + def <<(action) + send_off(&action) + self + end + + # Blocks the current thread (indefinitely!) until all actions dispatched + # thus far, from this thread or nested by the Agent, have occurred. Will + # block when {#failed?}. Will never return if a failed Agent is {#restart} + # with `:clear_actions` true. + # + # Returns a reference to `self` to support method chaining: + # + # ``` + # current_value = agent.await.value + # ``` + # + # @return [Boolean] self + # + # @!macro agent_await_warning + def await + wait(nil) + self + end + + # Blocks the current thread until all actions dispatched thus far, from this + # thread or nested by the Agent, have occurred, or the timeout (in seconds) + # has elapsed. + # + # @param [Float] timeout the maximum number of seconds to wait + # @return [Boolean] true if all actions complete before timeout else false + # + # @!macro agent_await_warning + def await_for(timeout) + wait(timeout.to_f) + end + + # Blocks the current thread until all actions dispatched thus far, from this + # thread or nested by the Agent, have occurred, or the timeout (in seconds) + # has elapsed. + # + # @param [Float] timeout the maximum number of seconds to wait + # @return [Boolean] true if all actions complete before timeout + # + # @raise [Concurrent::TimeoutError] when timout is reached + # + # @!macro agent_await_warning + def await_for!(timeout) + raise Concurrent::TimeoutError unless wait(timeout.to_f) + true + end + + # Blocks the current thread until all actions dispatched thus far, from this + # thread or nested by the Agent, have occurred, or the timeout (in seconds) + # has elapsed. Will block indefinitely when timeout is nil or not given. + # + # Provided mainly for consistency with other classes in this library. Prefer + # the various `await` methods instead. + # + # @param [Float] timeout the maximum number of seconds to wait + # @return [Boolean] true if all actions complete before timeout else false + # + # @!macro agent_await_warning + def wait(timeout = nil) + latch = Concurrent::CountDownLatch.new(1) + enqueue_await_job(latch) + latch.wait(timeout) + end + + # Is the Agent in a failed state? + # + # @see #restart + def failed? + !@error.value.nil? + end + + alias_method :stopped?, :failed? + + # When an Agent is {#failed?}, changes the Agent {#value} to `new_value` + # then un-fails the Agent so that action dispatches are allowed again. If + # the `:clear_actions` option is give and true, any actions queued on the + # Agent that were being held while it was failed will be discarded, + # otherwise those held actions will proceed. The `new_value` must pass the + # validator if any, or `restart` will raise an exception and the Agent will + # remain failed with its old {#value} and {#error}. Observers, if any, will + # not be notified of the new state. + # + # @param [Object] new_value the new value for the Agent once restarted + # @param [Hash] opts the configuration options + # @option opts [Symbol] :clear_actions true if all enqueued but unprocessed + # actions should be discarded on restart, else false (default: false) + # @return [Boolean] true + # + # @raise [Concurrent:AgentError] when not failed + def restart(new_value, opts = {}) + clear_actions = opts.fetch(:clear_actions, false) + synchronize do + raise Error.new('agent is not failed') unless failed? + raise ValidationError unless ns_validate(new_value) + @current.value = new_value + @error.value = nil + @queue.clear if clear_actions + ns_post_next_job unless @queue.empty? + end + true + end + + class << self + + # Blocks the current thread (indefinitely!) until all actions dispatched + # thus far to all the given Agents, from this thread or nested by the + # given Agents, have occurred. Will block when any of the agents are + # failed. Will never return if a failed Agent is restart with + # `:clear_actions` true. + # + # @param [Array] agents the Agents on which to wait + # @return [Boolean] true + # + # @!macro agent_await_warning + def await(*agents) + agents.each { |agent| agent.await } + true + end + + # Blocks the current thread until all actions dispatched thus far to all + # the given Agents, from this thread or nested by the given Agents, have + # occurred, or the timeout (in seconds) has elapsed. + # + # @param [Float] timeout the maximum number of seconds to wait + # @param [Array] agents the Agents on which to wait + # @return [Boolean] true if all actions complete before timeout else false + # + # @!macro agent_await_warning + def await_for(timeout, *agents) + end_at = Concurrent.monotonic_time + timeout.to_f + ok = agents.length.times do |i| + break false if (delay = end_at - Concurrent.monotonic_time) < 0 + break false unless agents[i].await_for(delay) + end + !!ok + end + + # Blocks the current thread until all actions dispatched thus far to all + # the given Agents, from this thread or nested by the given Agents, have + # occurred, or the timeout (in seconds) has elapsed. + # + # @param [Float] timeout the maximum number of seconds to wait + # @param [Array] agents the Agents on which to wait + # @return [Boolean] true if all actions complete before timeout + # + # @raise [Concurrent::TimeoutError] when timout is reached + # @!macro agent_await_warning + def await_for!(timeout, *agents) + raise Concurrent::TimeoutError unless await_for(timeout, *agents) + true + end + end + + private + + def ns_initialize(initial, opts) + @error_mode = opts[:error_mode] + @error_handler = opts[:error_handler] + + if @error_mode && !ERROR_MODES.include?(@error_mode) + raise ArgumentError.new('unrecognized error mode') + elsif @error_mode.nil? + @error_mode = @error_handler ? :continue : :fail + end + + @error_handler ||= DEFAULT_ERROR_HANDLER + @validator = opts.fetch(:validator, DEFAULT_VALIDATOR) + @current = Concurrent::AtomicReference.new(initial) + @error = Concurrent::AtomicReference.new(nil) + @caller = Concurrent::ThreadLocalVar.new(nil) + @queue = [] + + self.observers = Collection::CopyOnNotifyObserverSet.new + end + + def enqueue_action_job(action, args, executor) + raise ArgumentError.new('no action given') unless action + job = Job.new(action, args, executor, @caller.value || Thread.current.object_id) + synchronize { ns_enqueue_job(job) } + end + + def enqueue_await_job(latch) + synchronize do + if (index = ns_find_last_job_for_thread) + job = Job.new(AWAIT_ACTION, [latch], Concurrent.global_immediate_executor, + Thread.current.object_id) + ns_enqueue_job(job, index+1) + else + latch.count_down + true + end + end + end + + def ns_enqueue_job(job, index = nil) + # a non-nil index means this is an await job + return false if index.nil? && failed? + index ||= @queue.length + @queue.insert(index, job) + # if this is the only job, post to executor + ns_post_next_job if @queue.length == 1 + true + end + + def ns_post_next_job + @queue.first.executor.post { execute_next_job } + end + + def execute_next_job + job = synchronize { @queue.first } + old_value = @current.value + + @caller.value = job.caller # for nested actions + new_value = job.action.call(old_value, *job.args) + @caller.value = nil + + return if new_value == AWAIT_FLAG + + if ns_validate(new_value) + @current.value = new_value + observers.notify_observers(Time.now, old_value, new_value) + else + handle_error(ValidationError.new) + end + rescue => error + handle_error(error) + ensure + synchronize do + @queue.shift + unless failed? || @queue.empty? + ns_post_next_job + end + end + end + + def ns_validate(value) + @validator.call(value) + rescue + false + end + + def handle_error(error) + # stop new jobs from posting + @error.value = error if @error_mode == :fail + @error_handler.call(self, error) + rescue + # do nothing + end + + def ns_find_last_job_for_thread + @queue.rindex { |job| job.caller == Thread.current.object_id } + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/array.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/array.rb new file mode 100644 index 0000000..60e5b56 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/array.rb @@ -0,0 +1,66 @@ +require 'concurrent/utility/engine' +require 'concurrent/thread_safe/util' + +module Concurrent + + # @!macro concurrent_array + # + # A thread-safe subclass of Array. This version locks against the object + # itself for every method call, ensuring only one thread can be reading + # or writing at a time. This includes iteration methods like `#each`. + # + # @note `a += b` is **not** a **thread-safe** operation on + # `Concurrent::Array`. It reads array `a`, then it creates new `Concurrent::Array` + # which is concatenation of `a` and `b`, then it writes the concatenation to `a`. + # The read and write are independent operations they do not form a single atomic + # operation therefore when two `+=` operations are executed concurrently updates + # may be lost. Use `#concat` instead. + # + # @see http://ruby-doc.org/core/Array.html Ruby standard library `Array` + + # @!macro internal_implementation_note + ArrayImplementation = case + when Concurrent.on_cruby? + # Array is thread-safe in practice because CRuby runs + # threads one at a time and does not do context + # switching during the execution of C functions. + ::Array + + when Concurrent.on_jruby? + require 'jruby/synchronized' + + class JRubyArray < ::Array + include JRuby::Synchronized + end + JRubyArray + + when Concurrent.on_rbx? + require 'monitor' + require 'concurrent/thread_safe/util/data_structures' + + class RbxArray < ::Array + end + + ThreadSafe::Util.make_synchronized_on_rbx RbxArray + RbxArray + + when Concurrent.on_truffleruby? + require 'concurrent/thread_safe/util/data_structures' + + class TruffleRubyArray < ::Array + end + + ThreadSafe::Util.make_synchronized_on_truffleruby TruffleRubyArray + TruffleRubyArray + + else + warn 'Possibly unsupported Ruby implementation' + ::Array + end + private_constant :ArrayImplementation + + # @!macro concurrent_array + class Array < ArrayImplementation + end + +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/async.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/async.rb new file mode 100644 index 0000000..f9f8adf --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/async.rb @@ -0,0 +1,449 @@ +require 'concurrent/configuration' +require 'concurrent/ivar' +require 'concurrent/synchronization/lockable_object' + +module Concurrent + + # A mixin module that provides simple asynchronous behavior to a class, + # turning it into a simple actor. Loosely based on Erlang's + # [gen_server](http://www.erlang.org/doc/man/gen_server.html), but without + # supervision or linking. + # + # A more feature-rich {Concurrent::Actor} is also available when the + # capabilities of `Async` are too limited. + # + # ```cucumber + # Feature: + # As a stateful, plain old Ruby class + # I want safe, asynchronous behavior + # So my long-running methods don't block the main thread + # ``` + # + # The `Async` module is a way to mix simple yet powerful asynchronous + # capabilities into any plain old Ruby object or class, turning each object + # into a simple Actor. Method calls are processed on a background thread. The + # caller is free to perform other actions while processing occurs in the + # background. + # + # Method calls to the asynchronous object are made via two proxy methods: + # `async` (alias `cast`) and `await` (alias `call`). These proxy methods post + # the method call to the object's background thread and return a "future" + # which will eventually contain the result of the method call. + # + # This behavior is loosely patterned after Erlang's `gen_server` behavior. + # When an Erlang module implements the `gen_server` behavior it becomes + # inherently asynchronous. The `start` or `start_link` function spawns a + # process (similar to a thread but much more lightweight and efficient) and + # returns the ID of the process. Using the process ID, other processes can + # send messages to the `gen_server` via the `cast` and `call` methods. Unlike + # Erlang's `gen_server`, however, `Async` classes do not support linking or + # supervision trees. + # + # ## Basic Usage + # + # When this module is mixed into a class, objects of the class become inherently + # asynchronous. Each object gets its own background thread on which to post + # asynchronous method calls. Asynchronous method calls are executed in the + # background one at a time in the order they are received. + # + # To create an asynchronous class, simply mix in the `Concurrent::Async` module: + # + # ``` + # class Hello + # include Concurrent::Async + # + # def hello(name) + # "Hello, #{name}!" + # end + # end + # ``` + # + # Mixing this module into a class provides each object two proxy methods: + # `async` and `await`. These methods are thread safe with respect to the + # enclosing object. The former proxy allows methods to be called + # asynchronously by posting to the object's internal thread. The latter proxy + # allows a method to be called synchronously but does so safely with respect + # to any pending asynchronous method calls and ensures proper ordering. Both + # methods return a {Concurrent::IVar} which can be inspected for the result + # of the proxied method call. Calling a method with `async` will return a + # `:pending` `IVar` whereas `await` will return a `:complete` `IVar`. + # + # ``` + # class Echo + # include Concurrent::Async + # + # def echo(msg) + # print "#{msg}\n" + # end + # end + # + # horn = Echo.new + # horn.echo('zero') # synchronous, not thread-safe + # # returns the actual return value of the method + # + # horn.async.echo('one') # asynchronous, non-blocking, thread-safe + # # returns an IVar in the :pending state + # + # horn.await.echo('two') # synchronous, blocking, thread-safe + # # returns an IVar in the :complete state + # ``` + # + # ## Let It Fail + # + # The `async` and `await` proxy methods have built-in error protection based + # on Erlang's famous "let it fail" philosophy. Instance methods should not be + # programmed defensively. When an exception is raised by a delegated method + # the proxy will rescue the exception, expose it to the caller as the `reason` + # attribute of the returned future, then process the next method call. + # + # ## Calling Methods Internally + # + # External method calls should *always* use the `async` and `await` proxy + # methods. When one method calls another method, the `async` proxy should + # rarely be used and the `await` proxy should *never* be used. + # + # When an object calls one of its own methods using the `await` proxy the + # second call will be enqueued *behind* the currently running method call. + # Any attempt to wait on the result will fail as the second call will never + # run until after the current call completes. + # + # Calling a method using the `await` proxy from within a method that was + # itself called using `async` or `await` will irreversibly deadlock the + # object. Do *not* do this, ever. + # + # ## Instance Variables and Attribute Accessors + # + # Instance variables do not need to be thread-safe so long as they are private. + # Asynchronous method calls are processed in the order they are received and + # are processed one at a time. Therefore private instance variables can only + # be accessed by one thread at a time. This is inherently thread-safe. + # + # When using private instance variables within asynchronous methods, the best + # practice is to read the instance variable into a local variable at the start + # of the method then update the instance variable at the *end* of the method. + # This way, should an exception be raised during method execution the internal + # state of the object will not have been changed. + # + # ### Reader Attributes + # + # The use of `attr_reader` is discouraged. Internal state exposed externally, + # when necessary, should be done through accessor methods. The instance + # variables exposed by these methods *must* be thread-safe, or they must be + # called using the `async` and `await` proxy methods. These two approaches are + # subtly different. + # + # When internal state is accessed via the `async` and `await` proxy methods, + # the returned value represents the object's state *at the time the call is + # processed*, which may *not* be the state of the object at the time the call + # is made. + # + # To get the state *at the current* time, irrespective of an enqueued method + # calls, a reader method must be called directly. This is inherently unsafe + # unless the instance variable is itself thread-safe, preferably using one + # of the thread-safe classes within this library. Because the thread-safe + # classes within this library are internally-locking or non-locking, they can + # be safely used from within asynchronous methods without causing deadlocks. + # + # Generally speaking, the best practice is to *not* expose internal state via + # reader methods. The best practice is to simply use the method's return value. + # + # ### Writer Attributes + # + # Writer attributes should never be used with asynchronous classes. Changing + # the state externally, even when done in the thread-safe way, is not logically + # consistent. Changes to state need to be timed with respect to all asynchronous + # method calls which my be in-process or enqueued. The only safe practice is to + # pass all necessary data to each method as arguments and let the method update + # the internal state as necessary. + # + # ## Class Constants, Variables, and Methods + # + # ### Class Constants + # + # Class constants do not need to be thread-safe. Since they are read-only and + # immutable they may be safely read both externally and from within + # asynchronous methods. + # + # ### Class Variables + # + # Class variables should be avoided. Class variables represent shared state. + # Shared state is anathema to concurrency. Should there be a need to share + # state using class variables they *must* be thread-safe, preferably + # using the thread-safe classes within this library. When updating class + # variables, never assign a new value/object to the variable itself. Assignment + # is not thread-safe in Ruby. Instead, use the thread-safe update functions + # of the variable itself to change the value. + # + # The best practice is to *never* use class variables with `Async` classes. + # + # ### Class Methods + # + # Class methods which are pure functions are safe. Class methods which modify + # class variables should be avoided, for all the reasons listed above. + # + # ## An Important Note About Thread Safe Guarantees + # + # > Thread safe guarantees can only be made when asynchronous method calls + # > are not mixed with direct method calls. Use only direct method calls + # > when the object is used exclusively on a single thread. Use only + # > `async` and `await` when the object is shared between threads. Once you + # > call a method using `async` or `await`, you should no longer call methods + # > directly on the object. Use `async` and `await` exclusively from then on. + # + # @example + # + # class Echo + # include Concurrent::Async + # + # def echo(msg) + # print "#{msg}\n" + # end + # end + # + # horn = Echo.new + # horn.echo('zero') # synchronous, not thread-safe + # # returns the actual return value of the method + # + # horn.async.echo('one') # asynchronous, non-blocking, thread-safe + # # returns an IVar in the :pending state + # + # horn.await.echo('two') # synchronous, blocking, thread-safe + # # returns an IVar in the :complete state + # + # @see Concurrent::Actor + # @see https://en.wikipedia.org/wiki/Actor_model "Actor Model" at Wikipedia + # @see http://www.erlang.org/doc/man/gen_server.html Erlang gen_server + # @see http://c2.com/cgi/wiki?LetItCrash "Let It Crash" at http://c2.com/ + module Async + + # @!method self.new(*args, &block) + # + # Instanciate a new object and ensure proper initialization of the + # synchronization mechanisms. + # + # @param [Array] args Zero or more arguments to be passed to the + # object's initializer. + # @param [Proc] block Optional block to pass to the object's initializer. + # @return [Object] A properly initialized object of the asynchronous class. + + # Check for the presence of a method on an object and determine if a given + # set of arguments matches the required arity. + # + # @param [Object] obj the object to check against + # @param [Symbol] method the method to check the object for + # @param [Array] args zero or more arguments for the arity check + # + # @raise [NameError] the object does not respond to `method` method + # @raise [ArgumentError] the given `args` do not match the arity of `method` + # + # @note This check is imperfect because of the way Ruby reports the arity of + # methods with a variable number of arguments. It is possible to determine + # if too few arguments are given but impossible to determine if too many + # arguments are given. This check may also fail to recognize dynamic behavior + # of the object, such as methods simulated with `method_missing`. + # + # @see http://www.ruby-doc.org/core-2.1.1/Method.html#method-i-arity Method#arity + # @see http://ruby-doc.org/core-2.1.0/Object.html#method-i-respond_to-3F Object#respond_to? + # @see http://www.ruby-doc.org/core-2.1.0/BasicObject.html#method-i-method_missing BasicObject#method_missing + # + # @!visibility private + def self.validate_argc(obj, method, *args) + argc = args.length + arity = obj.method(method).arity + + if arity >= 0 && argc != arity + raise ArgumentError.new("wrong number of arguments (#{argc} for #{arity})") + elsif arity < 0 && (arity = (arity + 1).abs) > argc + raise ArgumentError.new("wrong number of arguments (#{argc} for #{arity}..*)") + end + end + + # @!visibility private + def self.included(base) + base.singleton_class.send(:alias_method, :original_new, :new) + base.extend(ClassMethods) + super(base) + end + + # @!visibility private + module ClassMethods + def new(*args, &block) + obj = original_new(*args, &block) + obj.send(:init_synchronization) + obj + end + ruby2_keywords :new if respond_to?(:ruby2_keywords, true) + end + private_constant :ClassMethods + + # Delegates asynchronous, thread-safe method calls to the wrapped object. + # + # @!visibility private + class AsyncDelegator < Synchronization::LockableObject + safe_initialization! + + # Create a new delegator object wrapping the given delegate. + # + # @param [Object] delegate the object to wrap and delegate method calls to + def initialize(delegate) + super() + @delegate = delegate + @queue = [] + @executor = Concurrent.global_io_executor + @ruby_pid = $$ + end + + # Delegates method calls to the wrapped object. + # + # @param [Symbol] method the method being called + # @param [Array] args zero or more arguments to the method + # + # @return [IVar] the result of the method call + # + # @raise [NameError] the object does not respond to `method` method + # @raise [ArgumentError] the given `args` do not match the arity of `method` + def method_missing(method, *args, &block) + super unless @delegate.respond_to?(method) + Async::validate_argc(@delegate, method, *args) + + ivar = Concurrent::IVar.new + synchronize do + reset_if_forked + @queue.push [ivar, method, args, block] + @executor.post { perform } if @queue.length == 1 + end + + ivar + end + + # Check whether the method is responsive + # + # @param [Symbol] method the method being called + def respond_to_missing?(method, include_private = false) + @delegate.respond_to?(method) || super + end + + # Perform all enqueued tasks. + # + # This method must be called from within the executor. It must not be + # called while already running. It will loop until the queue is empty. + def perform + loop do + ivar, method, args, block = synchronize { @queue.first } + break unless ivar # queue is empty + + begin + ivar.set(@delegate.send(method, *args, &block)) + rescue => error + ivar.fail(error) + end + + synchronize do + @queue.shift + return if @queue.empty? + end + end + end + + def reset_if_forked + if $$ != @ruby_pid + @queue.clear + @ruby_pid = $$ + end + end + end + private_constant :AsyncDelegator + + # Delegates synchronous, thread-safe method calls to the wrapped object. + # + # @!visibility private + class AwaitDelegator + + # Create a new delegator object wrapping the given delegate. + # + # @param [AsyncDelegator] delegate the object to wrap and delegate method calls to + def initialize(delegate) + @delegate = delegate + end + + # Delegates method calls to the wrapped object. + # + # @param [Symbol] method the method being called + # @param [Array] args zero or more arguments to the method + # + # @return [IVar] the result of the method call + # + # @raise [NameError] the object does not respond to `method` method + # @raise [ArgumentError] the given `args` do not match the arity of `method` + def method_missing(method, *args, &block) + ivar = @delegate.send(method, *args, &block) + ivar.wait + ivar + end + + # Check whether the method is responsive + # + # @param [Symbol] method the method being called + def respond_to_missing?(method, include_private = false) + @delegate.respond_to?(method) || super + end + end + private_constant :AwaitDelegator + + # Causes the chained method call to be performed asynchronously on the + # object's thread. The delegated method will return a future in the + # `:pending` state and the method call will have been scheduled on the + # object's thread. The final disposition of the method call can be obtained + # by inspecting the returned future. + # + # @!macro async_thread_safety_warning + # @note The method call is guaranteed to be thread safe with respect to + # all other method calls against the same object that are called with + # either `async` or `await`. The mutable nature of Ruby references + # (and object orientation in general) prevent any other thread safety + # guarantees. Do NOT mix direct method calls with delegated method calls. + # Use *only* delegated method calls when sharing the object between threads. + # + # @return [Concurrent::IVar] the pending result of the asynchronous operation + # + # @raise [NameError] the object does not respond to the requested method + # @raise [ArgumentError] the given `args` do not match the arity of + # the requested method + def async + @__async_delegator__ + end + alias_method :cast, :async + + # Causes the chained method call to be performed synchronously on the + # current thread. The delegated will return a future in either the + # `:fulfilled` or `:rejected` state and the delegated method will have + # completed. The final disposition of the delegated method can be obtained + # by inspecting the returned future. + # + # @!macro async_thread_safety_warning + # + # @return [Concurrent::IVar] the completed result of the synchronous operation + # + # @raise [NameError] the object does not respond to the requested method + # @raise [ArgumentError] the given `args` do not match the arity of the + # requested method + def await + @__await_delegator__ + end + alias_method :call, :await + + # Initialize the internal serializer and other stnchronization mechanisms. + # + # @note This method *must* be called immediately upon object construction. + # This is the only way thread-safe initialization can be guaranteed. + # + # @!visibility private + def init_synchronization + return self if defined?(@__async_initialized__) && @__async_initialized__ + @__async_initialized__ = true + @__async_delegator__ = AsyncDelegator.new(self) + @__await_delegator__ = AwaitDelegator.new(@__async_delegator__) + self + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atom.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atom.rb new file mode 100644 index 0000000..8a45730 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atom.rb @@ -0,0 +1,222 @@ +require 'concurrent/atomic/atomic_reference' +require 'concurrent/collection/copy_on_notify_observer_set' +require 'concurrent/concern/observable' +require 'concurrent/synchronization' + +# @!macro thread_safe_variable_comparison +# +# ## Thread-safe Variable Classes +# +# Each of the thread-safe variable classes is designed to solve a different +# problem. In general: +# +# * *{Concurrent::Agent}:* Shared, mutable variable providing independent, +# uncoordinated, *asynchronous* change of individual values. Best used when +# the value will undergo frequent, complex updates. Suitable when the result +# of an update does not need to be known immediately. +# * *{Concurrent::Atom}:* Shared, mutable variable providing independent, +# uncoordinated, *synchronous* change of individual values. Best used when +# the value will undergo frequent reads but only occasional, though complex, +# updates. Suitable when the result of an update must be known immediately. +# * *{Concurrent::AtomicReference}:* A simple object reference that can be updated +# atomically. Updates are synchronous but fast. Best used when updates a +# simple set operations. Not suitable when updates are complex. +# {Concurrent::AtomicBoolean} and {Concurrent::AtomicFixnum} are similar +# but optimized for the given data type. +# * *{Concurrent::Exchanger}:* Shared, stateless synchronization point. Used +# when two or more threads need to exchange data. The threads will pair then +# block on each other until the exchange is complete. +# * *{Concurrent::MVar}:* Shared synchronization point. Used when one thread +# must give a value to another, which must take the value. The threads will +# block on each other until the exchange is complete. +# * *{Concurrent::ThreadLocalVar}:* Shared, mutable, isolated variable which +# holds a different value for each thread which has access. Often used as +# an instance variable in objects which must maintain different state +# for different threads. +# * *{Concurrent::TVar}:* Shared, mutable variables which provide +# *coordinated*, *synchronous*, change of *many* stated. Used when multiple +# value must change together, in an all-or-nothing transaction. + + +module Concurrent + + # Atoms provide a way to manage shared, synchronous, independent state. + # + # An atom is initialized with an initial value and an optional validation + # proc. At any time the value of the atom can be synchronously and safely + # changed. If a validator is given at construction then any new value + # will be checked against the validator and will be rejected if the + # validator returns false or raises an exception. + # + # There are two ways to change the value of an atom: {#compare_and_set} and + # {#swap}. The former will set the new value if and only if it validates and + # the current value matches the new value. The latter will atomically set the + # new value to the result of running the given block if and only if that + # value validates. + # + # ## Example + # + # ``` + # def next_fibonacci(set = nil) + # return [0, 1] if set.nil? + # set + [set[-2..-1].reduce{|sum,x| sum + x }] + # end + # + # # create an atom with an initial value + # atom = Concurrent::Atom.new(next_fibonacci) + # + # # send a few update requests + # 5.times do + # atom.swap{|set| next_fibonacci(set) } + # end + # + # # get the current value + # atom.value #=> [0, 1, 1, 2, 3, 5, 8] + # ``` + # + # ## Observation + # + # Atoms support observers through the {Concurrent::Observable} mixin module. + # Notification of observers occurs every time the value of the Atom changes. + # When notified the observer will receive three arguments: `time`, `old_value`, + # and `new_value`. The `time` argument is the time at which the value change + # occurred. The `old_value` is the value of the Atom when the change began + # The `new_value` is the value to which the Atom was set when the change + # completed. Note that `old_value` and `new_value` may be the same. This is + # not an error. It simply means that the change operation returned the same + # value. + # + # Unlike in Clojure, `Atom` cannot participate in {Concurrent::TVar} transactions. + # + # @!macro thread_safe_variable_comparison + # + # @see http://clojure.org/atoms Clojure Atoms + # @see http://clojure.org/state Values and Change - Clojure's approach to Identity and State + class Atom < Synchronization::Object + include Concern::Observable + + safe_initialization! + attr_atomic(:value) + private :value=, :swap_value, :compare_and_set_value, :update_value + public :value + alias_method :deref, :value + + # @!method value + # The current value of the atom. + # + # @return [Object] The current value. + + # Create a new atom with the given initial value. + # + # @param [Object] value The initial value + # @param [Hash] opts The options used to configure the atom + # @option opts [Proc] :validator (nil) Optional proc used to validate new + # values. It must accept one and only one argument which will be the + # intended new value. The validator will return true if the new value + # is acceptable else return false (preferrably) or raise an exception. + # + # @!macro deref_options + # + # @raise [ArgumentError] if the validator is not a `Proc` (when given) + def initialize(value, opts = {}) + super() + @Validator = opts.fetch(:validator, -> v { true }) + self.observers = Collection::CopyOnNotifyObserverSet.new + self.value = value + end + + # Atomically swaps the value of atom using the given block. The current + # value will be passed to the block, as will any arguments passed as + # arguments to the function. The new value will be validated against the + # (optional) validator proc given at construction. If validation fails the + # value will not be changed. + # + # Internally, {#swap} reads the current value, applies the block to it, and + # attempts to compare-and-set it in. Since another thread may have changed + # the value in the intervening time, it may have to retry, and does so in a + # spin loop. The net effect is that the value will always be the result of + # the application of the supplied block to a current value, atomically. + # However, because the block might be called multiple times, it must be free + # of side effects. + # + # @note The given block may be called multiple times, and thus should be free + # of side effects. + # + # @param [Object] args Zero or more arguments passed to the block. + # + # @yield [value, args] Calculates a new value for the atom based on the + # current value and any supplied arguments. + # @yieldparam value [Object] The current value of the atom. + # @yieldparam args [Object] All arguments passed to the function, in order. + # @yieldreturn [Object] The intended new value of the atom. + # + # @return [Object] The final value of the atom after all operations and + # validations are complete. + # + # @raise [ArgumentError] When no block is given. + def swap(*args) + raise ArgumentError.new('no block given') unless block_given? + + loop do + old_value = value + new_value = yield(old_value, *args) + begin + break old_value unless valid?(new_value) + break new_value if compare_and_set(old_value, new_value) + rescue + break old_value + end + end + end + + # Atomically sets the value of atom to the new value if and only if the + # current value of the atom is identical to the old value and the new + # value successfully validates against the (optional) validator given + # at construction. + # + # @param [Object] old_value The expected current value. + # @param [Object] new_value The intended new value. + # + # @return [Boolean] True if the value is changed else false. + def compare_and_set(old_value, new_value) + if valid?(new_value) && compare_and_set_value(old_value, new_value) + observers.notify_observers(Time.now, old_value, new_value) + true + else + false + end + end + + # Atomically sets the value of atom to the new value without regard for the + # current value so long as the new value successfully validates against the + # (optional) validator given at construction. + # + # @param [Object] new_value The intended new value. + # + # @return [Object] The final value of the atom after all operations and + # validations are complete. + def reset(new_value) + old_value = value + if valid?(new_value) + self.value = new_value + observers.notify_observers(Time.now, old_value, new_value) + new_value + else + old_value + end + end + + private + + # Is the new value valid? + # + # @param [Object] new_value The intended new value. + # @return [Boolean] false if the validator function returns false or raises + # an exception else true + def valid?(new_value) + @Validator.call(new_value) + rescue + false + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb new file mode 100644 index 0000000..fcdeed7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb @@ -0,0 +1,66 @@ +require 'concurrent/constants' + +module Concurrent + + # @!macro thread_local_var + # @!macro internal_implementation_note + # @!visibility private + class AbstractThreadLocalVar + + # @!macro thread_local_var_method_initialize + def initialize(default = nil, &default_block) + if default && block_given? + raise ArgumentError, "Cannot use both value and block as default value" + end + + if block_given? + @default_block = default_block + @default = nil + else + @default_block = nil + @default = default + end + + allocate_storage + end + + # @!macro thread_local_var_method_get + def value + raise NotImplementedError + end + + # @!macro thread_local_var_method_set + def value=(value) + raise NotImplementedError + end + + # @!macro thread_local_var_method_bind + def bind(value, &block) + if block_given? + old_value = self.value + begin + self.value = value + yield + ensure + self.value = old_value + end + end + end + + protected + + # @!visibility private + def allocate_storage + raise NotImplementedError + end + + # @!visibility private + def default + if @default_block + self.value = @default_block.call + else + @default + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb new file mode 100644 index 0000000..0b0373d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb @@ -0,0 +1,126 @@ +require 'concurrent/atomic/mutex_atomic_boolean' +require 'concurrent/synchronization' + +module Concurrent + + ################################################################### + + # @!macro atomic_boolean_method_initialize + # + # Creates a new `AtomicBoolean` with the given initial value. + # + # @param [Boolean] initial the initial value + + # @!macro atomic_boolean_method_value_get + # + # Retrieves the current `Boolean` value. + # + # @return [Boolean] the current value + + # @!macro atomic_boolean_method_value_set + # + # Explicitly sets the value. + # + # @param [Boolean] value the new value to be set + # + # @return [Boolean] the current value + + # @!macro atomic_boolean_method_true_question + # + # Is the current value `true` + # + # @return [Boolean] true if the current value is `true`, else false + + # @!macro atomic_boolean_method_false_question + # + # Is the current value `false` + # + # @return [Boolean] true if the current value is `false`, else false + + # @!macro atomic_boolean_method_make_true + # + # Explicitly sets the value to true. + # + # @return [Boolean] true if value has changed, otherwise false + + # @!macro atomic_boolean_method_make_false + # + # Explicitly sets the value to false. + # + # @return [Boolean] true if value has changed, otherwise false + + ################################################################### + + # @!macro atomic_boolean_public_api + # + # @!method initialize(initial = false) + # @!macro atomic_boolean_method_initialize + # + # @!method value + # @!macro atomic_boolean_method_value_get + # + # @!method value=(value) + # @!macro atomic_boolean_method_value_set + # + # @!method true? + # @!macro atomic_boolean_method_true_question + # + # @!method false? + # @!macro atomic_boolean_method_false_question + # + # @!method make_true + # @!macro atomic_boolean_method_make_true + # + # @!method make_false + # @!macro atomic_boolean_method_make_false + + ################################################################### + + # @!visibility private + # @!macro internal_implementation_note + AtomicBooleanImplementation = case + when defined?(JavaAtomicBoolean) + JavaAtomicBoolean + when defined?(CAtomicBoolean) + CAtomicBoolean + else + MutexAtomicBoolean + end + private_constant :AtomicBooleanImplementation + + # @!macro atomic_boolean + # + # A boolean value that can be updated atomically. Reads and writes to an atomic + # boolean and thread-safe and guaranteed to succeed. Reads and writes may block + # briefly but no explicit locking is required. + # + # @!macro thread_safe_variable_comparison + # + # Performance: + # + # ``` + # Testing with ruby 2.1.2 + # Testing with Concurrent::MutexAtomicBoolean... + # 2.790000 0.000000 2.790000 ( 2.791454) + # Testing with Concurrent::CAtomicBoolean... + # 0.740000 0.000000 0.740000 ( 0.740206) + # + # Testing with jruby 1.9.3 + # Testing with Concurrent::MutexAtomicBoolean... + # 5.240000 2.520000 7.760000 ( 3.683000) + # Testing with Concurrent::JavaAtomicBoolean... + # 3.340000 0.010000 3.350000 ( 0.855000) + # ``` + # + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicBoolean.html java.util.concurrent.atomic.AtomicBoolean + # + # @!macro atomic_boolean_public_api + class AtomicBoolean < AtomicBooleanImplementation + # @return [String] Short string representation. + def to_s + format '%s value:%s>', super[0..-2], value + end + + alias_method :inspect, :to_s + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb new file mode 100644 index 0000000..c67166d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb @@ -0,0 +1,143 @@ +require 'concurrent/atomic/mutex_atomic_fixnum' +require 'concurrent/synchronization' + +module Concurrent + + ################################################################### + + # @!macro atomic_fixnum_method_initialize + # + # Creates a new `AtomicFixnum` with the given initial value. + # + # @param [Fixnum] initial the initial value + # @raise [ArgumentError] if the initial value is not a `Fixnum` + + # @!macro atomic_fixnum_method_value_get + # + # Retrieves the current `Fixnum` value. + # + # @return [Fixnum] the current value + + # @!macro atomic_fixnum_method_value_set + # + # Explicitly sets the value. + # + # @param [Fixnum] value the new value to be set + # + # @return [Fixnum] the current value + # + # @raise [ArgumentError] if the new value is not a `Fixnum` + + # @!macro atomic_fixnum_method_increment + # + # Increases the current value by the given amount (defaults to 1). + # + # @param [Fixnum] delta the amount by which to increase the current value + # + # @return [Fixnum] the current value after incrementation + + # @!macro atomic_fixnum_method_decrement + # + # Decreases the current value by the given amount (defaults to 1). + # + # @param [Fixnum] delta the amount by which to decrease the current value + # + # @return [Fixnum] the current value after decrementation + + # @!macro atomic_fixnum_method_compare_and_set + # + # Atomically sets the value to the given updated value if the current + # value == the expected value. + # + # @param [Fixnum] expect the expected value + # @param [Fixnum] update the new value + # + # @return [Boolean] true if the value was updated else false + + # @!macro atomic_fixnum_method_update + # + # Pass the current value to the given block, replacing it + # with the block's result. May retry if the value changes + # during the block's execution. + # + # @yield [Object] Calculate a new value for the atomic reference using + # given (old) value + # @yieldparam [Object] old_value the starting value of the atomic reference + # + # @return [Object] the new value + + ################################################################### + + # @!macro atomic_fixnum_public_api + # + # @!method initialize(initial = 0) + # @!macro atomic_fixnum_method_initialize + # + # @!method value + # @!macro atomic_fixnum_method_value_get + # + # @!method value=(value) + # @!macro atomic_fixnum_method_value_set + # + # @!method increment(delta = 1) + # @!macro atomic_fixnum_method_increment + # + # @!method decrement(delta = 1) + # @!macro atomic_fixnum_method_decrement + # + # @!method compare_and_set(expect, update) + # @!macro atomic_fixnum_method_compare_and_set + # + # @!method update + # @!macro atomic_fixnum_method_update + + ################################################################### + + # @!visibility private + # @!macro internal_implementation_note + AtomicFixnumImplementation = case + when defined?(JavaAtomicFixnum) + JavaAtomicFixnum + when defined?(CAtomicFixnum) + CAtomicFixnum + else + MutexAtomicFixnum + end + private_constant :AtomicFixnumImplementation + + # @!macro atomic_fixnum + # + # A numeric value that can be updated atomically. Reads and writes to an atomic + # fixnum and thread-safe and guaranteed to succeed. Reads and writes may block + # briefly but no explicit locking is required. + # + # @!macro thread_safe_variable_comparison + # + # Performance: + # + # ``` + # Testing with ruby 2.1.2 + # Testing with Concurrent::MutexAtomicFixnum... + # 3.130000 0.000000 3.130000 ( 3.136505) + # Testing with Concurrent::CAtomicFixnum... + # 0.790000 0.000000 0.790000 ( 0.785550) + # + # Testing with jruby 1.9.3 + # Testing with Concurrent::MutexAtomicFixnum... + # 5.460000 2.460000 7.920000 ( 3.715000) + # Testing with Concurrent::JavaAtomicFixnum... + # 4.520000 0.030000 4.550000 ( 1.187000) + # ``` + # + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicLong.html java.util.concurrent.atomic.AtomicLong + # + # @!macro atomic_fixnum_public_api + class AtomicFixnum < AtomicFixnumImplementation + # @return [String] Short string representation. + def to_s + format '%s value:%s>', super[0..-2], value + end + + alias_method :inspect, :to_s + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb new file mode 100644 index 0000000..f20cd46 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb @@ -0,0 +1,164 @@ +module Concurrent + # An atomic reference which maintains an object reference along with a mark bit + # that can be updated atomically. + # + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicMarkableReference.html + # java.util.concurrent.atomic.AtomicMarkableReference + class AtomicMarkableReference < ::Concurrent::Synchronization::Object + + attr_atomic(:reference) + private :reference, :reference=, :swap_reference, :compare_and_set_reference, :update_reference + + def initialize(value = nil, mark = false) + super() + self.reference = immutable_array(value, mark) + end + + # Atomically sets the value and mark to the given updated value and + # mark given both: + # - the current value == the expected value && + # - the current mark == the expected mark + # + # @param [Object] expected_val the expected value + # @param [Object] new_val the new value + # @param [Boolean] expected_mark the expected mark + # @param [Boolean] new_mark the new mark + # + # @return [Boolean] `true` if successful. A `false` return indicates + # that the actual value was not equal to the expected value or the + # actual mark was not equal to the expected mark + def compare_and_set(expected_val, new_val, expected_mark, new_mark) + # Memoize a valid reference to the current AtomicReference for + # later comparison. + current = reference + curr_val, curr_mark = current + + # Ensure that that the expected marks match. + return false unless expected_mark == curr_mark + + if expected_val.is_a? Numeric + # If the object is a numeric, we need to ensure we are comparing + # the numerical values + return false unless expected_val == curr_val + else + # Otherwise, we need to ensure we are comparing the object identity. + # Theoretically, this could be incorrect if a user monkey-patched + # `Object#equal?`, but they should know that they are playing with + # fire at that point. + return false unless expected_val.equal? curr_val + end + + prospect = immutable_array(new_val, new_mark) + + compare_and_set_reference current, prospect + end + + alias_method :compare_and_swap, :compare_and_set + + # Gets the current reference and marked values. + # + # @return [Array] the current reference and marked values + def get + reference + end + + # Gets the current value of the reference + # + # @return [Object] the current value of the reference + def value + reference[0] + end + + # Gets the current marked value + # + # @return [Boolean] the current marked value + def mark + reference[1] + end + + alias_method :marked?, :mark + + # _Unconditionally_ sets to the given value of both the reference and + # the mark. + # + # @param [Object] new_val the new value + # @param [Boolean] new_mark the new mark + # + # @return [Array] both the new value and the new mark + def set(new_val, new_mark) + self.reference = immutable_array(new_val, new_mark) + end + + # Pass the current value and marked state to the given block, replacing it + # with the block's results. May retry if the value changes during the + # block's execution. + # + # @yield [Object] Calculate a new value and marked state for the atomic + # reference using given (old) value and (old) marked + # @yieldparam [Object] old_val the starting value of the atomic reference + # @yieldparam [Boolean] old_mark the starting state of marked + # + # @return [Array] the new value and new mark + def update + loop do + old_val, old_mark = reference + new_val, new_mark = yield old_val, old_mark + + if compare_and_set old_val, new_val, old_mark, new_mark + return immutable_array(new_val, new_mark) + end + end + end + + # Pass the current value to the given block, replacing it + # with the block's result. Raise an exception if the update + # fails. + # + # @yield [Object] Calculate a new value and marked state for the atomic + # reference using given (old) value and (old) marked + # @yieldparam [Object] old_val the starting value of the atomic reference + # @yieldparam [Boolean] old_mark the starting state of marked + # + # @return [Array] the new value and marked state + # + # @raise [Concurrent::ConcurrentUpdateError] if the update fails + def try_update! + old_val, old_mark = reference + new_val, new_mark = yield old_val, old_mark + + unless compare_and_set old_val, new_val, old_mark, new_mark + fail ::Concurrent::ConcurrentUpdateError, + 'AtomicMarkableReference: Update failed due to race condition.', + 'Note: If you would like to guarantee an update, please use ' + + 'the `AtomicMarkableReference#update` method.' + end + + immutable_array(new_val, new_mark) + end + + # Pass the current value to the given block, replacing it with the + # block's result. Simply return nil if update fails. + # + # @yield [Object] Calculate a new value and marked state for the atomic + # reference using given (old) value and (old) marked + # @yieldparam [Object] old_val the starting value of the atomic reference + # @yieldparam [Boolean] old_mark the starting state of marked + # + # @return [Array] the new value and marked state, or nil if + # the update failed + def try_update + old_val, old_mark = reference + new_val, new_mark = yield old_val, old_mark + + return unless compare_and_set old_val, new_val, old_mark, new_mark + + immutable_array(new_val, new_mark) + end + + private + + def immutable_array(*args) + args.freeze + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb new file mode 100644 index 0000000..674f866 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb @@ -0,0 +1,205 @@ +require 'concurrent/synchronization' +require 'concurrent/utility/engine' +require 'concurrent/atomic_reference/numeric_cas_wrapper' + +# Shim for TruffleRuby::AtomicReference +if Concurrent.on_truffleruby? && !defined?(TruffleRuby::AtomicReference) + # @!visibility private + module TruffleRuby + AtomicReference = Truffle::AtomicReference + end +end + +module Concurrent + + # Define update methods that use direct paths + # + # @!visibility private + # @!macro internal_implementation_note + module AtomicDirectUpdate + + # @!macro atomic_reference_method_update + # + # Pass the current value to the given block, replacing it + # with the block's result. May retry if the value changes + # during the block's execution. + # + # @yield [Object] Calculate a new value for the atomic reference using + # given (old) value + # @yieldparam [Object] old_value the starting value of the atomic reference + # @return [Object] the new value + def update + true until compare_and_set(old_value = get, new_value = yield(old_value)) + new_value + end + + # @!macro atomic_reference_method_try_update + # + # Pass the current value to the given block, replacing it + # with the block's result. Return nil if the update fails. + # + # @yield [Object] Calculate a new value for the atomic reference using + # given (old) value + # @yieldparam [Object] old_value the starting value of the atomic reference + # @note This method was altered to avoid raising an exception by default. + # Instead, this method now returns `nil` in case of failure. For more info, + # please see: https://github.com/ruby-concurrency/concurrent-ruby/pull/336 + # @return [Object] the new value, or nil if update failed + def try_update + old_value = get + new_value = yield old_value + + return unless compare_and_set old_value, new_value + + new_value + end + + # @!macro atomic_reference_method_try_update! + # + # Pass the current value to the given block, replacing it + # with the block's result. Raise an exception if the update + # fails. + # + # @yield [Object] Calculate a new value for the atomic reference using + # given (old) value + # @yieldparam [Object] old_value the starting value of the atomic reference + # @note This behavior mimics the behavior of the original + # `AtomicReference#try_update` API. The reason this was changed was to + # avoid raising exceptions (which are inherently slow) by default. For more + # info: https://github.com/ruby-concurrency/concurrent-ruby/pull/336 + # @return [Object] the new value + # @raise [Concurrent::ConcurrentUpdateError] if the update fails + def try_update! + old_value = get + new_value = yield old_value + unless compare_and_set(old_value, new_value) + if $VERBOSE + raise ConcurrentUpdateError, "Update failed" + else + raise ConcurrentUpdateError, "Update failed", ConcurrentUpdateError::CONC_UP_ERR_BACKTRACE + end + end + new_value + end + end + + require 'concurrent/atomic_reference/mutex_atomic' + + # @!macro atomic_reference + # + # An object reference that may be updated atomically. All read and write + # operations have java volatile semantic. + # + # @!macro thread_safe_variable_comparison + # + # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html + # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html + # + # @!method initialize(value = nil) + # @!macro atomic_reference_method_initialize + # @param [Object] value The initial value. + # + # @!method get + # @!macro atomic_reference_method_get + # Gets the current value. + # @return [Object] the current value + # + # @!method set(new_value) + # @!macro atomic_reference_method_set + # Sets to the given value. + # @param [Object] new_value the new value + # @return [Object] the new value + # + # @!method get_and_set(new_value) + # @!macro atomic_reference_method_get_and_set + # Atomically sets to the given value and returns the old value. + # @param [Object] new_value the new value + # @return [Object] the old value + # + # @!method compare_and_set(old_value, new_value) + # @!macro atomic_reference_method_compare_and_set + # + # Atomically sets the value to the given updated value if + # the current value == the expected value. + # + # @param [Object] old_value the expected value + # @param [Object] new_value the new value + # + # @return [Boolean] `true` if successful. A `false` return indicates + # that the actual value was not equal to the expected value. + # + # @!method update + # @!macro atomic_reference_method_update + # + # @!method try_update + # @!macro atomic_reference_method_try_update + # + # @!method try_update! + # @!macro atomic_reference_method_try_update! + + + # @!macro internal_implementation_note + class ConcurrentUpdateError < ThreadError + # frozen pre-allocated backtrace to speed ConcurrentUpdateError + CONC_UP_ERR_BACKTRACE = ['backtrace elided; set verbose to enable'].freeze + end + + # @!macro internal_implementation_note + AtomicReferenceImplementation = case + when Concurrent.on_cruby? && Concurrent.c_extensions_loaded? + # @!visibility private + # @!macro internal_implementation_note + class CAtomicReference + include AtomicDirectUpdate + include AtomicNumericCompareAndSetWrapper + alias_method :compare_and_swap, :compare_and_set + end + CAtomicReference + when Concurrent.on_jruby? + # @!visibility private + # @!macro internal_implementation_note + class JavaAtomicReference + include AtomicDirectUpdate + end + JavaAtomicReference + when Concurrent.on_truffleruby? + class TruffleRubyAtomicReference < TruffleRuby::AtomicReference + include AtomicDirectUpdate + alias_method :value, :get + alias_method :value=, :set + alias_method :compare_and_swap, :compare_and_set + alias_method :swap, :get_and_set + end + TruffleRubyAtomicReference + when Concurrent.on_rbx? + # @note Extends `Rubinius::AtomicReference` version adding aliases + # and numeric logic. + # + # @!visibility private + # @!macro internal_implementation_note + class RbxAtomicReference < Rubinius::AtomicReference + alias_method :_compare_and_set, :compare_and_set + include AtomicDirectUpdate + include AtomicNumericCompareAndSetWrapper + alias_method :value, :get + alias_method :value=, :set + alias_method :swap, :get_and_set + alias_method :compare_and_swap, :compare_and_set + end + RbxAtomicReference + else + MutexAtomicReference + end + private_constant :AtomicReferenceImplementation + + # @!macro atomic_reference + class AtomicReference < AtomicReferenceImplementation + + # @return [String] Short string representation. + def to_s + format '%s value:%s>', super[0..-2], get + end + + alias_method :inspect, :to_s + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/count_down_latch.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/count_down_latch.rb new file mode 100644 index 0000000..d883aed --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/count_down_latch.rb @@ -0,0 +1,100 @@ +require 'concurrent/utility/engine' +require 'concurrent/atomic/mutex_count_down_latch' +require 'concurrent/atomic/java_count_down_latch' + +module Concurrent + + ################################################################### + + # @!macro count_down_latch_method_initialize + # + # Create a new `CountDownLatch` with the initial `count`. + # + # @param [new] count the initial count + # + # @raise [ArgumentError] if `count` is not an integer or is less than zero + + # @!macro count_down_latch_method_wait + # + # Block on the latch until the counter reaches zero or until `timeout` is reached. + # + # @param [Fixnum] timeout the number of seconds to wait for the counter or `nil` + # to block indefinitely + # @return [Boolean] `true` if the `count` reaches zero else false on `timeout` + + # @!macro count_down_latch_method_count_down + # + # Signal the latch to decrement the counter. Will signal all blocked threads when + # the `count` reaches zero. + + # @!macro count_down_latch_method_count + # + # The current value of the counter. + # + # @return [Fixnum] the current value of the counter + + ################################################################### + + # @!macro count_down_latch_public_api + # + # @!method initialize(count = 1) + # @!macro count_down_latch_method_initialize + # + # @!method wait(timeout = nil) + # @!macro count_down_latch_method_wait + # + # @!method count_down + # @!macro count_down_latch_method_count_down + # + # @!method count + # @!macro count_down_latch_method_count + + ################################################################### + + # @!visibility private + # @!macro internal_implementation_note + CountDownLatchImplementation = case + when Concurrent.on_jruby? + JavaCountDownLatch + else + MutexCountDownLatch + end + private_constant :CountDownLatchImplementation + + # @!macro count_down_latch + # + # A synchronization object that allows one thread to wait on multiple other threads. + # The thread that will wait creates a `CountDownLatch` and sets the initial value + # (normally equal to the number of other threads). The initiating thread passes the + # latch to the other threads then waits for the other threads by calling the `#wait` + # method. Each of the other threads calls `#count_down` when done with its work. + # When the latch counter reaches zero the waiting thread is unblocked and continues + # with its work. A `CountDownLatch` can be used only once. Its value cannot be reset. + # + # @!macro count_down_latch_public_api + # @example Waiter and Decrementer + # latch = Concurrent::CountDownLatch.new(3) + # + # waiter = Thread.new do + # latch.wait() + # puts ("Waiter released") + # end + # + # decrementer = Thread.new do + # sleep(1) + # latch.count_down + # puts latch.count + # + # sleep(1) + # latch.count_down + # puts latch.count + # + # sleep(1) + # latch.count_down + # puts latch.count + # end + # + # [waiter, decrementer].each(&:join) + class CountDownLatch < CountDownLatchImplementation + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb new file mode 100644 index 0000000..42f5a94 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb @@ -0,0 +1,128 @@ +require 'concurrent/synchronization' +require 'concurrent/utility/native_integer' + +module Concurrent + + # A synchronization aid that allows a set of threads to all wait for each + # other to reach a common barrier point. + # @example + # barrier = Concurrent::CyclicBarrier.new(3) + # jobs = Array.new(3) { |i| -> { sleep i; p done: i } } + # process = -> (i) do + # # waiting to start at the same time + # barrier.wait + # # execute job + # jobs[i].call + # # wait for others to finish + # barrier.wait + # end + # threads = 2.times.map do |i| + # Thread.new(i, &process) + # end + # + # # use main as well + # process.call 2 + # + # # here we can be sure that all jobs are processed + class CyclicBarrier < Synchronization::LockableObject + + # @!visibility private + Generation = Struct.new(:status) + private_constant :Generation + + # Create a new `CyclicBarrier` that waits for `parties` threads + # + # @param [Fixnum] parties the number of parties + # @yield an optional block that will be executed that will be executed after + # the last thread arrives and before the others are released + # + # @raise [ArgumentError] if `parties` is not an integer or is less than zero + def initialize(parties, &block) + Utility::NativeInteger.ensure_integer_and_bounds parties + Utility::NativeInteger.ensure_positive_and_no_zero parties + + super(&nil) + synchronize { ns_initialize parties, &block } + end + + # @return [Fixnum] the number of threads needed to pass the barrier + def parties + synchronize { @parties } + end + + # @return [Fixnum] the number of threads currently waiting on the barrier + def number_waiting + synchronize { @number_waiting } + end + + # Blocks on the barrier until the number of waiting threads is equal to + # `parties` or until `timeout` is reached or `reset` is called + # If a block has been passed to the constructor, it will be executed once by + # the last arrived thread before releasing the others + # @param [Fixnum] timeout the number of seconds to wait for the counter or + # `nil` to block indefinitely + # @return [Boolean] `true` if the `count` reaches zero else false on + # `timeout` or on `reset` or if the barrier is broken + def wait(timeout = nil) + synchronize do + + return false unless @generation.status == :waiting + + @number_waiting += 1 + + if @number_waiting == @parties + @action.call if @action + ns_generation_done @generation, :fulfilled + true + else + generation = @generation + if ns_wait_until(timeout) { generation.status != :waiting } + generation.status == :fulfilled + else + ns_generation_done generation, :broken, false + false + end + end + end + end + + # resets the barrier to its initial state + # If there is at least one waiting thread, it will be woken up, the `wait` + # method will return false and the barrier will be broken + # If the barrier is broken, this method restores it to the original state + # + # @return [nil] + def reset + synchronize { ns_generation_done @generation, :reset } + end + + # A barrier can be broken when: + # - a thread called the `reset` method while at least one other thread was waiting + # - at least one thread timed out on `wait` method + # + # A broken barrier can be restored using `reset` it's safer to create a new one + # @return [Boolean] true if the barrier is broken otherwise false + def broken? + synchronize { @generation.status != :waiting } + end + + protected + + def ns_generation_done(generation, status, continue = true) + generation.status = status + ns_next_generation if continue + ns_broadcast + end + + def ns_next_generation + @generation = Generation.new(:waiting) + @number_waiting = 0 + end + + def ns_initialize(parties, &block) + @parties = parties + @action = block + ns_next_generation + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/event.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/event.rb new file mode 100644 index 0000000..31700ce --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/event.rb @@ -0,0 +1,109 @@ +require 'thread' +require 'concurrent/synchronization' + +module Concurrent + + # Old school kernel-style event reminiscent of Win32 programming in C++. + # + # When an `Event` is created it is in the `unset` state. Threads can choose to + # `#wait` on the event, blocking until released by another thread. When one + # thread wants to alert all blocking threads it calls the `#set` method which + # will then wake up all listeners. Once an `Event` has been set it remains set. + # New threads calling `#wait` will return immediately. An `Event` may be + # `#reset` at any time once it has been set. + # + # @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms682655.aspx + # @example + # event = Concurrent::Event.new + # + # t1 = Thread.new do + # puts "t1 is waiting" + # event.wait(1) + # puts "event occurred" + # end + # + # t2 = Thread.new do + # puts "t2 calling set" + # event.set + # end + # + # [t1, t2].each(&:join) + # + # # prints: + # # t1 is waiting + # # t2 calling set + # # event occurred + class Event < Synchronization::LockableObject + + # Creates a new `Event` in the unset state. Threads calling `#wait` on the + # `Event` will block. + def initialize + super + synchronize { ns_initialize } + end + + # Is the object in the set state? + # + # @return [Boolean] indicating whether or not the `Event` has been set + def set? + synchronize { @set } + end + + # Trigger the event, setting the state to `set` and releasing all threads + # waiting on the event. Has no effect if the `Event` has already been set. + # + # @return [Boolean] should always return `true` + def set + synchronize { ns_set } + end + + def try? + synchronize { @set ? false : ns_set } + end + + # Reset a previously set event back to the `unset` state. + # Has no effect if the `Event` has not yet been set. + # + # @return [Boolean] should always return `true` + def reset + synchronize do + if @set + @set = false + @iteration +=1 + end + true + end + end + + # Wait a given number of seconds for the `Event` to be set by another + # thread. Will wait forever when no `timeout` value is given. Returns + # immediately if the `Event` has already been set. + # + # @return [Boolean] true if the `Event` was set before timeout else false + def wait(timeout = nil) + synchronize do + unless @set + iteration = @iteration + ns_wait_until(timeout) { iteration < @iteration || @set } + else + true + end + end + end + + protected + + def ns_set + unless @set + @set = true + ns_broadcast + end + true + end + + def ns_initialize + @set = false + @iteration = 0 + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb new file mode 100644 index 0000000..cb5b35a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb @@ -0,0 +1,42 @@ +if Concurrent.on_jruby? + + module Concurrent + + # @!macro count_down_latch + # @!visibility private + # @!macro internal_implementation_note + class JavaCountDownLatch + + # @!macro count_down_latch_method_initialize + def initialize(count = 1) + Utility::NativeInteger.ensure_integer_and_bounds(count) + Utility::NativeInteger.ensure_positive(count) + @latch = java.util.concurrent.CountDownLatch.new(count) + end + + # @!macro count_down_latch_method_wait + def wait(timeout = nil) + result = nil + if timeout.nil? + Synchronization::JRuby.sleep_interruptibly { @latch.await } + result = true + else + Synchronization::JRuby.sleep_interruptibly do + result = @latch.await(1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS) + end + end + result + end + + # @!macro count_down_latch_method_count_down + def count_down + @latch.countDown + end + + # @!macro count_down_latch_method_count + def count + @latch.getCount + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb new file mode 100644 index 0000000..b41018f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb @@ -0,0 +1,37 @@ +require 'concurrent/atomic/abstract_thread_local_var' + +if Concurrent.on_jruby? + + module Concurrent + + # @!visibility private + # @!macro internal_implementation_note + class JavaThreadLocalVar < AbstractThreadLocalVar + + # @!macro thread_local_var_method_get + def value + value = @var.get + + if value.nil? + default + elsif value == NULL + nil + else + value + end + end + + # @!macro thread_local_var_method_set + def value=(value) + @var.set(value) + end + + protected + + # @!visibility private + def allocate_storage + @var = java.lang.ThreadLocal.new + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb new file mode 100644 index 0000000..a033de4 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb @@ -0,0 +1,62 @@ +require 'concurrent/synchronization' + +module Concurrent + + # @!macro atomic_boolean + # @!visibility private + # @!macro internal_implementation_note + class MutexAtomicBoolean < Synchronization::LockableObject + + # @!macro atomic_boolean_method_initialize + def initialize(initial = false) + super() + synchronize { ns_initialize(initial) } + end + + # @!macro atomic_boolean_method_value_get + def value + synchronize { @value } + end + + # @!macro atomic_boolean_method_value_set + def value=(value) + synchronize { @value = !!value } + end + + # @!macro atomic_boolean_method_true_question + def true? + synchronize { @value } + end + + # @!macro atomic_boolean_method_false_question + def false? + synchronize { !@value } + end + + # @!macro atomic_boolean_method_make_true + def make_true + synchronize { ns_make_value(true) } + end + + # @!macro atomic_boolean_method_make_false + def make_false + synchronize { ns_make_value(false) } + end + + protected + + # @!visibility private + def ns_initialize(initial) + @value = !!initial + end + + private + + # @!visibility private + def ns_make_value(value) + old = @value + @value = value + old != @value + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb new file mode 100644 index 0000000..77b91d2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb @@ -0,0 +1,75 @@ +require 'concurrent/synchronization' +require 'concurrent/utility/native_integer' + +module Concurrent + + # @!macro atomic_fixnum + # @!visibility private + # @!macro internal_implementation_note + class MutexAtomicFixnum < Synchronization::LockableObject + + # @!macro atomic_fixnum_method_initialize + def initialize(initial = 0) + super() + synchronize { ns_initialize(initial) } + end + + # @!macro atomic_fixnum_method_value_get + def value + synchronize { @value } + end + + # @!macro atomic_fixnum_method_value_set + def value=(value) + synchronize { ns_set(value) } + end + + # @!macro atomic_fixnum_method_increment + def increment(delta = 1) + synchronize { ns_set(@value + delta.to_i) } + end + + alias_method :up, :increment + + # @!macro atomic_fixnum_method_decrement + def decrement(delta = 1) + synchronize { ns_set(@value - delta.to_i) } + end + + alias_method :down, :decrement + + # @!macro atomic_fixnum_method_compare_and_set + def compare_and_set(expect, update) + synchronize do + if @value == expect.to_i + @value = update.to_i + true + else + false + end + end + end + + # @!macro atomic_fixnum_method_update + def update + synchronize do + @value = yield @value + end + end + + protected + + # @!visibility private + def ns_initialize(initial) + ns_set(initial) + end + + private + + # @!visibility private + def ns_set(value) + Utility::NativeInteger.ensure_integer_and_bounds value + @value = value + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb new file mode 100644 index 0000000..e99744c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb @@ -0,0 +1,44 @@ +require 'concurrent/synchronization' +require 'concurrent/utility/native_integer' + +module Concurrent + + # @!macro count_down_latch + # @!visibility private + # @!macro internal_implementation_note + class MutexCountDownLatch < Synchronization::LockableObject + + # @!macro count_down_latch_method_initialize + def initialize(count = 1) + Utility::NativeInteger.ensure_integer_and_bounds count + Utility::NativeInteger.ensure_positive count + + super() + synchronize { ns_initialize count } + end + + # @!macro count_down_latch_method_wait + def wait(timeout = nil) + synchronize { ns_wait_until(timeout) { @count == 0 } } + end + + # @!macro count_down_latch_method_count_down + def count_down + synchronize do + @count -= 1 if @count > 0 + ns_broadcast if @count == 0 + end + end + + # @!macro count_down_latch_method_count + def count + synchronize { @count } + end + + protected + + def ns_initialize(count) + @count = count + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb new file mode 100644 index 0000000..b6ac45f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb @@ -0,0 +1,131 @@ +require 'concurrent/synchronization' +require 'concurrent/utility/native_integer' + +module Concurrent + + # @!macro semaphore + # @!visibility private + # @!macro internal_implementation_note + class MutexSemaphore < Synchronization::LockableObject + + # @!macro semaphore_method_initialize + def initialize(count) + Utility::NativeInteger.ensure_integer_and_bounds count + + super() + synchronize { ns_initialize count } + end + + # @!macro semaphore_method_acquire + def acquire(permits = 1) + Utility::NativeInteger.ensure_integer_and_bounds permits + Utility::NativeInteger.ensure_positive permits + + synchronize do + try_acquire_timed(permits, nil) + end + + return unless block_given? + + begin + yield + ensure + release(permits) + end + end + + # @!macro semaphore_method_available_permits + def available_permits + synchronize { @free } + end + + # @!macro semaphore_method_drain_permits + # + # Acquires and returns all permits that are immediately available. + # + # @return [Integer] + def drain_permits + synchronize do + @free.tap { |_| @free = 0 } + end + end + + # @!macro semaphore_method_try_acquire + def try_acquire(permits = 1, timeout = nil) + Utility::NativeInteger.ensure_integer_and_bounds permits + Utility::NativeInteger.ensure_positive permits + + acquired = synchronize do + if timeout.nil? + try_acquire_now(permits) + else + try_acquire_timed(permits, timeout) + end + end + + return acquired unless block_given? + return unless acquired + + begin + yield + ensure + release(permits) + end + end + + # @!macro semaphore_method_release + def release(permits = 1) + Utility::NativeInteger.ensure_integer_and_bounds permits + Utility::NativeInteger.ensure_positive permits + + synchronize do + @free += permits + permits.times { ns_signal } + end + nil + end + + # Shrinks the number of available permits by the indicated reduction. + # + # @param [Fixnum] reduction Number of permits to remove. + # + # @raise [ArgumentError] if `reduction` is not an integer or is negative + # + # @raise [ArgumentError] if `@free` - `@reduction` is less than zero + # + # @return [nil] + # + # @!visibility private + def reduce_permits(reduction) + Utility::NativeInteger.ensure_integer_and_bounds reduction + Utility::NativeInteger.ensure_positive reduction + + synchronize { @free -= reduction } + nil + end + + protected + + # @!visibility private + def ns_initialize(count) + @free = count + end + + private + + # @!visibility private + def try_acquire_now(permits) + if @free >= permits + @free -= permits + true + else + false + end + end + + # @!visibility private + def try_acquire_timed(permits, timeout) + ns_wait_until(timeout) { try_acquire_now(permits) } + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb new file mode 100644 index 0000000..246f21a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb @@ -0,0 +1,254 @@ +require 'thread' +require 'concurrent/atomic/atomic_fixnum' +require 'concurrent/errors' +require 'concurrent/synchronization' + +module Concurrent + + # Ruby read-write lock implementation + # + # Allows any number of concurrent readers, but only one concurrent writer + # (And if the "write" lock is taken, any readers who come along will have to wait) + # + # If readers are already active when a writer comes along, the writer will wait for + # all the readers to finish before going ahead. + # Any additional readers that come when the writer is already waiting, will also + # wait (so writers are not starved). + # + # This implementation is based on `java.util.concurrent.ReentrantReadWriteLock`. + # + # @example + # lock = Concurrent::ReadWriteLock.new + # lock.with_read_lock { data.retrieve } + # lock.with_write_lock { data.modify! } + # + # @note Do **not** try to acquire the write lock while already holding a read lock + # **or** try to acquire the write lock while you already have it. + # This will lead to deadlock + # + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html java.util.concurrent.ReentrantReadWriteLock + class ReadWriteLock < Synchronization::Object + + # @!visibility private + WAITING_WRITER = 1 << 15 + + # @!visibility private + RUNNING_WRITER = 1 << 29 + + # @!visibility private + MAX_READERS = WAITING_WRITER - 1 + + # @!visibility private + MAX_WRITERS = RUNNING_WRITER - MAX_READERS - 1 + + safe_initialization! + + # Implementation notes: + # A goal is to make the uncontended path for both readers/writers lock-free + # Only if there is reader-writer or writer-writer contention, should locks be used + # Internal state is represented by a single integer ("counter"), and updated + # using atomic compare-and-swap operations + # When the counter is 0, the lock is free + # Each reader increments the counter by 1 when acquiring a read lock + # (and decrements by 1 when releasing the read lock) + # The counter is increased by (1 << 15) for each writer waiting to acquire the + # write lock, and by (1 << 29) if the write lock is taken + + # Create a new `ReadWriteLock` in the unlocked state. + def initialize + super() + @Counter = AtomicFixnum.new(0) # single integer which represents lock state + @ReadLock = Synchronization::Lock.new + @WriteLock = Synchronization::Lock.new + end + + # Execute a block operation within a read lock. + # + # @yield the task to be performed within the lock. + # + # @return [Object] the result of the block operation. + # + # @raise [ArgumentError] when no block is given. + # @raise [Concurrent::ResourceLimitError] if the maximum number of readers + # is exceeded. + def with_read_lock + raise ArgumentError.new('no block given') unless block_given? + acquire_read_lock + begin + yield + ensure + release_read_lock + end + end + + # Execute a block operation within a write lock. + # + # @yield the task to be performed within the lock. + # + # @return [Object] the result of the block operation. + # + # @raise [ArgumentError] when no block is given. + # @raise [Concurrent::ResourceLimitError] if the maximum number of readers + # is exceeded. + def with_write_lock + raise ArgumentError.new('no block given') unless block_given? + acquire_write_lock + begin + yield + ensure + release_write_lock + end + end + + # Acquire a read lock. If a write lock has been acquired will block until + # it is released. Will not block if other read locks have been acquired. + # + # @return [Boolean] true if the lock is successfully acquired + # + # @raise [Concurrent::ResourceLimitError] if the maximum number of readers + # is exceeded. + def acquire_read_lock + while true + c = @Counter.value + raise ResourceLimitError.new('Too many reader threads') if max_readers?(c) + + # If a writer is waiting when we first queue up, we need to wait + if waiting_writer?(c) + @ReadLock.wait_until { !waiting_writer? } + + # after a reader has waited once, they are allowed to "barge" ahead of waiting writers + # but if a writer is *running*, the reader still needs to wait (naturally) + while true + c = @Counter.value + if running_writer?(c) + @ReadLock.wait_until { !running_writer? } + else + return if @Counter.compare_and_set(c, c+1) + end + end + else + break if @Counter.compare_and_set(c, c+1) + end + end + true + end + + # Release a previously acquired read lock. + # + # @return [Boolean] true if the lock is successfully released + def release_read_lock + while true + c = @Counter.value + if @Counter.compare_and_set(c, c-1) + # If one or more writers were waiting, and we were the last reader, wake a writer up + if waiting_writer?(c) && running_readers(c) == 1 + @WriteLock.signal + end + break + end + end + true + end + + # Acquire a write lock. Will block and wait for all active readers and writers. + # + # @return [Boolean] true if the lock is successfully acquired + # + # @raise [Concurrent::ResourceLimitError] if the maximum number of writers + # is exceeded. + def acquire_write_lock + while true + c = @Counter.value + raise ResourceLimitError.new('Too many writer threads') if max_writers?(c) + + if c == 0 # no readers OR writers running + # if we successfully swap the RUNNING_WRITER bit on, then we can go ahead + break if @Counter.compare_and_set(0, RUNNING_WRITER) + elsif @Counter.compare_and_set(c, c+WAITING_WRITER) + while true + # Now we have successfully incremented, so no more readers will be able to increment + # (they will wait instead) + # However, readers OR writers could decrement right here, OR another writer could increment + @WriteLock.wait_until do + # So we have to do another check inside the synchronized section + # If a writer OR reader is running, then go to sleep + c = @Counter.value + !running_writer?(c) && !running_readers?(c) + end + + # We just came out of a wait + # If we successfully turn the RUNNING_WRITER bit on with an atomic swap, + # Then we are OK to stop waiting and go ahead + # Otherwise go back and wait again + c = @Counter.value + break if !running_writer?(c) && !running_readers?(c) && @Counter.compare_and_set(c, c+RUNNING_WRITER-WAITING_WRITER) + end + break + end + end + true + end + + # Release a previously acquired write lock. + # + # @return [Boolean] true if the lock is successfully released + def release_write_lock + return true unless running_writer? + c = @Counter.update { |counter| counter - RUNNING_WRITER } + @ReadLock.broadcast + @WriteLock.signal if waiting_writers(c) > 0 + true + end + + # Queries if the write lock is held by any thread. + # + # @return [Boolean] true if the write lock is held else false` + def write_locked? + @Counter.value >= RUNNING_WRITER + end + + # Queries whether any threads are waiting to acquire the read or write lock. + # + # @return [Boolean] true if any threads are waiting for a lock else false + def has_waiters? + waiting_writer?(@Counter.value) + end + + private + + # @!visibility private + def running_readers(c = @Counter.value) + c & MAX_READERS + end + + # @!visibility private + def running_readers?(c = @Counter.value) + (c & MAX_READERS) > 0 + end + + # @!visibility private + def running_writer?(c = @Counter.value) + c >= RUNNING_WRITER + end + + # @!visibility private + def waiting_writers(c = @Counter.value) + (c & MAX_WRITERS) / WAITING_WRITER + end + + # @!visibility private + def waiting_writer?(c = @Counter.value) + c >= WAITING_WRITER + end + + # @!visibility private + def max_readers?(c = @Counter.value) + (c & MAX_READERS) == MAX_READERS + end + + # @!visibility private + def max_writers?(c = @Counter.value) + (c & MAX_WRITERS) == MAX_WRITERS + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb new file mode 100644 index 0000000..92c96fd --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb @@ -0,0 +1,377 @@ +require 'thread' +require 'concurrent/atomic/atomic_reference' +require 'concurrent/errors' +require 'concurrent/synchronization' +require 'concurrent/atomic/thread_local_var' + +module Concurrent + + # Re-entrant read-write lock implementation + # + # Allows any number of concurrent readers, but only one concurrent writer + # (And while the "write" lock is taken, no read locks can be obtained either. + # Hence, the write lock can also be called an "exclusive" lock.) + # + # If another thread has taken a read lock, any thread which wants a write lock + # will block until all the readers release their locks. However, once a thread + # starts waiting to obtain a write lock, any additional readers that come along + # will also wait (so writers are not starved). + # + # A thread can acquire both a read and write lock at the same time. A thread can + # also acquire a read lock OR a write lock more than once. Only when the read (or + # write) lock is released as many times as it was acquired, will the thread + # actually let it go, allowing other threads which might have been waiting + # to proceed. Therefore the lock can be upgraded by first acquiring + # read lock and then write lock and that the lock can be downgraded by first + # having both read and write lock a releasing just the write lock. + # + # If both read and write locks are acquired by the same thread, it is not strictly + # necessary to release them in the same order they were acquired. In other words, + # the following code is legal: + # + # @example + # lock = Concurrent::ReentrantReadWriteLock.new + # lock.acquire_write_lock + # lock.acquire_read_lock + # lock.release_write_lock + # # At this point, the current thread is holding only a read lock, not a write + # # lock. So other threads can take read locks, but not a write lock. + # lock.release_read_lock + # # Now the current thread is not holding either a read or write lock, so + # # another thread could potentially acquire a write lock. + # + # This implementation was inspired by `java.util.concurrent.ReentrantReadWriteLock`. + # + # @example + # lock = Concurrent::ReentrantReadWriteLock.new + # lock.with_read_lock { data.retrieve } + # lock.with_write_lock { data.modify! } + # + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html java.util.concurrent.ReentrantReadWriteLock + class ReentrantReadWriteLock < Synchronization::Object + + # Implementation notes: + # + # A goal is to make the uncontended path for both readers/writers mutex-free + # Only if there is reader-writer or writer-writer contention, should mutexes be used + # Otherwise, a single CAS operation is all we need to acquire/release a lock + # + # Internal state is represented by a single integer ("counter"), and updated + # using atomic compare-and-swap operations + # When the counter is 0, the lock is free + # Each thread which has one OR MORE read locks increments the counter by 1 + # (and decrements by 1 when releasing the read lock) + # The counter is increased by (1 << 15) for each writer waiting to acquire the + # write lock, and by (1 << 29) if the write lock is taken + # + # Additionally, each thread uses a thread-local variable to count how many times + # it has acquired a read lock, AND how many times it has acquired a write lock. + # It uses a similar trick; an increment of 1 means a read lock was taken, and + # an increment of (1 << 15) means a write lock was taken + # This is what makes re-entrancy possible + # + # 2 rules are followed to ensure good liveness properties: + # 1) Once a writer has queued up and is waiting for a write lock, no other thread + # can take a lock without waiting + # 2) When a write lock is released, readers are given the "first chance" to wake + # up and acquire a read lock + # Following these rules means readers and writers tend to "take turns", so neither + # can starve the other, even under heavy contention + + # @!visibility private + READER_BITS = 15 + # @!visibility private + WRITER_BITS = 14 + + # Used with @Counter: + # @!visibility private + WAITING_WRITER = 1 << READER_BITS + # @!visibility private + RUNNING_WRITER = 1 << (READER_BITS + WRITER_BITS) + # @!visibility private + MAX_READERS = WAITING_WRITER - 1 + # @!visibility private + MAX_WRITERS = RUNNING_WRITER - MAX_READERS - 1 + + # Used with @HeldCount: + # @!visibility private + WRITE_LOCK_HELD = 1 << READER_BITS + # @!visibility private + READ_LOCK_MASK = WRITE_LOCK_HELD - 1 + # @!visibility private + WRITE_LOCK_MASK = MAX_WRITERS + + safe_initialization! + + # Create a new `ReentrantReadWriteLock` in the unlocked state. + def initialize + super() + @Counter = AtomicFixnum.new(0) # single integer which represents lock state + @ReadQueue = Synchronization::Lock.new # used to queue waiting readers + @WriteQueue = Synchronization::Lock.new # used to queue waiting writers + @HeldCount = ThreadLocalVar.new(0) # indicates # of R & W locks held by this thread + end + + # Execute a block operation within a read lock. + # + # @yield the task to be performed within the lock. + # + # @return [Object] the result of the block operation. + # + # @raise [ArgumentError] when no block is given. + # @raise [Concurrent::ResourceLimitError] if the maximum number of readers + # is exceeded. + def with_read_lock + raise ArgumentError.new('no block given') unless block_given? + acquire_read_lock + begin + yield + ensure + release_read_lock + end + end + + # Execute a block operation within a write lock. + # + # @yield the task to be performed within the lock. + # + # @return [Object] the result of the block operation. + # + # @raise [ArgumentError] when no block is given. + # @raise [Concurrent::ResourceLimitError] if the maximum number of readers + # is exceeded. + def with_write_lock + raise ArgumentError.new('no block given') unless block_given? + acquire_write_lock + begin + yield + ensure + release_write_lock + end + end + + # Acquire a read lock. If a write lock is held by another thread, will block + # until it is released. + # + # @return [Boolean] true if the lock is successfully acquired + # + # @raise [Concurrent::ResourceLimitError] if the maximum number of readers + # is exceeded. + def acquire_read_lock + if (held = @HeldCount.value) > 0 + # If we already have a lock, there's no need to wait + if held & READ_LOCK_MASK == 0 + # But we do need to update the counter, if we were holding a write + # lock but not a read lock + @Counter.update { |c| c + 1 } + end + @HeldCount.value = held + 1 + return true + end + + while true + c = @Counter.value + raise ResourceLimitError.new('Too many reader threads') if max_readers?(c) + + # If a writer is waiting OR running when we first queue up, we need to wait + if waiting_or_running_writer?(c) + # Before going to sleep, check again with the ReadQueue mutex held + @ReadQueue.synchronize do + @ReadQueue.ns_wait if waiting_or_running_writer? + end + # Note: the above 'synchronize' block could have used #wait_until, + # but that waits repeatedly in a loop, checking the wait condition + # each time it wakes up (to protect against spurious wakeups) + # But we are already in a loop, which is only broken when we successfully + # acquire the lock! So we don't care about spurious wakeups, and would + # rather not pay the extra overhead of using #wait_until + + # After a reader has waited once, they are allowed to "barge" ahead of waiting writers + # But if a writer is *running*, the reader still needs to wait (naturally) + while true + c = @Counter.value + if running_writer?(c) + @ReadQueue.synchronize do + @ReadQueue.ns_wait if running_writer? + end + elsif @Counter.compare_and_set(c, c+1) + @HeldCount.value = held + 1 + return true + end + end + elsif @Counter.compare_and_set(c, c+1) + @HeldCount.value = held + 1 + return true + end + end + end + + # Try to acquire a read lock and return true if we succeed. If it cannot be + # acquired immediately, return false. + # + # @return [Boolean] true if the lock is successfully acquired + def try_read_lock + if (held = @HeldCount.value) > 0 + if held & READ_LOCK_MASK == 0 + # If we hold a write lock, but not a read lock... + @Counter.update { |c| c + 1 } + end + @HeldCount.value = held + 1 + return true + else + c = @Counter.value + if !waiting_or_running_writer?(c) && @Counter.compare_and_set(c, c+1) + @HeldCount.value = held + 1 + return true + end + end + false + end + + # Release a previously acquired read lock. + # + # @return [Boolean] true if the lock is successfully released + def release_read_lock + held = @HeldCount.value = @HeldCount.value - 1 + rlocks_held = held & READ_LOCK_MASK + if rlocks_held == 0 + c = @Counter.update { |counter| counter - 1 } + # If one or more writers were waiting, and we were the last reader, wake a writer up + if waiting_or_running_writer?(c) && running_readers(c) == 0 + @WriteQueue.signal + end + elsif rlocks_held == READ_LOCK_MASK + raise IllegalOperationError, "Cannot release a read lock which is not held" + end + true + end + + # Acquire a write lock. Will block and wait for all active readers and writers. + # + # @return [Boolean] true if the lock is successfully acquired + # + # @raise [Concurrent::ResourceLimitError] if the maximum number of writers + # is exceeded. + def acquire_write_lock + if (held = @HeldCount.value) >= WRITE_LOCK_HELD + # if we already have a write (exclusive) lock, there's no need to wait + @HeldCount.value = held + WRITE_LOCK_HELD + return true + end + + while true + c = @Counter.value + raise ResourceLimitError.new('Too many writer threads') if max_writers?(c) + + # To go ahead and take the lock without waiting, there must be no writer + # running right now, AND no writers who came before us still waiting to + # acquire the lock + # Additionally, if any read locks have been taken, we must hold all of them + if held > 0 && @Counter.compare_and_set(1, c+RUNNING_WRITER) + # If we are the only one reader and successfully swap the RUNNING_WRITER bit on, then we can go ahead + @HeldCount.value = held + WRITE_LOCK_HELD + return true + elsif @Counter.compare_and_set(c, c+WAITING_WRITER) + while true + # Now we have successfully incremented, so no more readers will be able to increment + # (they will wait instead) + # However, readers OR writers could decrement right here + @WriteQueue.synchronize do + # So we have to do another check inside the synchronized section + # If a writer OR another reader is running, then go to sleep + c = @Counter.value + @WriteQueue.ns_wait if running_writer?(c) || running_readers(c) != held + end + # Note: if you are thinking of replacing the above 'synchronize' block + # with #wait_until, read the comment in #acquire_read_lock first! + + # We just came out of a wait + # If we successfully turn the RUNNING_WRITER bit on with an atomic swap, + # then we are OK to stop waiting and go ahead + # Otherwise go back and wait again + c = @Counter.value + if !running_writer?(c) && + running_readers(c) == held && + @Counter.compare_and_set(c, c+RUNNING_WRITER-WAITING_WRITER) + @HeldCount.value = held + WRITE_LOCK_HELD + return true + end + end + end + end + end + + # Try to acquire a write lock and return true if we succeed. If it cannot be + # acquired immediately, return false. + # + # @return [Boolean] true if the lock is successfully acquired + def try_write_lock + if (held = @HeldCount.value) >= WRITE_LOCK_HELD + @HeldCount.value = held + WRITE_LOCK_HELD + return true + else + c = @Counter.value + if !waiting_or_running_writer?(c) && + running_readers(c) == held && + @Counter.compare_and_set(c, c+RUNNING_WRITER) + @HeldCount.value = held + WRITE_LOCK_HELD + return true + end + end + false + end + + # Release a previously acquired write lock. + # + # @return [Boolean] true if the lock is successfully released + def release_write_lock + held = @HeldCount.value = @HeldCount.value - WRITE_LOCK_HELD + wlocks_held = held & WRITE_LOCK_MASK + if wlocks_held == 0 + c = @Counter.update { |counter| counter - RUNNING_WRITER } + @ReadQueue.broadcast + @WriteQueue.signal if waiting_writers(c) > 0 + elsif wlocks_held == WRITE_LOCK_MASK + raise IllegalOperationError, "Cannot release a write lock which is not held" + end + true + end + + private + + # @!visibility private + def running_readers(c = @Counter.value) + c & MAX_READERS + end + + # @!visibility private + def running_readers?(c = @Counter.value) + (c & MAX_READERS) > 0 + end + + # @!visibility private + def running_writer?(c = @Counter.value) + c >= RUNNING_WRITER + end + + # @!visibility private + def waiting_writers(c = @Counter.value) + (c & MAX_WRITERS) >> READER_BITS + end + + # @!visibility private + def waiting_or_running_writer?(c = @Counter.value) + c >= WAITING_WRITER + end + + # @!visibility private + def max_readers?(c = @Counter.value) + (c & MAX_READERS) == MAX_READERS + end + + # @!visibility private + def max_writers?(c = @Counter.value) + (c & MAX_WRITERS) == MAX_WRITERS + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb new file mode 100644 index 0000000..9a51eb2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb @@ -0,0 +1,181 @@ +require 'thread' +require 'concurrent/atomic/abstract_thread_local_var' + +module Concurrent + + # @!visibility private + # @!macro internal_implementation_note + class RubyThreadLocalVar < AbstractThreadLocalVar + + # Each thread has a (lazily initialized) array of thread-local variable values + # Each time a new thread-local var is created, we allocate an "index" for it + # For example, if the allocated index is 1, that means slot #1 in EVERY + # thread's thread-local array will be used for the value of that TLV + # + # The good thing about using a per-THREAD structure to hold values, rather + # than a per-TLV structure, is that no synchronization is needed when + # reading and writing those values (since the structure is only ever + # accessed by a single thread) + # + # Of course, when a TLV is GC'd, 1) we need to recover its index for use + # by other new TLVs (otherwise the thread-local arrays could get bigger + # and bigger with time), and 2) we need to null out all the references + # held in the now-unused slots (both to avoid blocking GC of those objects, + # and also to prevent "stale" values from being passed on to a new TLV + # when the index is reused) + # Because we need to null out freed slots, we need to keep references to + # ALL the thread-local arrays -- ARRAYS is for that + # But when a Thread is GC'd, we need to drop the reference to its thread-local + # array, so we don't leak memory + + FREE = [] + LOCK = Mutex.new + THREAD_LOCAL_ARRAYS = {} # used as a hash set + + # synchronize when not on MRI + # on MRI using lock in finalizer leads to "can't be called from trap context" error + # so the code is carefully written to be tread-safe on MRI relying on GIL + + if Concurrent.on_cruby? + # @!visibility private + def self.semi_sync(&block) + block.call + end + else + # @!visibility private + def self.semi_sync(&block) + LOCK.synchronize(&block) + end + end + + private_constant :FREE, :LOCK, :THREAD_LOCAL_ARRAYS + + # @!macro thread_local_var_method_get + def value + if (array = get_threadlocal_array) + value = array[@index] + if value.nil? + default + elsif value.equal?(NULL) + nil + else + value + end + else + default + end + end + + # @!macro thread_local_var_method_set + def value=(value) + me = Thread.current + # We could keep the thread-local arrays in a hash, keyed by Thread + # But why? That would require locking + # Using Ruby's built-in thread-local storage is faster + unless (array = get_threadlocal_array(me)) + array = set_threadlocal_array([], me) + self.class.semi_sync { THREAD_LOCAL_ARRAYS[array.object_id] = array } + ObjectSpace.define_finalizer(me, self.class.thread_finalizer(array.object_id)) + end + array[@index] = (value.nil? ? NULL : value) + value + end + + protected + + # @!visibility private + def allocate_storage + @index = FREE.pop || next_index + + ObjectSpace.define_finalizer(self, self.class.thread_local_finalizer(@index)) + end + + # @!visibility private + def self.thread_local_finalizer(index) + proc do + semi_sync do + # The cost of GC'ing a TLV is linear in the number of threads using TLVs + # But that is natural! More threads means more storage is used per TLV + # So naturally more CPU time is required to free more storage + # + # DO NOT use each_value which might conflict with new pair assignment + # into the hash in #value= method + THREAD_LOCAL_ARRAYS.values.each { |array| array[index] = nil } + # free index has to be published after the arrays are cleared + FREE.push(index) + end + end + end + + # @!visibility private + def self.thread_finalizer(id) + proc do + semi_sync do + # The thread which used this thread-local array is now gone + # So don't hold onto a reference to the array (thus blocking GC) + THREAD_LOCAL_ARRAYS.delete(id) + end + end + end + + private + + # noinspection RubyClassVariableUsageInspection + @@next = 0 + # noinspection RubyClassVariableUsageInspection + def next_index + LOCK.synchronize do + result = @@next + @@next += 1 + result + end + end + + if Thread.instance_methods.include?(:thread_variable_get) + + def get_threadlocal_array(thread = Thread.current) + thread.thread_variable_get(:__threadlocal_array__) + end + + def set_threadlocal_array(array, thread = Thread.current) + thread.thread_variable_set(:__threadlocal_array__, array) + end + + else + + def get_threadlocal_array(thread = Thread.current) + thread[:__threadlocal_array__] + end + + def set_threadlocal_array(array, thread = Thread.current) + thread[:__threadlocal_array__] = array + end + end + + # This exists only for use in testing + # @!visibility private + def value_for(thread) + if (array = get_threadlocal_array(thread)) + value = array[@index] + if value.nil? + get_default + elsif value.equal?(NULL) + nil + else + value + end + else + get_default + end + end + + # @!visibility private + def get_default + if @default_block + raise "Cannot use default_for with default block" + else + @default + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/semaphore.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/semaphore.rb new file mode 100644 index 0000000..cc6a892 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/semaphore.rb @@ -0,0 +1,166 @@ +require 'concurrent/atomic/mutex_semaphore' +require 'concurrent/synchronization' + +module Concurrent + + ################################################################### + + # @!macro semaphore_method_initialize + # + # Create a new `Semaphore` with the initial `count`. + # + # @param [Fixnum] count the initial count + # + # @raise [ArgumentError] if `count` is not an integer or is less than zero + + # @!macro semaphore_method_acquire + # + # Acquires the given number of permits from this semaphore, + # blocking until all are available. If a block is given, + # yields to it and releases the permits afterwards. + # + # @param [Fixnum] permits Number of permits to acquire + # + # @raise [ArgumentError] if `permits` is not an integer or is less than + # one + # + # @return [nil, BasicObject] Without a block, `nil` is returned. If a block + # is given, its return value is returned. + + # @!macro semaphore_method_available_permits + # + # Returns the current number of permits available in this semaphore. + # + # @return [Integer] + + # @!macro semaphore_method_drain_permits + # + # Acquires and returns all permits that are immediately available. + # + # @return [Integer] + + # @!macro semaphore_method_try_acquire + # + # Acquires the given number of permits from this semaphore, + # only if all are available at the time of invocation or within + # `timeout` interval. If a block is given, yields to it if the permits + # were successfully acquired, and releases them afterward, returning the + # block's return value. + # + # @param [Fixnum] permits the number of permits to acquire + # + # @param [Fixnum] timeout the number of seconds to wait for the counter + # or `nil` to return immediately + # + # @raise [ArgumentError] if `permits` is not an integer or is less than + # one + # + # @return [true, false, nil, BasicObject] `false` if no permits are + # available, `true` when acquired a permit. If a block is given, the + # block's return value is returned if the permits were acquired; if not, + # `nil` is returned. + + # @!macro semaphore_method_release + # + # Releases the given number of permits, returning them to the semaphore. + # + # @param [Fixnum] permits Number of permits to return to the semaphore. + # + # @raise [ArgumentError] if `permits` is not a number or is less than one + # + # @return [nil] + + ################################################################### + + # @!macro semaphore_public_api + # + # @!method initialize(count) + # @!macro semaphore_method_initialize + # + # @!method acquire(permits = 1) + # @!macro semaphore_method_acquire + # + # @!method available_permits + # @!macro semaphore_method_available_permits + # + # @!method drain_permits + # @!macro semaphore_method_drain_permits + # + # @!method try_acquire(permits = 1, timeout = nil) + # @!macro semaphore_method_try_acquire + # + # @!method release(permits = 1) + # @!macro semaphore_method_release + + ################################################################### + + # @!visibility private + # @!macro internal_implementation_note + SemaphoreImplementation = case + when defined?(JavaSemaphore) + JavaSemaphore + else + MutexSemaphore + end + private_constant :SemaphoreImplementation + + # @!macro semaphore + # + # A counting semaphore. Conceptually, a semaphore maintains a set of + # permits. Each {#acquire} blocks if necessary until a permit is + # available, and then takes it. Each {#release} adds a permit, potentially + # releasing a blocking acquirer. + # However, no actual permit objects are used; the Semaphore just keeps a + # count of the number available and acts accordingly. + # Alternatively, permits may be acquired within a block, and automatically + # released after the block finishes executing. + # + # @!macro semaphore_public_api + # @example + # semaphore = Concurrent::Semaphore.new(2) + # + # t1 = Thread.new do + # semaphore.acquire + # puts "Thread 1 acquired semaphore" + # end + # + # t2 = Thread.new do + # semaphore.acquire + # puts "Thread 2 acquired semaphore" + # end + # + # t3 = Thread.new do + # semaphore.acquire + # puts "Thread 3 acquired semaphore" + # end + # + # t4 = Thread.new do + # sleep(2) + # puts "Thread 4 releasing semaphore" + # semaphore.release + # end + # + # [t1, t2, t3, t4].each(&:join) + # + # # prints: + # # Thread 3 acquired semaphore + # # Thread 2 acquired semaphore + # # Thread 4 releasing semaphore + # # Thread 1 acquired semaphore + # + # @example + # semaphore = Concurrent::Semaphore.new(1) + # + # puts semaphore.available_permits + # semaphore.acquire do + # puts semaphore.available_permits + # end + # puts semaphore.available_permits + # + # # prints: + # # 1 + # # 0 + # # 1 + class Semaphore < SemaphoreImplementation + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb new file mode 100644 index 0000000..100cc8d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb @@ -0,0 +1,104 @@ +require 'concurrent/utility/engine' +require 'concurrent/atomic/ruby_thread_local_var' +require 'concurrent/atomic/java_thread_local_var' + +module Concurrent + + ################################################################### + + # @!macro thread_local_var_method_initialize + # + # Creates a thread local variable. + # + # @param [Object] default the default value when otherwise unset + # @param [Proc] default_block Optional block that gets called to obtain the + # default value for each thread + + # @!macro thread_local_var_method_get + # + # Returns the value in the current thread's copy of this thread-local variable. + # + # @return [Object] the current value + + # @!macro thread_local_var_method_set + # + # Sets the current thread's copy of this thread-local variable to the specified value. + # + # @param [Object] value the value to set + # @return [Object] the new value + + # @!macro thread_local_var_method_bind + # + # Bind the given value to thread local storage during + # execution of the given block. + # + # @param [Object] value the value to bind + # @yield the operation to be performed with the bound variable + # @return [Object] the value + + + ################################################################### + + # @!macro thread_local_var_public_api + # + # @!method initialize(default = nil, &default_block) + # @!macro thread_local_var_method_initialize + # + # @!method value + # @!macro thread_local_var_method_get + # + # @!method value=(value) + # @!macro thread_local_var_method_set + # + # @!method bind(value, &block) + # @!macro thread_local_var_method_bind + + ################################################################### + + # @!visibility private + # @!macro internal_implementation_note + ThreadLocalVarImplementation = case + when Concurrent.on_jruby? + JavaThreadLocalVar + else + RubyThreadLocalVar + end + private_constant :ThreadLocalVarImplementation + + # @!macro thread_local_var + # + # A `ThreadLocalVar` is a variable where the value is different for each thread. + # Each variable may have a default value, but when you modify the variable only + # the current thread will ever see that change. + # + # @!macro thread_safe_variable_comparison + # + # @example + # v = ThreadLocalVar.new(14) + # v.value #=> 14 + # v.value = 2 + # v.value #=> 2 + # + # @example + # v = ThreadLocalVar.new(14) + # + # t1 = Thread.new do + # v.value #=> 14 + # v.value = 1 + # v.value #=> 1 + # end + # + # t2 = Thread.new do + # v.value #=> 14 + # v.value = 2 + # v.value #=> 2 + # end + # + # v.value #=> 14 + # + # @see https://docs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html Java ThreadLocal + # + # @!macro thread_local_var_public_api + class ThreadLocalVar < ThreadLocalVarImplementation + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb new file mode 100644 index 0000000..d092aed --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb @@ -0,0 +1,56 @@ +module Concurrent + + # @!visibility private + # @!macro internal_implementation_note + class MutexAtomicReference < Synchronization::LockableObject + include AtomicDirectUpdate + include AtomicNumericCompareAndSetWrapper + alias_method :compare_and_swap, :compare_and_set + + # @!macro atomic_reference_method_initialize + def initialize(value = nil) + super() + synchronize { ns_initialize(value) } + end + + # @!macro atomic_reference_method_get + def get + synchronize { @value } + end + alias_method :value, :get + + # @!macro atomic_reference_method_set + def set(new_value) + synchronize { @value = new_value } + end + alias_method :value=, :set + + # @!macro atomic_reference_method_get_and_set + def get_and_set(new_value) + synchronize do + old_value = @value + @value = new_value + old_value + end + end + alias_method :swap, :get_and_set + + # @!macro atomic_reference_method_compare_and_set + def _compare_and_set(old_value, new_value) + synchronize do + if @value.equal? old_value + @value = new_value + true + else + false + end + end + end + + protected + + def ns_initialize(value) + @value = value + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb new file mode 100644 index 0000000..709a382 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb @@ -0,0 +1,28 @@ +module Concurrent + + # Special "compare and set" handling of numeric values. + # + # @!visibility private + # @!macro internal_implementation_note + module AtomicNumericCompareAndSetWrapper + + # @!macro atomic_reference_method_compare_and_set + def compare_and_set(old_value, new_value) + if old_value.kind_of? Numeric + while true + old = get + + return false unless old.kind_of? Numeric + + return false unless old == old_value + + result = _compare_and_set(old, new_value) + return result if result + end + else + _compare_and_set(old_value, new_value) + end + end + + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomics.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomics.rb new file mode 100644 index 0000000..16cbe66 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/atomics.rb @@ -0,0 +1,10 @@ +require 'concurrent/atomic/atomic_reference' +require 'concurrent/atomic/atomic_boolean' +require 'concurrent/atomic/atomic_fixnum' +require 'concurrent/atomic/cyclic_barrier' +require 'concurrent/atomic/count_down_latch' +require 'concurrent/atomic/event' +require 'concurrent/atomic/read_write_lock' +require 'concurrent/atomic/reentrant_read_write_lock' +require 'concurrent/atomic/semaphore' +require 'concurrent/atomic/thread_local_var' diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb new file mode 100644 index 0000000..50d52a6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb @@ -0,0 +1,107 @@ +require 'concurrent/synchronization' + +module Concurrent + module Collection + + # A thread safe observer set implemented using copy-on-read approach: + # observers are added and removed from a thread safe collection; every time + # a notification is required the internal data structure is copied to + # prevent concurrency issues + # + # @api private + class CopyOnNotifyObserverSet < Synchronization::LockableObject + + def initialize + super() + synchronize { ns_initialize } + end + + # @!macro observable_add_observer + def add_observer(observer = nil, func = :update, &block) + if observer.nil? && block.nil? + raise ArgumentError, 'should pass observer as a first argument or block' + elsif observer && block + raise ArgumentError.new('cannot provide both an observer and a block') + end + + if block + observer = block + func = :call + end + + synchronize do + @observers[observer] = func + observer + end + end + + # @!macro observable_delete_observer + def delete_observer(observer) + synchronize do + @observers.delete(observer) + observer + end + end + + # @!macro observable_delete_observers + def delete_observers + synchronize do + @observers.clear + self + end + end + + # @!macro observable_count_observers + def count_observers + synchronize { @observers.count } + end + + # Notifies all registered observers with optional args + # @param [Object] args arguments to be passed to each observer + # @return [CopyOnWriteObserverSet] self + def notify_observers(*args, &block) + observers = duplicate_observers + notify_to(observers, *args, &block) + self + end + + # Notifies all registered observers with optional args and deletes them. + # + # @param [Object] args arguments to be passed to each observer + # @return [CopyOnWriteObserverSet] self + def notify_and_delete_observers(*args, &block) + observers = duplicate_and_clear_observers + notify_to(observers, *args, &block) + self + end + + protected + + def ns_initialize + @observers = {} + end + + private + + def duplicate_and_clear_observers + synchronize do + observers = @observers.dup + @observers.clear + observers + end + end + + def duplicate_observers + synchronize { @observers.dup } + end + + def notify_to(observers, *args) + raise ArgumentError.new('cannot give arguments and a block') if block_given? && !args.empty? + observers.each do |observer, function| + args = yield if block_given? + observer.send(function, *args) + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb new file mode 100644 index 0000000..3f3f7cc --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb @@ -0,0 +1,111 @@ +require 'concurrent/synchronization' + +module Concurrent + module Collection + + # A thread safe observer set implemented using copy-on-write approach: + # every time an observer is added or removed the whole internal data structure is + # duplicated and replaced with a new one. + # + # @api private + class CopyOnWriteObserverSet < Synchronization::LockableObject + + def initialize + super() + synchronize { ns_initialize } + end + + # @!macro observable_add_observer + def add_observer(observer = nil, func = :update, &block) + if observer.nil? && block.nil? + raise ArgumentError, 'should pass observer as a first argument or block' + elsif observer && block + raise ArgumentError.new('cannot provide both an observer and a block') + end + + if block + observer = block + func = :call + end + + synchronize do + new_observers = @observers.dup + new_observers[observer] = func + @observers = new_observers + observer + end + end + + # @!macro observable_delete_observer + def delete_observer(observer) + synchronize do + new_observers = @observers.dup + new_observers.delete(observer) + @observers = new_observers + observer + end + end + + # @!macro observable_delete_observers + def delete_observers + self.observers = {} + self + end + + # @!macro observable_count_observers + def count_observers + observers.count + end + + # Notifies all registered observers with optional args + # @param [Object] args arguments to be passed to each observer + # @return [CopyOnWriteObserverSet] self + def notify_observers(*args, &block) + notify_to(observers, *args, &block) + self + end + + # Notifies all registered observers with optional args and deletes them. + # + # @param [Object] args arguments to be passed to each observer + # @return [CopyOnWriteObserverSet] self + def notify_and_delete_observers(*args, &block) + old = clear_observers_and_return_old + notify_to(old, *args, &block) + self + end + + protected + + def ns_initialize + @observers = {} + end + + private + + def notify_to(observers, *args) + raise ArgumentError.new('cannot give arguments and a block') if block_given? && !args.empty? + observers.each do |observer, function| + args = yield if block_given? + observer.send(function, *args) + end + end + + def observers + synchronize { @observers } + end + + def observers=(new_set) + synchronize { @observers = new_set } + end + + def clear_observers_and_return_old + synchronize do + old_observers = @observers + @observers = {} + old_observers + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/java_non_concurrent_priority_queue.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/java_non_concurrent_priority_queue.rb new file mode 100644 index 0000000..2be9e43 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/java_non_concurrent_priority_queue.rb @@ -0,0 +1,84 @@ +if Concurrent.on_jruby? + + module Concurrent + module Collection + + + # @!macro priority_queue + # + # @!visibility private + # @!macro internal_implementation_note + class JavaNonConcurrentPriorityQueue + + # @!macro priority_queue_method_initialize + def initialize(opts = {}) + order = opts.fetch(:order, :max) + if [:min, :low].include?(order) + @queue = java.util.PriorityQueue.new(11) # 11 is the default initial capacity + else + @queue = java.util.PriorityQueue.new(11, java.util.Collections.reverseOrder()) + end + end + + # @!macro priority_queue_method_clear + def clear + @queue.clear + true + end + + # @!macro priority_queue_method_delete + def delete(item) + found = false + while @queue.remove(item) do + found = true + end + found + end + + # @!macro priority_queue_method_empty + def empty? + @queue.size == 0 + end + + # @!macro priority_queue_method_include + def include?(item) + @queue.contains(item) + end + alias_method :has_priority?, :include? + + # @!macro priority_queue_method_length + def length + @queue.size + end + alias_method :size, :length + + # @!macro priority_queue_method_peek + def peek + @queue.peek + end + + # @!macro priority_queue_method_pop + def pop + @queue.poll + end + alias_method :deq, :pop + alias_method :shift, :pop + + # @!macro priority_queue_method_push + def push(item) + raise ArgumentError.new('cannot enqueue nil') if item.nil? + @queue.add(item) + end + alias_method :<<, :push + alias_method :enq, :push + + # @!macro priority_queue_method_from_list + def self.from_list(list, opts = {}) + queue = new(opts) + list.each{|item| queue << item } + queue + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb new file mode 100644 index 0000000..d003d3c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb @@ -0,0 +1,158 @@ +module Concurrent + + # @!macro warn.edge + class LockFreeStack < Synchronization::Object + + safe_initialization! + + class Node + # TODO (pitr-ch 20-Dec-2016): Could be unified with Stack class? + + # @return [Node] + attr_reader :next_node + + # @return [Object] + attr_reader :value + + # @!visibility private + # allow to nil-ify to free GC when the entry is no longer relevant, not synchronised + attr_writer :value + + def initialize(value, next_node) + @value = value + @next_node = next_node + end + + singleton_class.send :alias_method, :[], :new + end + + # The singleton for empty node + EMPTY = Node[nil, nil] + def EMPTY.next_node + self + end + + attr_atomic(:head) + private :head, :head=, :swap_head, :compare_and_set_head, :update_head + + # @!visibility private + def self.of1(value) + new Node[value, EMPTY] + end + + # @!visibility private + def self.of2(value1, value2) + new Node[value1, Node[value2, EMPTY]] + end + + # @param [Node] head + def initialize(head = EMPTY) + super() + self.head = head + end + + # @param [Node] head + # @return [true, false] + def empty?(head = head()) + head.equal? EMPTY + end + + # @param [Node] head + # @param [Object] value + # @return [true, false] + def compare_and_push(head, value) + compare_and_set_head head, Node[value, head] + end + + # @param [Object] value + # @return [self] + def push(value) + while true + current_head = head + return self if compare_and_set_head current_head, Node[value, current_head] + end + end + + # @return [Node] + def peek + head + end + + # @param [Node] head + # @return [true, false] + def compare_and_pop(head) + compare_and_set_head head, head.next_node + end + + # @return [Object] + def pop + while true + current_head = head + return current_head.value if compare_and_set_head current_head, current_head.next_node + end + end + + # @param [Node] head + # @return [true, false] + def compare_and_clear(head) + compare_and_set_head head, EMPTY + end + + include Enumerable + + # @param [Node] head + # @return [self] + def each(head = nil) + return to_enum(:each, head) unless block_given? + it = head || peek + until it.equal?(EMPTY) + yield it.value + it = it.next_node + end + self + end + + # @return [true, false] + def clear + while true + current_head = head + return false if current_head == EMPTY + return true if compare_and_set_head current_head, EMPTY + end + end + + # @param [Node] head + # @return [true, false] + def clear_if(head) + compare_and_set_head head, EMPTY + end + + # @param [Node] head + # @param [Node] new_head + # @return [true, false] + def replace_if(head, new_head) + compare_and_set_head head, new_head + end + + # @return [self] + # @yield over the cleared stack + # @yieldparam [Object] value + def clear_each(&block) + while true + current_head = head + return self if current_head == EMPTY + if compare_and_set_head current_head, EMPTY + each current_head, &block + return self + end + end + end + + # @return [String] Short string representation. + def to_s + format '%s %s>', super[0..-2], to_a.to_s + end + + alias_method :inspect, :to_s + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/map/atomic_reference_map_backend.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/map/atomic_reference_map_backend.rb new file mode 100644 index 0000000..dc51893 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/map/atomic_reference_map_backend.rb @@ -0,0 +1,927 @@ +require 'concurrent/constants' +require 'concurrent/thread_safe/util' +require 'concurrent/thread_safe/util/adder' +require 'concurrent/thread_safe/util/cheap_lockable' +require 'concurrent/thread_safe/util/power_of_two_tuple' +require 'concurrent/thread_safe/util/volatile' +require 'concurrent/thread_safe/util/xor_shift_random' + +module Concurrent + + # @!visibility private + module Collection + + # A Ruby port of the Doug Lea's jsr166e.ConcurrentHashMapV8 class version 1.59 + # available in public domain. + # + # Original source code available here: + # http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ConcurrentHashMapV8.java?revision=1.59 + # + # The Ruby port skips out the +TreeBin+ (red-black trees for use in bins whose + # size exceeds a threshold). + # + # A hash table supporting full concurrency of retrievals and high expected + # concurrency for updates. However, even though all operations are + # thread-safe, retrieval operations do _not_ entail locking, and there is + # _not_ any support for locking the entire table in a way that prevents all + # access. + # + # Retrieval operations generally do not block, so may overlap with update + # operations. Retrievals reflect the results of the most recently _completed_ + # update operations holding upon their onset. (More formally, an update + # operation for a given key bears a _happens-before_ relation with any (non + # +nil+) retrieval for that key reporting the updated value.) For aggregate + # operations such as +clear()+, concurrent retrievals may reflect insertion or + # removal of only some entries. Similarly, the +each_pair+ iterator yields + # elements reflecting the state of the hash table at some point at or since + # the start of the +each_pair+. Bear in mind that the results of aggregate + # status methods including +size()+ and +empty?+} are typically useful only + # when a map is not undergoing concurrent updates in other threads. Otherwise + # the results of these methods reflect transient states that may be adequate + # for monitoring or estimation purposes, but not for program control. + # + # The table is dynamically expanded when there are too many collisions (i.e., + # keys that have distinct hash codes but fall into the same slot modulo the + # table size), with the expected average effect of maintaining roughly two + # bins per mapping (corresponding to a 0.75 load factor threshold for + # resizing). There may be much variance around this average as mappings are + # added and removed, but overall, this maintains a commonly accepted + # time/space tradeoff for hash tables. However, resizing this or any other + # kind of hash table may be a relatively slow operation. When possible, it is + # a good idea to provide a size estimate as an optional :initial_capacity + # initializer argument. An additional optional :load_factor constructor + # argument provides a further means of customizing initial table capacity by + # specifying the table density to be used in calculating the amount of space + # to allocate for the given number of elements. Note that using many keys with + # exactly the same +hash+ is a sure way to slow down performance of any hash + # table. + # + # ## Design overview + # + # The primary design goal of this hash table is to maintain concurrent + # readability (typically method +[]+, but also iteration and related methods) + # while minimizing update contention. Secondary goals are to keep space + # consumption about the same or better than plain +Hash+, and to support high + # initial insertion rates on an empty table by many threads. + # + # Each key-value mapping is held in a +Node+. The validation-based approach + # explained below leads to a lot of code sprawl because retry-control + # precludes factoring into smaller methods. + # + # The table is lazily initialized to a power-of-two size upon the first + # insertion. Each bin in the table normally contains a list of +Node+s (most + # often, the list has only zero or one +Node+). Table accesses require + # volatile/atomic reads, writes, and CASes. The lists of nodes within bins are + # always accurately traversable under volatile reads, so long as lookups check + # hash code and non-nullness of value before checking key equality. + # + # We use the top two bits of +Node+ hash fields for control purposes -- they + # are available anyway because of addressing constraints. As explained further + # below, these top bits are used as follows: + # + # - 00 - Normal + # - 01 - Locked + # - 11 - Locked and may have a thread waiting for lock + # - 10 - +Node+ is a forwarding node + # + # The lower 28 bits of each +Node+'s hash field contain a the key's hash code, + # except for forwarding nodes, for which the lower bits are zero (and so + # always have hash field == +MOVED+). + # + # Insertion (via +[]=+ or its variants) of the first node in an empty bin is + # performed by just CASing it to the bin. This is by far the most common case + # for put operations under most key/hash distributions. Other update + # operations (insert, delete, and replace) require locks. We do not want to + # waste the space required to associate a distinct lock object with each bin, + # so instead use the first node of a bin list itself as a lock. Blocking + # support for these locks relies +Concurrent::ThreadSafe::Util::CheapLockable. However, we also need a + # +try_lock+ construction, so we overlay these by using bits of the +Node+ + # hash field for lock control (see above), and so normally use builtin + # monitors only for blocking and signalling using + # +cheap_wait+/+cheap_broadcast+ constructions. See +Node#try_await_lock+. + # + # Using the first node of a list as a lock does not by itself suffice though: + # When a node is locked, any update must first validate that it is still the + # first node after locking it, and retry if not. Because new nodes are always + # appended to lists, once a node is first in a bin, it remains first until + # deleted or the bin becomes invalidated (upon resizing). However, operations + # that only conditionally update may inspect nodes until the point of update. + # This is a converse of sorts to the lazy locking technique described by + # Herlihy & Shavit. + # + # The main disadvantage of per-bin locks is that other update operations on + # other nodes in a bin list protected by the same lock can stall, for example + # when user +eql?+ or mapping functions take a long time. However, + # statistically, under random hash codes, this is not a common problem. + # Ideally, the frequency of nodes in bins follows a Poisson distribution + # (http://en.wikipedia.org/wiki/Poisson_distribution) with a parameter of + # about 0.5 on average, given the resizing threshold of 0.75, although with a + # large variance because of resizing granularity. Ignoring variance, the + # expected occurrences of list size k are (exp(-0.5) * pow(0.5, k) / + # factorial(k)). The first values are: + # + # - 0: 0.60653066 + # - 1: 0.30326533 + # - 2: 0.07581633 + # - 3: 0.01263606 + # - 4: 0.00157952 + # - 5: 0.00015795 + # - 6: 0.00001316 + # - 7: 0.00000094 + # - 8: 0.00000006 + # - more: less than 1 in ten million + # + # Lock contention probability for two threads accessing distinct elements is + # roughly 1 / (8 * #elements) under random hashes. + # + # The table is resized when occupancy exceeds a percentage threshold + # (nominally, 0.75, but see below). Only a single thread performs the resize + # (using field +size_control+, to arrange exclusion), but the table otherwise + # remains usable for reads and updates. Resizing proceeds by transferring + # bins, one by one, from the table to the next table. Because we are using + # power-of-two expansion, the elements from each bin must either stay at same + # index, or move with a power of two offset. We eliminate unnecessary node + # creation by catching cases where old nodes can be reused because their next + # fields won't change. On average, only about one-sixth of them need cloning + # when a table doubles. The nodes they replace will be garbage collectable as + # soon as they are no longer referenced by any reader thread that may be in + # the midst of concurrently traversing table. Upon transfer, the old table bin + # contains only a special forwarding node (with hash field +MOVED+) that + # contains the next table as its key. On encountering a forwarding node, + # access and update operations restart, using the new table. + # + # Each bin transfer requires its bin lock. However, unlike other cases, a + # transfer can skip a bin if it fails to acquire its lock, and revisit it + # later. Method +rebuild+ maintains a buffer of TRANSFER_BUFFER_SIZE bins that + # have been skipped because of failure to acquire a lock, and blocks only if + # none are available (i.e., only very rarely). The transfer operation must + # also ensure that all accessible bins in both the old and new table are + # usable by any traversal. When there are no lock acquisition failures, this + # is arranged simply by proceeding from the last bin (+table.size - 1+) up + # towards the first. Upon seeing a forwarding node, traversals arrange to move + # to the new table without revisiting nodes. However, when any node is skipped + # during a transfer, all earlier table bins may have become visible, so are + # initialized with a reverse-forwarding node back to the old table until the + # new ones are established. (This sometimes requires transiently locking a + # forwarding node, which is possible under the above encoding.) These more + # expensive mechanics trigger only when necessary. + # + # The traversal scheme also applies to partial traversals of + # ranges of bins (via an alternate Traverser constructor) + # to support partitioned aggregate operations. Also, read-only + # operations give up if ever forwarded to a null table, which + # provides support for shutdown-style clearing, which is also not + # currently implemented. + # + # Lazy table initialization minimizes footprint until first use. + # + # The element count is maintained using a +Concurrent::ThreadSafe::Util::Adder+, + # which avoids contention on updates but can encounter cache thrashing + # if read too frequently during concurrent access. To avoid reading so + # often, resizing is attempted either when a bin lock is + # contended, or upon adding to a bin already holding two or more + # nodes (checked before adding in the +x_if_absent+ methods, after + # adding in others). Under uniform hash distributions, the + # probability of this occurring at threshold is around 13%, + # meaning that only about 1 in 8 puts check threshold (and after + # resizing, many fewer do so). But this approximation has high + # variance for small table sizes, so we check on any collision + # for sizes <= 64. The bulk putAll operation further reduces + # contention by only committing count updates upon these size + # checks. + # + # @!visibility private + class AtomicReferenceMapBackend + + # @!visibility private + class Table < Concurrent::ThreadSafe::Util::PowerOfTwoTuple + def cas_new_node(i, hash, key, value) + cas(i, nil, Node.new(hash, key, value)) + end + + def try_to_cas_in_computed(i, hash, key) + succeeded = false + new_value = nil + new_node = Node.new(locked_hash = hash | LOCKED, key, NULL) + if cas(i, nil, new_node) + begin + if NULL == (new_value = yield(NULL)) + was_null = true + else + new_node.value = new_value + end + succeeded = true + ensure + volatile_set(i, nil) if !succeeded || was_null + new_node.unlock_via_hash(locked_hash, hash) + end + end + return succeeded, new_value + end + + def try_lock_via_hash(i, node, node_hash) + node.try_lock_via_hash(node_hash) do + yield if volatile_get(i) == node + end + end + + def delete_node_at(i, node, predecessor_node) + if predecessor_node + predecessor_node.next = node.next + else + volatile_set(i, node.next) + end + end + end + + # Key-value entry. Nodes with a hash field of +MOVED+ are special, and do + # not contain user keys or values. Otherwise, keys are never +nil+, and + # +NULL+ +value+ fields indicate that a node is in the process of being + # deleted or created. For purposes of read-only access, a key may be read + # before a value, but can only be used after checking value to be +!= NULL+. + # + # @!visibility private + class Node + extend Concurrent::ThreadSafe::Util::Volatile + attr_volatile :hash, :value, :next + + include Concurrent::ThreadSafe::Util::CheapLockable + + bit_shift = Concurrent::ThreadSafe::Util::FIXNUM_BIT_SIZE - 2 # need 2 bits for ourselves + # Encodings for special uses of Node hash fields. See above for explanation. + MOVED = ('10' << ('0' * bit_shift)).to_i(2) # hash field for forwarding nodes + LOCKED = ('01' << ('0' * bit_shift)).to_i(2) # set/tested only as a bit + WAITING = ('11' << ('0' * bit_shift)).to_i(2) # both bits set/tested together + HASH_BITS = ('00' << ('1' * bit_shift)).to_i(2) # usable bits of normal node hash + + SPIN_LOCK_ATTEMPTS = Concurrent::ThreadSafe::Util::CPU_COUNT > 1 ? Concurrent::ThreadSafe::Util::CPU_COUNT * 2 : 0 + + attr_reader :key + + def initialize(hash, key, value, next_node = nil) + super() + @key = key + self.lazy_set_hash(hash) + self.lazy_set_value(value) + self.next = next_node + end + + # Spins a while if +LOCKED+ bit set and this node is the first of its bin, + # and then sets +WAITING+ bits on hash field and blocks (once) if they are + # still set. It is OK for this method to return even if lock is not + # available upon exit, which enables these simple single-wait mechanics. + # + # The corresponding signalling operation is performed within callers: Upon + # detecting that +WAITING+ has been set when unlocking lock (via a failed + # CAS from non-waiting +LOCKED+ state), unlockers acquire the + # +cheap_synchronize+ lock and perform a +cheap_broadcast+. + def try_await_lock(table, i) + if table && i >= 0 && i < table.size # bounds check, TODO: why are we bounds checking? + spins = SPIN_LOCK_ATTEMPTS + randomizer = base_randomizer = Concurrent::ThreadSafe::Util::XorShiftRandom.get + while equal?(table.volatile_get(i)) && self.class.locked_hash?(my_hash = hash) + if spins >= 0 + if (randomizer = (randomizer >> 1)).even? # spin at random + if (spins -= 1) == 0 + Thread.pass # yield before blocking + else + randomizer = base_randomizer = Concurrent::ThreadSafe::Util::XorShiftRandom.xorshift(base_randomizer) if randomizer.zero? + end + end + elsif cas_hash(my_hash, my_hash | WAITING) + force_acquire_lock(table, i) + break + end + end + end + end + + def key?(key) + @key.eql?(key) + end + + def matches?(key, hash) + pure_hash == hash && key?(key) + end + + def pure_hash + hash & HASH_BITS + end + + def try_lock_via_hash(node_hash = hash) + if cas_hash(node_hash, locked_hash = node_hash | LOCKED) + begin + yield + ensure + unlock_via_hash(locked_hash, node_hash) + end + end + end + + def locked? + self.class.locked_hash?(hash) + end + + def unlock_via_hash(locked_hash, node_hash) + unless cas_hash(locked_hash, node_hash) + self.hash = node_hash + cheap_synchronize { cheap_broadcast } + end + end + + private + def force_acquire_lock(table, i) + cheap_synchronize do + if equal?(table.volatile_get(i)) && (hash & WAITING) == WAITING + cheap_wait + else + cheap_broadcast # possibly won race vs signaller + end + end + end + + class << self + def locked_hash?(hash) + (hash & LOCKED) != 0 + end + end + end + + # shorthands + MOVED = Node::MOVED + LOCKED = Node::LOCKED + WAITING = Node::WAITING + HASH_BITS = Node::HASH_BITS + + NOW_RESIZING = -1 + DEFAULT_CAPACITY = 16 + MAX_CAPACITY = Concurrent::ThreadSafe::Util::MAX_INT + + # The buffer size for skipped bins during transfers. The + # value is arbitrary but should be large enough to avoid + # most locking stalls during resizes. + TRANSFER_BUFFER_SIZE = 32 + + extend Concurrent::ThreadSafe::Util::Volatile + attr_volatile :table, # The array of bins. Lazily initialized upon first insertion. Size is always a power of two. + + # Table initialization and resizing control. When negative, the + # table is being initialized or resized. Otherwise, when table is + # null, holds the initial table size to use upon creation, or 0 + # for default. After initialization, holds the next element count + # value upon which to resize the table. + :size_control + + def initialize(options = nil) + super() + @counter = Concurrent::ThreadSafe::Util::Adder.new + initial_capacity = options && options[:initial_capacity] || DEFAULT_CAPACITY + self.size_control = (capacity = table_size_for(initial_capacity)) > MAX_CAPACITY ? MAX_CAPACITY : capacity + end + + def get_or_default(key, else_value = nil) + hash = key_hash(key) + current_table = table + while current_table + node = current_table.volatile_get_by_hash(hash) + current_table = + while node + if (node_hash = node.hash) == MOVED + break node.key + elsif (node_hash & HASH_BITS) == hash && node.key?(key) && NULL != (value = node.value) + return value + end + node = node.next + end + end + else_value + end + + def [](key) + get_or_default(key) + end + + def key?(key) + get_or_default(key, NULL) != NULL + end + + def []=(key, value) + get_and_set(key, value) + value + end + + def compute_if_absent(key) + hash = key_hash(key) + current_table = table || initialize_table + while true + if !(node = current_table.volatile_get(i = current_table.hash_to_index(hash))) + succeeded, new_value = current_table.try_to_cas_in_computed(i, hash, key) { yield } + if succeeded + increment_size + return new_value + end + elsif (node_hash = node.hash) == MOVED + current_table = node.key + elsif NULL != (current_value = find_value_in_node_list(node, key, hash, node_hash & HASH_BITS)) + return current_value + elsif Node.locked_hash?(node_hash) + try_await_lock(current_table, i, node) + else + succeeded, value = attempt_internal_compute_if_absent(key, hash, current_table, i, node, node_hash) { yield } + return value if succeeded + end + end + end + + def compute_if_present(key) + new_value = nil + internal_replace(key) do |old_value| + if (new_value = yield(NULL == old_value ? nil : old_value)).nil? + NULL + else + new_value + end + end + new_value + end + + def compute(key) + internal_compute(key) do |old_value| + if (new_value = yield(NULL == old_value ? nil : old_value)).nil? + NULL + else + new_value + end + end + end + + def merge_pair(key, value) + internal_compute(key) do |old_value| + if NULL == old_value || !(value = yield(old_value)).nil? + value + else + NULL + end + end + end + + def replace_pair(key, old_value, new_value) + NULL != internal_replace(key, old_value) { new_value } + end + + def replace_if_exists(key, new_value) + if (result = internal_replace(key) { new_value }) && NULL != result + result + end + end + + def get_and_set(key, value) # internalPut in the original CHMV8 + hash = key_hash(key) + current_table = table || initialize_table + while true + if !(node = current_table.volatile_get(i = current_table.hash_to_index(hash))) + if current_table.cas_new_node(i, hash, key, value) + increment_size + break + end + elsif (node_hash = node.hash) == MOVED + current_table = node.key + elsif Node.locked_hash?(node_hash) + try_await_lock(current_table, i, node) + else + succeeded, old_value = attempt_get_and_set(key, value, hash, current_table, i, node, node_hash) + break old_value if succeeded + end + end + end + + def delete(key) + replace_if_exists(key, NULL) + end + + def delete_pair(key, value) + result = internal_replace(key, value) { NULL } + if result && NULL != result + !!result + else + false + end + end + + def each_pair + return self unless current_table = table + current_table_size = base_size = current_table.size + i = base_index = 0 + while base_index < base_size + if node = current_table.volatile_get(i) + if node.hash == MOVED + current_table = node.key + current_table_size = current_table.size + else + begin + if NULL != (value = node.value) # skip deleted or special nodes + yield node.key, value + end + end while node = node.next + end + end + + if (i_with_base = i + base_size) < current_table_size + i = i_with_base # visit upper slots if present + else + i = base_index += 1 + end + end + self + end + + def size + (sum = @counter.sum) < 0 ? 0 : sum # ignore transient negative values + end + + def empty? + size == 0 + end + + # Implementation for clear. Steps through each bin, removing all nodes. + def clear + return self unless current_table = table + current_table_size = current_table.size + deleted_count = i = 0 + while i < current_table_size + if !(node = current_table.volatile_get(i)) + i += 1 + elsif (node_hash = node.hash) == MOVED + current_table = node.key + current_table_size = current_table.size + elsif Node.locked_hash?(node_hash) + decrement_size(deleted_count) # opportunistically update count + deleted_count = 0 + node.try_await_lock(current_table, i) + else + current_table.try_lock_via_hash(i, node, node_hash) do + begin + deleted_count += 1 if NULL != node.value # recheck under lock + node.value = nil + end while node = node.next + current_table.volatile_set(i, nil) + i += 1 + end + end + end + decrement_size(deleted_count) + self + end + + private + # Internal versions of the insertion methods, each a + # little more complicated than the last. All have + # the same basic structure: + # 1. If table uninitialized, create + # 2. If bin empty, try to CAS new node + # 3. If bin stale, use new table + # 4. Lock and validate; if valid, scan and add or update + # + # The others interweave other checks and/or alternative actions: + # * Plain +get_and_set+ checks for and performs resize after insertion. + # * compute_if_absent prescans for mapping without lock (and fails to add + # if present), which also makes pre-emptive resize checks worthwhile. + # + # Someday when details settle down a bit more, it might be worth + # some factoring to reduce sprawl. + def internal_replace(key, expected_old_value = NULL, &block) + hash = key_hash(key) + current_table = table + while current_table + if !(node = current_table.volatile_get(i = current_table.hash_to_index(hash))) + break + elsif (node_hash = node.hash) == MOVED + current_table = node.key + elsif (node_hash & HASH_BITS) != hash && !node.next # precheck + break # rules out possible existence + elsif Node.locked_hash?(node_hash) + try_await_lock(current_table, i, node) + else + succeeded, old_value = attempt_internal_replace(key, expected_old_value, hash, current_table, i, node, node_hash, &block) + return old_value if succeeded + end + end + NULL + end + + def attempt_internal_replace(key, expected_old_value, hash, current_table, i, node, node_hash) + current_table.try_lock_via_hash(i, node, node_hash) do + predecessor_node = nil + old_value = NULL + begin + if node.matches?(key, hash) && NULL != (current_value = node.value) + if NULL == expected_old_value || expected_old_value == current_value # NULL == expected_old_value means whatever value + old_value = current_value + if NULL == (node.value = yield(old_value)) + current_table.delete_node_at(i, node, predecessor_node) + decrement_size + end + end + break + end + + predecessor_node = node + end while node = node.next + + return true, old_value + end + end + + def find_value_in_node_list(node, key, hash, pure_hash) + do_check_for_resize = false + while true + if pure_hash == hash && node.key?(key) && NULL != (value = node.value) + return value + elsif node = node.next + do_check_for_resize = true # at least 2 nodes -> check for resize + pure_hash = node.pure_hash + else + return NULL + end + end + ensure + check_for_resize if do_check_for_resize + end + + def internal_compute(key, &block) + hash = key_hash(key) + current_table = table || initialize_table + while true + if !(node = current_table.volatile_get(i = current_table.hash_to_index(hash))) + succeeded, new_value = current_table.try_to_cas_in_computed(i, hash, key, &block) + if succeeded + if NULL == new_value + break nil + else + increment_size + break new_value + end + end + elsif (node_hash = node.hash) == MOVED + current_table = node.key + elsif Node.locked_hash?(node_hash) + try_await_lock(current_table, i, node) + else + succeeded, new_value = attempt_compute(key, hash, current_table, i, node, node_hash, &block) + break new_value if succeeded + end + end + end + + def attempt_internal_compute_if_absent(key, hash, current_table, i, node, node_hash) + added = false + current_table.try_lock_via_hash(i, node, node_hash) do + while true + if node.matches?(key, hash) && NULL != (value = node.value) + return true, value + end + last = node + unless node = node.next + last.next = Node.new(hash, key, value = yield) + added = true + increment_size + return true, value + end + end + end + ensure + check_for_resize if added + end + + def attempt_compute(key, hash, current_table, i, node, node_hash) + added = false + current_table.try_lock_via_hash(i, node, node_hash) do + predecessor_node = nil + while true + if node.matches?(key, hash) && NULL != (value = node.value) + if NULL == (node.value = value = yield(value)) + current_table.delete_node_at(i, node, predecessor_node) + decrement_size + value = nil + end + return true, value + end + predecessor_node = node + unless node = node.next + if NULL == (value = yield(NULL)) + value = nil + else + predecessor_node.next = Node.new(hash, key, value) + added = true + increment_size + end + return true, value + end + end + end + ensure + check_for_resize if added + end + + def attempt_get_and_set(key, value, hash, current_table, i, node, node_hash) + node_nesting = nil + current_table.try_lock_via_hash(i, node, node_hash) do + node_nesting = 1 + old_value = nil + found_old_value = false + while node + if node.matches?(key, hash) && NULL != (old_value = node.value) + found_old_value = true + node.value = value + break + end + last = node + unless node = node.next + last.next = Node.new(hash, key, value) + break + end + node_nesting += 1 + end + + return true, old_value if found_old_value + increment_size + true + end + ensure + check_for_resize if node_nesting && (node_nesting > 1 || current_table.size <= 64) + end + + def initialize_copy(other) + super + @counter = Concurrent::ThreadSafe::Util::Adder.new + self.table = nil + self.size_control = (other_table = other.table) ? other_table.size : DEFAULT_CAPACITY + self + end + + def try_await_lock(current_table, i, node) + check_for_resize # try resizing if can't get lock + node.try_await_lock(current_table, i) + end + + def key_hash(key) + key.hash & HASH_BITS + end + + # Returns a power of two table size for the given desired capacity. + def table_size_for(entry_count) + size = 2 + size <<= 1 while size < entry_count + size + end + + # Initializes table, using the size recorded in +size_control+. + def initialize_table + until current_table ||= table + if (size_ctrl = size_control) == NOW_RESIZING + Thread.pass # lost initialization race; just spin + else + try_in_resize_lock(current_table, size_ctrl) do + initial_size = size_ctrl > 0 ? size_ctrl : DEFAULT_CAPACITY + current_table = self.table = Table.new(initial_size) + initial_size - (initial_size >> 2) # 75% load factor + end + end + end + current_table + end + + # If table is too small and not already resizing, creates next table and + # transfers bins. Rechecks occupancy after a transfer to see if another + # resize is already needed because resizings are lagging additions. + def check_for_resize + while (current_table = table) && MAX_CAPACITY > (table_size = current_table.size) && NOW_RESIZING != (size_ctrl = size_control) && size_ctrl < @counter.sum + try_in_resize_lock(current_table, size_ctrl) do + self.table = rebuild(current_table) + (table_size << 1) - (table_size >> 1) # 75% load factor + end + end + end + + def try_in_resize_lock(current_table, size_ctrl) + if cas_size_control(size_ctrl, NOW_RESIZING) + begin + if current_table == table # recheck under lock + size_ctrl = yield # get new size_control + end + ensure + self.size_control = size_ctrl + end + end + end + + # Moves and/or copies the nodes in each bin to new table. See above for explanation. + def rebuild(table) + old_table_size = table.size + new_table = table.next_in_size_table + # puts "#{old_table_size} -> #{new_table.size}" + forwarder = Node.new(MOVED, new_table, NULL) + rev_forwarder = nil + locked_indexes = nil # holds bins to revisit; nil until needed + locked_arr_idx = 0 + bin = old_table_size - 1 + i = bin + while true + if !(node = table.volatile_get(i)) + # no lock needed (or available) if bin >= 0, because we're not popping values from locked_indexes until we've run through the whole table + redo unless (bin >= 0 ? table.cas(i, nil, forwarder) : lock_and_clean_up_reverse_forwarders(table, old_table_size, new_table, i, forwarder)) + elsif Node.locked_hash?(node_hash = node.hash) + locked_indexes ||= ::Array.new + if bin < 0 && locked_arr_idx > 0 + locked_arr_idx -= 1 + i, locked_indexes[locked_arr_idx] = locked_indexes[locked_arr_idx], i # swap with another bin + redo + end + if bin < 0 || locked_indexes.size >= TRANSFER_BUFFER_SIZE + node.try_await_lock(table, i) # no other options -- block + redo + end + rev_forwarder ||= Node.new(MOVED, table, NULL) + redo unless table.volatile_get(i) == node && node.locked? # recheck before adding to list + locked_indexes << i + new_table.volatile_set(i, rev_forwarder) + new_table.volatile_set(i + old_table_size, rev_forwarder) + else + redo unless split_old_bin(table, new_table, i, node, node_hash, forwarder) + end + + if bin > 0 + i = (bin -= 1) + elsif locked_indexes && !locked_indexes.empty? + bin = -1 + i = locked_indexes.pop + locked_arr_idx = locked_indexes.size - 1 + else + return new_table + end + end + end + + def lock_and_clean_up_reverse_forwarders(old_table, old_table_size, new_table, i, forwarder) + # transiently use a locked forwarding node + locked_forwarder = Node.new(moved_locked_hash = MOVED | LOCKED, new_table, NULL) + if old_table.cas(i, nil, locked_forwarder) + new_table.volatile_set(i, nil) # kill the potential reverse forwarders + new_table.volatile_set(i + old_table_size, nil) # kill the potential reverse forwarders + old_table.volatile_set(i, forwarder) + locked_forwarder.unlock_via_hash(moved_locked_hash, MOVED) + true + end + end + + # Splits a normal bin with list headed by e into lo and hi parts; installs in given table. + def split_old_bin(table, new_table, i, node, node_hash, forwarder) + table.try_lock_via_hash(i, node, node_hash) do + split_bin(new_table, i, node, node_hash) + table.volatile_set(i, forwarder) + end + end + + def split_bin(new_table, i, node, node_hash) + bit = new_table.size >> 1 # bit to split on + run_bit = node_hash & bit + last_run = nil + low = nil + high = nil + current_node = node + # this optimises for the lowest amount of volatile writes and objects created + while current_node = current_node.next + unless (b = current_node.hash & bit) == run_bit + run_bit = b + last_run = current_node + end + end + if run_bit == 0 + low = last_run + else + high = last_run + end + current_node = node + until current_node == last_run + pure_hash = current_node.pure_hash + if (pure_hash & bit) == 0 + low = Node.new(pure_hash, current_node.key, current_node.value, low) + else + high = Node.new(pure_hash, current_node.key, current_node.value, high) + end + current_node = current_node.next + end + new_table.volatile_set(i, low) + new_table.volatile_set(i + bit, high) + end + + def increment_size + @counter.increment + end + + def decrement_size(by = 1) + @counter.add(-by) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb new file mode 100644 index 0000000..903c1f2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb @@ -0,0 +1,66 @@ +require 'thread' +require 'concurrent/collection/map/non_concurrent_map_backend' + +module Concurrent + + # @!visibility private + module Collection + + # @!visibility private + class MriMapBackend < NonConcurrentMapBackend + + def initialize(options = nil) + super(options) + @write_lock = Mutex.new + end + + def []=(key, value) + @write_lock.synchronize { super } + end + + def compute_if_absent(key) + if NULL != (stored_value = @backend.fetch(key, NULL)) # fast non-blocking path for the most likely case + stored_value + else + @write_lock.synchronize { super } + end + end + + def compute_if_present(key) + @write_lock.synchronize { super } + end + + def compute(key) + @write_lock.synchronize { super } + end + + def merge_pair(key, value) + @write_lock.synchronize { super } + end + + def replace_pair(key, old_value, new_value) + @write_lock.synchronize { super } + end + + def replace_if_exists(key, new_value) + @write_lock.synchronize { super } + end + + def get_and_set(key, value) + @write_lock.synchronize { super } + end + + def delete(key) + @write_lock.synchronize { super } + end + + def delete_pair(key, value) + @write_lock.synchronize { super } + end + + def clear + @write_lock.synchronize { super } + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb new file mode 100644 index 0000000..e7c62e6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb @@ -0,0 +1,140 @@ +require 'concurrent/constants' + +module Concurrent + + # @!visibility private + module Collection + + # @!visibility private + class NonConcurrentMapBackend + + # WARNING: all public methods of the class must operate on the @backend + # directly without calling each other. This is important because of the + # SynchronizedMapBackend which uses a non-reentrant mutex for performance + # reasons. + def initialize(options = nil) + @backend = {} + end + + def [](key) + @backend[key] + end + + def []=(key, value) + @backend[key] = value + end + + def compute_if_absent(key) + if NULL != (stored_value = @backend.fetch(key, NULL)) + stored_value + else + @backend[key] = yield + end + end + + def replace_pair(key, old_value, new_value) + if pair?(key, old_value) + @backend[key] = new_value + true + else + false + end + end + + def replace_if_exists(key, new_value) + if NULL != (stored_value = @backend.fetch(key, NULL)) + @backend[key] = new_value + stored_value + end + end + + def compute_if_present(key) + if NULL != (stored_value = @backend.fetch(key, NULL)) + store_computed_value(key, yield(stored_value)) + end + end + + def compute(key) + store_computed_value(key, yield(@backend[key])) + end + + def merge_pair(key, value) + if NULL == (stored_value = @backend.fetch(key, NULL)) + @backend[key] = value + else + store_computed_value(key, yield(stored_value)) + end + end + + def get_and_set(key, value) + stored_value = @backend[key] + @backend[key] = value + stored_value + end + + def key?(key) + @backend.key?(key) + end + + def delete(key) + @backend.delete(key) + end + + def delete_pair(key, value) + if pair?(key, value) + @backend.delete(key) + true + else + false + end + end + + def clear + @backend.clear + self + end + + def each_pair + dupped_backend.each_pair do |k, v| + yield k, v + end + self + end + + def size + @backend.size + end + + def get_or_default(key, default_value) + @backend.fetch(key, default_value) + end + + alias_method :_get, :[] + alias_method :_set, :[]= + private :_get, :_set + private + def initialize_copy(other) + super + @backend = {} + self + end + + def dupped_backend + @backend.dup + end + + def pair?(key, expected_value) + NULL != (stored_value = @backend.fetch(key, NULL)) && expected_value.equal?(stored_value) + end + + def store_computed_value(key, new_value) + if new_value.nil? + @backend.delete(key) + nil + else + @backend[key] = new_value + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb new file mode 100644 index 0000000..190c8d9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb @@ -0,0 +1,82 @@ +require 'concurrent/collection/map/non_concurrent_map_backend' + +module Concurrent + + # @!visibility private + module Collection + + # @!visibility private + class SynchronizedMapBackend < NonConcurrentMapBackend + + require 'mutex_m' + include Mutex_m + # WARNING: Mutex_m is a non-reentrant lock, so the synchronized methods are + # not allowed to call each other. + + def [](key) + synchronize { super } + end + + def []=(key, value) + synchronize { super } + end + + def compute_if_absent(key) + synchronize { super } + end + + def compute_if_present(key) + synchronize { super } + end + + def compute(key) + synchronize { super } + end + + def merge_pair(key, value) + synchronize { super } + end + + def replace_pair(key, old_value, new_value) + synchronize { super } + end + + def replace_if_exists(key, new_value) + synchronize { super } + end + + def get_and_set(key, value) + synchronize { super } + end + + def key?(key) + synchronize { super } + end + + def delete(key) + synchronize { super } + end + + def delete_pair(key, value) + synchronize { super } + end + + def clear + synchronize { super } + end + + def size + synchronize { super } + end + + def get_or_default(key, default_value) + synchronize { super } + end + + private + def dupped_backend + synchronize { super } + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb new file mode 100644 index 0000000..68a1b38 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb @@ -0,0 +1,14 @@ +module Concurrent + + # @!visibility private + module Collection + + # @!visibility private + class TruffleRubyMapBackend < TruffleRuby::ConcurrentMap + def initialize(options = nil) + options ||= {} + super(initial_capacity: options[:initial_capacity], load_factor: options[:load_factor]) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/non_concurrent_priority_queue.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/non_concurrent_priority_queue.rb new file mode 100644 index 0000000..694cd7a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/non_concurrent_priority_queue.rb @@ -0,0 +1,143 @@ +require 'concurrent/utility/engine' +require 'concurrent/collection/java_non_concurrent_priority_queue' +require 'concurrent/collection/ruby_non_concurrent_priority_queue' + +module Concurrent + module Collection + + # @!visibility private + # @!macro internal_implementation_note + NonConcurrentPriorityQueueImplementation = case + when Concurrent.on_jruby? + JavaNonConcurrentPriorityQueue + else + RubyNonConcurrentPriorityQueue + end + private_constant :NonConcurrentPriorityQueueImplementation + + # @!macro priority_queue + # + # A queue collection in which the elements are sorted based on their + # comparison (spaceship) operator `<=>`. Items are added to the queue + # at a position relative to their priority. On removal the element + # with the "highest" priority is removed. By default the sort order is + # from highest to lowest, but a lowest-to-highest sort order can be + # set on construction. + # + # The API is based on the `Queue` class from the Ruby standard library. + # + # The pure Ruby implementation, `RubyNonConcurrentPriorityQueue` uses a heap algorithm + # stored in an array. The algorithm is based on the work of Robert Sedgewick + # and Kevin Wayne. + # + # The JRuby native implementation is a thin wrapper around the standard + # library `java.util.NonConcurrentPriorityQueue`. + # + # When running under JRuby the class `NonConcurrentPriorityQueue` extends `JavaNonConcurrentPriorityQueue`. + # When running under all other interpreters it extends `RubyNonConcurrentPriorityQueue`. + # + # @note This implementation is *not* thread safe. + # + # @see http://en.wikipedia.org/wiki/Priority_queue + # @see http://ruby-doc.org/stdlib-2.0.0/libdoc/thread/rdoc/Queue.html + # + # @see http://algs4.cs.princeton.edu/24pq/index.php#2.6 + # @see http://algs4.cs.princeton.edu/24pq/MaxPQ.java.html + # + # @see http://docs.oracle.com/javase/7/docs/api/java/util/PriorityQueue.html + # + # @!visibility private + class NonConcurrentPriorityQueue < NonConcurrentPriorityQueueImplementation + + alias_method :has_priority?, :include? + + alias_method :size, :length + + alias_method :deq, :pop + alias_method :shift, :pop + + alias_method :<<, :push + alias_method :enq, :push + + # @!method initialize(opts = {}) + # @!macro priority_queue_method_initialize + # + # Create a new priority queue with no items. + # + # @param [Hash] opts the options for creating the queue + # @option opts [Symbol] :order (:max) dictates the order in which items are + # stored: from highest to lowest when `:max` or `:high`; from lowest to + # highest when `:min` or `:low` + + # @!method clear + # @!macro priority_queue_method_clear + # + # Removes all of the elements from this priority queue. + + # @!method delete(item) + # @!macro priority_queue_method_delete + # + # Deletes all items from `self` that are equal to `item`. + # + # @param [Object] item the item to be removed from the queue + # @return [Object] true if the item is found else false + + # @!method empty? + # @!macro priority_queue_method_empty + # + # Returns `true` if `self` contains no elements. + # + # @return [Boolean] true if there are no items in the queue else false + + # @!method include?(item) + # @!macro priority_queue_method_include + # + # Returns `true` if the given item is present in `self` (that is, if any + # element == `item`), otherwise returns false. + # + # @param [Object] item the item to search for + # + # @return [Boolean] true if the item is found else false + + # @!method length + # @!macro priority_queue_method_length + # + # The current length of the queue. + # + # @return [Fixnum] the number of items in the queue + + # @!method peek + # @!macro priority_queue_method_peek + # + # Retrieves, but does not remove, the head of this queue, or returns `nil` + # if this queue is empty. + # + # @return [Object] the head of the queue or `nil` when empty + + # @!method pop + # @!macro priority_queue_method_pop + # + # Retrieves and removes the head of this queue, or returns `nil` if this + # queue is empty. + # + # @return [Object] the head of the queue or `nil` when empty + + # @!method push(item) + # @!macro priority_queue_method_push + # + # Inserts the specified element into this priority queue. + # + # @param [Object] item the item to insert onto the queue + + # @!method self.from_list(list, opts = {}) + # @!macro priority_queue_method_from_list + # + # Create a new priority queue from the given list. + # + # @param [Enumerable] list the list to build the queue from + # @param [Hash] opts the options for creating the queue + # + # @return [NonConcurrentPriorityQueue] the newly created and populated queue + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb new file mode 100644 index 0000000..322b4ac --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb @@ -0,0 +1,160 @@ +module Concurrent + module Collection + + # @!macro priority_queue + # + # @!visibility private + # @!macro internal_implementation_note + class RubyNonConcurrentPriorityQueue + + # @!macro priority_queue_method_initialize + def initialize(opts = {}) + order = opts.fetch(:order, :max) + @comparator = [:min, :low].include?(order) ? -1 : 1 + clear + end + + # @!macro priority_queue_method_clear + def clear + @queue = [nil] + @length = 0 + true + end + + # @!macro priority_queue_method_delete + def delete(item) + return false if empty? + original_length = @length + k = 1 + while k <= @length + if @queue[k] == item + swap(k, @length) + @length -= 1 + sink(k) || swim(k) + @queue.pop + else + k += 1 + end + end + @length != original_length + end + + # @!macro priority_queue_method_empty + def empty? + size == 0 + end + + # @!macro priority_queue_method_include + def include?(item) + @queue.include?(item) + end + alias_method :has_priority?, :include? + + # @!macro priority_queue_method_length + def length + @length + end + alias_method :size, :length + + # @!macro priority_queue_method_peek + def peek + empty? ? nil : @queue[1] + end + + # @!macro priority_queue_method_pop + def pop + return nil if empty? + max = @queue[1] + swap(1, @length) + @length -= 1 + sink(1) + @queue.pop + max + end + alias_method :deq, :pop + alias_method :shift, :pop + + # @!macro priority_queue_method_push + def push(item) + raise ArgumentError.new('cannot enqueue nil') if item.nil? + @length += 1 + @queue << item + swim(@length) + true + end + alias_method :<<, :push + alias_method :enq, :push + + # @!macro priority_queue_method_from_list + def self.from_list(list, opts = {}) + queue = new(opts) + list.each{|item| queue << item } + queue + end + + private + + # Exchange the values at the given indexes within the internal array. + # + # @param [Integer] x the first index to swap + # @param [Integer] y the second index to swap + # + # @!visibility private + def swap(x, y) + temp = @queue[x] + @queue[x] = @queue[y] + @queue[y] = temp + end + + # Are the items at the given indexes ordered based on the priority + # order specified at construction? + # + # @param [Integer] x the first index from which to retrieve a comparable value + # @param [Integer] y the second index from which to retrieve a comparable value + # + # @return [Boolean] true if the two elements are in the correct priority order + # else false + # + # @!visibility private + def ordered?(x, y) + (@queue[x] <=> @queue[y]) == @comparator + end + + # Percolate down to maintain heap invariant. + # + # @param [Integer] k the index at which to start the percolation + # + # @!visibility private + def sink(k) + success = false + + while (j = (2 * k)) <= @length do + j += 1 if j < @length && ! ordered?(j, j+1) + break if ordered?(k, j) + swap(k, j) + success = true + k = j + end + + success + end + + # Percolate up to maintain heap invariant. + # + # @param [Integer] k the index at which to start the percolation + # + # @!visibility private + def swim(k) + success = false + + while k > 1 && ! ordered?(k/2, k) do + swap(k, k/2) + k = k/2 + success = true + end + + success + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/concern/deprecation.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/concern/deprecation.rb new file mode 100644 index 0000000..35ae4b2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/concern/deprecation.rb @@ -0,0 +1,34 @@ +require 'concurrent/concern/logging' + +module Concurrent + module Concern + + # @!visibility private + # @!macro internal_implementation_note + module Deprecation + # TODO require additional parameter: a version. Display when it'll be removed based on that. Error if not removed. + include Concern::Logging + + def deprecated(message, strip = 2) + caller_line = caller(strip).first if strip > 0 + klass = if Module === self + self + else + self.class + end + message = if strip > 0 + format("[DEPRECATED] %s\ncalled on: %s", message, caller_line) + else + format('[DEPRECATED] %s', message) + end + log WARN, klass.to_s, message + end + + def deprecated_method(old_name, new_name) + deprecated "`#{old_name}` is deprecated and it'll removed in next release, use `#{new_name}` instead", 3 + end + + extend self + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/concern/dereferenceable.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/concern/dereferenceable.rb new file mode 100644 index 0000000..dc172ba --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/concern/dereferenceable.rb @@ -0,0 +1,73 @@ +module Concurrent + module Concern + + # Object references in Ruby are mutable. This can lead to serious problems when + # the `#value` of a concurrent object is a mutable reference. Which is always the + # case unless the value is a `Fixnum`, `Symbol`, or similar "primitive" data type. + # Most classes in this library that expose a `#value` getter method do so using the + # `Dereferenceable` mixin module. + # + # @!macro copy_options + module Dereferenceable + # NOTE: This module is going away in 2.0. In the mean time we need it to + # play nicely with the synchronization layer. This means that the + # including class SHOULD be synchronized and it MUST implement a + # `#synchronize` method. Not doing so will lead to runtime errors. + + # Return the value this object represents after applying the options specified + # by the `#set_deref_options` method. + # + # @return [Object] the current value of the object + def value + synchronize { apply_deref_options(@value) } + end + alias_method :deref, :value + + protected + + # Set the internal value of this object + # + # @param [Object] value the new value + def value=(value) + synchronize{ @value = value } + end + + # @!macro dereferenceable_set_deref_options + # Set the options which define the operations #value performs before + # returning data to the caller (dereferencing). + # + # @note Most classes that include this module will call `#set_deref_options` + # from within the constructor, thus allowing these options to be set at + # object creation. + # + # @param [Hash] opts the options defining dereference behavior. + # @option opts [String] :dup_on_deref (false) call `#dup` before returning the data + # @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data + # @option opts [String] :copy_on_deref (nil) call the given `Proc` passing + # the internal value and returning the value returned from the proc + def set_deref_options(opts = {}) + synchronize{ ns_set_deref_options(opts) } + end + + # @!macro dereferenceable_set_deref_options + # @!visibility private + def ns_set_deref_options(opts) + @dup_on_deref = opts[:dup_on_deref] || opts[:dup] + @freeze_on_deref = opts[:freeze_on_deref] || opts[:freeze] + @copy_on_deref = opts[:copy_on_deref] || opts[:copy] + @do_nothing_on_deref = !(@dup_on_deref || @freeze_on_deref || @copy_on_deref) + nil + end + + # @!visibility private + def apply_deref_options(value) + return nil if value.nil? + return value if @do_nothing_on_deref + value = @copy_on_deref.call(value) if @copy_on_deref + value = value.dup if @dup_on_deref + value = value.freeze if @freeze_on_deref + value + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/concern/logging.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/concern/logging.rb new file mode 100644 index 0000000..2c74999 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/concern/logging.rb @@ -0,0 +1,32 @@ +require 'logger' + +module Concurrent + module Concern + + # Include where logging is needed + # + # @!visibility private + module Logging + include Logger::Severity + + # Logs through {Concurrent.global_logger}, it can be overridden by setting @logger + # @param [Integer] level one of Logger::Severity constants + # @param [String] progname e.g. a path of an Actor + # @param [String, nil] message when nil block is used to generate the message + # @yieldreturn [String] a message + def log(level, progname, message = nil, &block) + #NOTE: Cannot require 'concurrent/configuration' above due to circular references. + # Assume that the gem has been initialized if we've gotten this far. + logger = if defined?(@logger) && @logger + @logger + else + Concurrent.global_logger + end + logger.call level, progname, message, &block + rescue => error + $stderr.puts "`Concurrent.configuration.logger` failed to log #{[level, progname, message, block]}\n" + + "#{error.message} (#{error.class})\n#{error.backtrace.join "\n"}" + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/concern/obligation.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/concern/obligation.rb new file mode 100644 index 0000000..2c9ac12 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/concern/obligation.rb @@ -0,0 +1,220 @@ +require 'thread' +require 'timeout' + +require 'concurrent/atomic/event' +require 'concurrent/concern/dereferenceable' + +module Concurrent + module Concern + + module Obligation + include Concern::Dereferenceable + # NOTE: The Dereferenceable module is going away in 2.0. In the mean time + # we need it to place nicely with the synchronization layer. This means + # that the including class SHOULD be synchronized and it MUST implement a + # `#synchronize` method. Not doing so will lead to runtime errors. + + # Has the obligation been fulfilled? + # + # @return [Boolean] + def fulfilled? + state == :fulfilled + end + alias_method :realized?, :fulfilled? + + # Has the obligation been rejected? + # + # @return [Boolean] + def rejected? + state == :rejected + end + + # Is obligation completion still pending? + # + # @return [Boolean] + def pending? + state == :pending + end + + # Is the obligation still unscheduled? + # + # @return [Boolean] + def unscheduled? + state == :unscheduled + end + + # Has the obligation completed processing? + # + # @return [Boolean] + def complete? + [:fulfilled, :rejected].include? state + end + + # Is the obligation still awaiting completion of processing? + # + # @return [Boolean] + def incomplete? + ! complete? + end + + # The current value of the obligation. Will be `nil` while the state is + # pending or the operation has been rejected. + # + # @param [Numeric] timeout the maximum time in seconds to wait. + # @return [Object] see Dereferenceable#deref + def value(timeout = nil) + wait timeout + deref + end + + # Wait until obligation is complete or the timeout has been reached. + # + # @param [Numeric] timeout the maximum time in seconds to wait. + # @return [Obligation] self + def wait(timeout = nil) + event.wait(timeout) if timeout != 0 && incomplete? + self + end + + # Wait until obligation is complete or the timeout is reached. Will re-raise + # any exceptions raised during processing (but will not raise an exception + # on timeout). + # + # @param [Numeric] timeout the maximum time in seconds to wait. + # @return [Obligation] self + # @raise [Exception] raises the reason when rejected + def wait!(timeout = nil) + wait(timeout).tap { raise self if rejected? } + end + alias_method :no_error!, :wait! + + # The current value of the obligation. Will be `nil` while the state is + # pending or the operation has been rejected. Will re-raise any exceptions + # raised during processing (but will not raise an exception on timeout). + # + # @param [Numeric] timeout the maximum time in seconds to wait. + # @return [Object] see Dereferenceable#deref + # @raise [Exception] raises the reason when rejected + def value!(timeout = nil) + wait(timeout) + if rejected? + raise self + else + deref + end + end + + # The current state of the obligation. + # + # @return [Symbol] the current state + def state + synchronize { @state } + end + + # If an exception was raised during processing this will return the + # exception object. Will return `nil` when the state is pending or if + # the obligation has been successfully fulfilled. + # + # @return [Exception] the exception raised during processing or `nil` + def reason + synchronize { @reason } + end + + # @example allows Obligation to be risen + # rejected_ivar = Ivar.new.fail + # raise rejected_ivar + def exception(*args) + raise 'obligation is not rejected' unless rejected? + reason.exception(*args) + end + + protected + + # @!visibility private + def get_arguments_from(opts = {}) + [*opts.fetch(:args, [])] + end + + # @!visibility private + def init_obligation + @event = Event.new + @value = @reason = nil + end + + # @!visibility private + def event + @event + end + + # @!visibility private + def set_state(success, value, reason) + if success + @value = value + @state = :fulfilled + else + @reason = reason + @state = :rejected + end + end + + # @!visibility private + def state=(value) + synchronize { ns_set_state(value) } + end + + # Atomic compare and set operation + # State is set to `next_state` only if `current state == expected_current`. + # + # @param [Symbol] next_state + # @param [Symbol] expected_current + # + # @return [Boolean] true is state is changed, false otherwise + # + # @!visibility private + def compare_and_set_state(next_state, *expected_current) + synchronize do + if expected_current.include? @state + @state = next_state + true + else + false + end + end + end + + # Executes the block within mutex if current state is included in expected_states + # + # @return block value if executed, false otherwise + # + # @!visibility private + def if_state(*expected_states) + synchronize do + raise ArgumentError.new('no block given') unless block_given? + + if expected_states.include? @state + yield + else + false + end + end + end + + protected + + # Am I in the current state? + # + # @param [Symbol] expected The state to check against + # @return [Boolean] true if in the expected state else false + # + # @!visibility private + def ns_check_state?(expected) + @state == expected + end + + # @!visibility private + def ns_set_state(value) + @state = value + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/concern/observable.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/concern/observable.rb new file mode 100644 index 0000000..b513271 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/concern/observable.rb @@ -0,0 +1,110 @@ +require 'concurrent/collection/copy_on_notify_observer_set' +require 'concurrent/collection/copy_on_write_observer_set' + +module Concurrent + module Concern + + # The [observer pattern](http://en.wikipedia.org/wiki/Observer_pattern) is one + # of the most useful design patterns. + # + # The workflow is very simple: + # - an `observer` can register itself to a `subject` via a callback + # - many `observers` can be registered to the same `subject` + # - the `subject` notifies all registered observers when its status changes + # - an `observer` can deregister itself when is no more interested to receive + # event notifications + # + # In a single threaded environment the whole pattern is very easy: the + # `subject` can use a simple data structure to manage all its subscribed + # `observer`s and every `observer` can react directly to every event without + # caring about synchronization. + # + # In a multi threaded environment things are more complex. The `subject` must + # synchronize the access to its data structure and to do so currently we're + # using two specialized ObserverSet: {Concurrent::Concern::CopyOnWriteObserverSet} + # and {Concurrent::Concern::CopyOnNotifyObserverSet}. + # + # When implementing and `observer` there's a very important rule to remember: + # **there are no guarantees about the thread that will execute the callback** + # + # Let's take this example + # ``` + # class Observer + # def initialize + # @count = 0 + # end + # + # def update + # @count += 1 + # end + # end + # + # obs = Observer.new + # [obj1, obj2, obj3, obj4].each { |o| o.add_observer(obs) } + # # execute [obj1, obj2, obj3, obj4] + # ``` + # + # `obs` is wrong because the variable `@count` can be accessed by different + # threads at the same time, so it should be synchronized (using either a Mutex + # or an AtomicFixum) + module Observable + + # @!macro observable_add_observer + # + # Adds an observer to this set. If a block is passed, the observer will be + # created by this method and no other params should be passed. + # + # @param [Object] observer the observer to add + # @param [Symbol] func the function to call on the observer during notification. + # Default is :update + # @return [Object] the added observer + def add_observer(observer = nil, func = :update, &block) + observers.add_observer(observer, func, &block) + end + + # As `#add_observer` but can be used for chaining. + # + # @param [Object] observer the observer to add + # @param [Symbol] func the function to call on the observer during notification. + # @return [Observable] self + def with_observer(observer = nil, func = :update, &block) + add_observer(observer, func, &block) + self + end + + # @!macro observable_delete_observer + # + # Remove `observer` as an observer on this object so that it will no + # longer receive notifications. + # + # @param [Object] observer the observer to remove + # @return [Object] the deleted observer + def delete_observer(observer) + observers.delete_observer(observer) + end + + # @!macro observable_delete_observers + # + # Remove all observers associated with this object. + # + # @return [Observable] self + def delete_observers + observers.delete_observers + self + end + + # @!macro observable_count_observers + # + # Return the number of observers associated with this object. + # + # @return [Integer] the observers count + def count_observers + observers.count_observers + end + + protected + + attr_accessor :observers + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/concurrent_ruby.jar b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/concurrent_ruby.jar new file mode 100644 index 0000000..e0d30ec Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/concurrent_ruby.jar differ diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/configuration.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/configuration.rb new file mode 100644 index 0000000..a00dc84 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/configuration.rb @@ -0,0 +1,188 @@ +require 'thread' +require 'concurrent/delay' +require 'concurrent/errors' +require 'concurrent/atomic/atomic_reference' +require 'concurrent/concern/logging' +require 'concurrent/concern/deprecation' +require 'concurrent/executor/immediate_executor' +require 'concurrent/executor/cached_thread_pool' +require 'concurrent/utility/processor_counter' + +module Concurrent + extend Concern::Logging + extend Concern::Deprecation + + autoload :Options, 'concurrent/options' + autoload :TimerSet, 'concurrent/executor/timer_set' + autoload :ThreadPoolExecutor, 'concurrent/executor/thread_pool_executor' + + # @return [Logger] Logger with provided level and output. + def self.create_simple_logger(level = Logger::FATAL, output = $stderr) + # TODO (pitr-ch 24-Dec-2016): figure out why it had to be replaced, stdlogger was deadlocking + lambda do |severity, progname, message = nil, &block| + return false if severity < level + + message = block ? block.call : message + formatted_message = case message + when String + message + when Exception + format "%s (%s)\n%s", + message.message, message.class, (message.backtrace || []).join("\n") + else + message.inspect + end + + output.print format "[%s] %5s -- %s: %s\n", + Time.now.strftime('%Y-%m-%d %H:%M:%S.%L'), + Logger::SEV_LABEL[severity], + progname, + formatted_message + true + end + end + + # Use logger created by #create_simple_logger to log concurrent-ruby messages. + def self.use_simple_logger(level = Logger::FATAL, output = $stderr) + Concurrent.global_logger = create_simple_logger level, output + end + + # @return [Logger] Logger with provided level and output. + # @deprecated + def self.create_stdlib_logger(level = Logger::FATAL, output = $stderr) + logger = Logger.new(output) + logger.level = level + logger.formatter = lambda do |severity, datetime, progname, msg| + formatted_message = case msg + when String + msg + when Exception + format "%s (%s)\n%s", + msg.message, msg.class, (msg.backtrace || []).join("\n") + else + msg.inspect + end + format "[%s] %5s -- %s: %s\n", + datetime.strftime('%Y-%m-%d %H:%M:%S.%L'), + severity, + progname, + formatted_message + end + + lambda do |loglevel, progname, message = nil, &block| + logger.add loglevel, message, progname, &block + end + end + + # Use logger created by #create_stdlib_logger to log concurrent-ruby messages. + # @deprecated + def self.use_stdlib_logger(level = Logger::FATAL, output = $stderr) + Concurrent.global_logger = create_stdlib_logger level, output + end + + # TODO (pitr-ch 27-Dec-2016): remove deadlocking stdlib_logger methods + + # Suppresses all output when used for logging. + NULL_LOGGER = lambda { |level, progname, message = nil, &block| } + + # @!visibility private + GLOBAL_LOGGER = AtomicReference.new(create_simple_logger(Logger::WARN)) + private_constant :GLOBAL_LOGGER + + def self.global_logger + GLOBAL_LOGGER.value + end + + def self.global_logger=(value) + GLOBAL_LOGGER.value = value + end + + # @!visibility private + GLOBAL_FAST_EXECUTOR = Delay.new { Concurrent.new_fast_executor } + private_constant :GLOBAL_FAST_EXECUTOR + + # @!visibility private + GLOBAL_IO_EXECUTOR = Delay.new { Concurrent.new_io_executor } + private_constant :GLOBAL_IO_EXECUTOR + + # @!visibility private + GLOBAL_TIMER_SET = Delay.new { TimerSet.new } + private_constant :GLOBAL_TIMER_SET + + # @!visibility private + GLOBAL_IMMEDIATE_EXECUTOR = ImmediateExecutor.new + private_constant :GLOBAL_IMMEDIATE_EXECUTOR + + # Disables AtExit handlers including pool auto-termination handlers. + # When disabled it will be the application programmer's responsibility + # to ensure that the handlers are shutdown properly prior to application + # exit by calling `AtExit.run` method. + # + # @note this option should be needed only because of `at_exit` ordering + # issues which may arise when running some of the testing frameworks. + # E.g. Minitest's test-suite runs itself in `at_exit` callback which + # executes after the pools are already terminated. Then auto termination + # needs to be disabled and called manually after test-suite ends. + # @note This method should *never* be called + # from within a gem. It should *only* be used from within the main + # application and even then it should be used only when necessary. + # @deprecated Has no effect since it is no longer needed, see https://github.com/ruby-concurrency/concurrent-ruby/pull/841. + # + def self.disable_at_exit_handlers! + deprecated "Method #disable_at_exit_handlers! has no effect since it is no longer needed, see https://github.com/ruby-concurrency/concurrent-ruby/pull/841." + end + + # Global thread pool optimized for short, fast *operations*. + # + # @return [ThreadPoolExecutor] the thread pool + def self.global_fast_executor + GLOBAL_FAST_EXECUTOR.value + end + + # Global thread pool optimized for long, blocking (IO) *tasks*. + # + # @return [ThreadPoolExecutor] the thread pool + def self.global_io_executor + GLOBAL_IO_EXECUTOR.value + end + + def self.global_immediate_executor + GLOBAL_IMMEDIATE_EXECUTOR + end + + # Global thread pool user for global *timers*. + # + # @return [Concurrent::TimerSet] the thread pool + def self.global_timer_set + GLOBAL_TIMER_SET.value + end + + # General access point to global executors. + # @param [Symbol, Executor] executor_identifier symbols: + # - :fast - {Concurrent.global_fast_executor} + # - :io - {Concurrent.global_io_executor} + # - :immediate - {Concurrent.global_immediate_executor} + # @return [Executor] + def self.executor(executor_identifier) + Options.executor(executor_identifier) + end + + def self.new_fast_executor(opts = {}) + FixedThreadPool.new( + [2, Concurrent.processor_count].max, + auto_terminate: opts.fetch(:auto_terminate, true), + idletime: 60, # 1 minute + max_queue: 0, # unlimited + fallback_policy: :abort, # shouldn't matter -- 0 max queue + name: "fast" + ) + end + + def self.new_io_executor(opts = {}) + CachedThreadPool.new( + auto_terminate: opts.fetch(:auto_terminate, true), + fallback_policy: :abort, # shouldn't matter -- 0 max queue + name: "io" + ) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/constants.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/constants.rb new file mode 100644 index 0000000..676c2af --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/constants.rb @@ -0,0 +1,8 @@ +module Concurrent + + # Various classes within allows for +nil+ values to be stored, + # so a special +NULL+ token is required to indicate the "nil-ness". + # @!visibility private + NULL = ::Object.new + +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/dataflow.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/dataflow.rb new file mode 100644 index 0000000..d55f19d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/dataflow.rb @@ -0,0 +1,81 @@ +require 'concurrent/future' +require 'concurrent/atomic/atomic_fixnum' + +module Concurrent + + # @!visibility private + class DependencyCounter # :nodoc: + + def initialize(count, &block) + @counter = AtomicFixnum.new(count) + @block = block + end + + def update(time, value, reason) + if @counter.decrement == 0 + @block.call + end + end + end + + # Dataflow allows you to create a task that will be scheduled when all of its data dependencies are available. + # {include:file:docs-source/dataflow.md} + # + # @param [Future] inputs zero or more `Future` operations that this dataflow depends upon + # + # @yield The operation to perform once all the dependencies are met + # @yieldparam [Future] inputs each of the `Future` inputs to the dataflow + # @yieldreturn [Object] the result of the block operation + # + # @return [Object] the result of all the operations + # + # @raise [ArgumentError] if no block is given + # @raise [ArgumentError] if any of the inputs are not `IVar`s + def dataflow(*inputs, &block) + dataflow_with(Concurrent.global_io_executor, *inputs, &block) + end + module_function :dataflow + + def dataflow_with(executor, *inputs, &block) + call_dataflow(:value, executor, *inputs, &block) + end + module_function :dataflow_with + + def dataflow!(*inputs, &block) + dataflow_with!(Concurrent.global_io_executor, *inputs, &block) + end + module_function :dataflow! + + def dataflow_with!(executor, *inputs, &block) + call_dataflow(:value!, executor, *inputs, &block) + end + module_function :dataflow_with! + + private + + def call_dataflow(method, executor, *inputs, &block) + raise ArgumentError.new('an executor must be provided') if executor.nil? + raise ArgumentError.new('no block given') unless block_given? + unless inputs.all? { |input| input.is_a? IVar } + raise ArgumentError.new("Not all dependencies are IVars.\nDependencies: #{ inputs.inspect }") + end + + result = Future.new(executor: executor) do + values = inputs.map { |input| input.send(method) } + block.call(*values) + end + + if inputs.empty? + result.execute + else + counter = DependencyCounter.new(inputs.size) { result.execute } + + inputs.each do |input| + input.add_observer counter + end + end + + result + end + module_function :call_dataflow +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/delay.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/delay.rb new file mode 100644 index 0000000..83799d0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/delay.rb @@ -0,0 +1,199 @@ +require 'thread' +require 'concurrent/concern/obligation' +require 'concurrent/executor/immediate_executor' +require 'concurrent/synchronization' + +module Concurrent + + # This file has circular require issues. It must be autoloaded here. + autoload :Options, 'concurrent/options' + + # Lazy evaluation of a block yielding an immutable result. Useful for + # expensive operations that may never be needed. It may be non-blocking, + # supports the `Concern::Obligation` interface, and accepts the injection of + # custom executor upon which to execute the block. Processing of + # block will be deferred until the first time `#value` is called. + # At that time the caller can choose to return immediately and let + # the block execute asynchronously, block indefinitely, or block + # with a timeout. + # + # When a `Delay` is created its state is set to `pending`. The value and + # reason are both `nil`. The first time the `#value` method is called the + # enclosed opration will be run and the calling thread will block. Other + # threads attempting to call `#value` will block as well. Once the operation + # is complete the *value* will be set to the result of the operation or the + # *reason* will be set to the raised exception, as appropriate. All threads + # blocked on `#value` will return. Subsequent calls to `#value` will immediately + # return the cached value. The operation will only be run once. This means that + # any side effects created by the operation will only happen once as well. + # + # `Delay` includes the `Concurrent::Concern::Dereferenceable` mixin to support thread + # safety of the reference returned by `#value`. + # + # @!macro copy_options + # + # @!macro delay_note_regarding_blocking + # @note The default behavior of `Delay` is to block indefinitely when + # calling either `value` or `wait`, executing the delayed operation on + # the current thread. This makes the `timeout` value completely + # irrelevant. To enable non-blocking behavior, use the `executor` + # constructor option. This will cause the delayed operation to be + # execute on the given executor, allowing the call to timeout. + # + # @see Concurrent::Concern::Dereferenceable + class Delay < Synchronization::LockableObject + include Concern::Obligation + + # NOTE: Because the global thread pools are lazy-loaded with these objects + # there is a performance hit every time we post a new task to one of these + # thread pools. Subsequently it is critical that `Delay` perform as fast + # as possible post-completion. This class has been highly optimized using + # the benchmark script `examples/lazy_and_delay.rb`. Do NOT attempt to + # DRY-up this class or perform other refactoring with running the + # benchmarks and ensuring that performance is not negatively impacted. + + # Create a new `Delay` in the `:pending` state. + # + # @!macro executor_and_deref_options + # + # @yield the delayed operation to perform + # + # @raise [ArgumentError] if no block is given + def initialize(opts = {}, &block) + raise ArgumentError.new('no block given') unless block_given? + super(&nil) + synchronize { ns_initialize(opts, &block) } + end + + # Return the value this object represents after applying the options + # specified by the `#set_deref_options` method. If the delayed operation + # raised an exception this method will return nil. The execption object + # can be accessed via the `#reason` method. + # + # @param [Numeric] timeout the maximum number of seconds to wait + # @return [Object] the current value of the object + # + # @!macro delay_note_regarding_blocking + def value(timeout = nil) + if @executor # TODO (pitr 12-Sep-2015): broken unsafe read? + super + else + # this function has been optimized for performance and + # should not be modified without running new benchmarks + synchronize do + execute = @evaluation_started = true unless @evaluation_started + if execute + begin + set_state(true, @task.call, nil) + rescue => ex + set_state(false, nil, ex) + end + elsif incomplete? + raise IllegalOperationError, 'Recursive call to #value during evaluation of the Delay' + end + end + if @do_nothing_on_deref + @value + else + apply_deref_options(@value) + end + end + end + + # Return the value this object represents after applying the options + # specified by the `#set_deref_options` method. If the delayed operation + # raised an exception, this method will raise that exception (even when) + # the operation has already been executed). + # + # @param [Numeric] timeout the maximum number of seconds to wait + # @return [Object] the current value of the object + # @raise [Exception] when `#rejected?` raises `#reason` + # + # @!macro delay_note_regarding_blocking + def value!(timeout = nil) + if @executor + super + else + result = value + raise @reason if @reason + result + end + end + + # Return the value this object represents after applying the options + # specified by the `#set_deref_options` method. + # + # @param [Integer] timeout (nil) the maximum number of seconds to wait for + # the value to be computed. When `nil` the caller will block indefinitely. + # + # @return [Object] self + # + # @!macro delay_note_regarding_blocking + def wait(timeout = nil) + if @executor + execute_task_once + super(timeout) + else + value + end + self + end + + # Reconfigures the block returning the value if still `#incomplete?` + # + # @yield the delayed operation to perform + # @return [true, false] if success + def reconfigure(&block) + synchronize do + raise ArgumentError.new('no block given') unless block_given? + unless @evaluation_started + @task = block + true + else + false + end + end + end + + protected + + def ns_initialize(opts, &block) + init_obligation + set_deref_options(opts) + @executor = opts[:executor] + + @task = block + @state = :pending + @evaluation_started = false + end + + private + + # @!visibility private + def execute_task_once # :nodoc: + # this function has been optimized for performance and + # should not be modified without running new benchmarks + execute = task = nil + synchronize do + execute = @evaluation_started = true unless @evaluation_started + task = @task + end + + if execute + executor = Options.executor_from_options(executor: @executor) + executor.post do + begin + result = task.call + success = true + rescue => ex + reason = ex + end + synchronize do + set_state(success, result, reason) + event.set + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/errors.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/errors.rb new file mode 100644 index 0000000..b69fec0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/errors.rb @@ -0,0 +1,69 @@ +module Concurrent + + Error = Class.new(StandardError) + + # Raised when errors occur during configuration. + ConfigurationError = Class.new(Error) + + # Raised when an asynchronous operation is cancelled before execution. + CancelledOperationError = Class.new(Error) + + # Raised when a lifecycle method (such as `stop`) is called in an improper + # sequence or when the object is in an inappropriate state. + LifecycleError = Class.new(Error) + + # Raised when an attempt is made to violate an immutability guarantee. + ImmutabilityError = Class.new(Error) + + # Raised when an operation is attempted which is not legal given the + # receiver's current state + IllegalOperationError = Class.new(Error) + + # Raised when an object's methods are called when it has not been + # properly initialized. + InitializationError = Class.new(Error) + + # Raised when an object with a start/stop lifecycle has been started an + # excessive number of times. Often used in conjunction with a restart + # policy or strategy. + MaxRestartFrequencyError = Class.new(Error) + + # Raised when an attempt is made to modify an immutable object + # (such as an `IVar`) after its final state has been set. + class MultipleAssignmentError < Error + attr_reader :inspection_data + + def initialize(message = nil, inspection_data = nil) + @inspection_data = inspection_data + super message + end + + def inspect + format '%s %s>', super[0..-2], @inspection_data.inspect + end + end + + # Raised by an `Executor` when it is unable to process a given task, + # possibly because of a reject policy or other internal error. + RejectedExecutionError = Class.new(Error) + + # Raised when any finite resource, such as a lock counter, exceeds its + # maximum limit/threshold. + ResourceLimitError = Class.new(Error) + + # Raised when an operation times out. + TimeoutError = Class.new(Error) + + # Aggregates multiple exceptions. + class MultipleErrors < Error + attr_reader :errors + + def initialize(errors, message = "#{errors.size} errors") + @errors = errors + super [*message, + *errors.map { |e| [format('%s (%s)', e.message, e.class), *e.backtrace] }.flatten(1) + ].join("\n") + end + end + +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/exchanger.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/exchanger.rb new file mode 100644 index 0000000..5a99550 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/exchanger.rb @@ -0,0 +1,352 @@ +require 'concurrent/constants' +require 'concurrent/errors' +require 'concurrent/maybe' +require 'concurrent/atomic/atomic_reference' +require 'concurrent/atomic/count_down_latch' +require 'concurrent/utility/engine' +require 'concurrent/utility/monotonic_time' + +module Concurrent + + # @!macro exchanger + # + # A synchronization point at which threads can pair and swap elements within + # pairs. Each thread presents some object on entry to the exchange method, + # matches with a partner thread, and receives its partner's object on return. + # + # @!macro thread_safe_variable_comparison + # + # This implementation is very simple, using only a single slot for each + # exchanger (unlike more advanced implementations which use an "arena"). + # This approach will work perfectly fine when there are only a few threads + # accessing a single `Exchanger`. Beyond a handful of threads the performance + # will degrade rapidly due to contention on the single slot, but the algorithm + # will remain correct. + # + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Exchanger.html java.util.concurrent.Exchanger + # @example + # + # exchanger = Concurrent::Exchanger.new + # + # threads = [ + # Thread.new { puts "first: " << exchanger.exchange('foo', 1) }, #=> "first: bar" + # Thread.new { puts "second: " << exchanger.exchange('bar', 1) } #=> "second: foo" + # ] + # threads.each {|t| t.join(2) } + + # @!visibility private + class AbstractExchanger < Synchronization::Object + + # @!visibility private + CANCEL = ::Object.new + private_constant :CANCEL + + def initialize + super + end + + # @!macro exchanger_method_do_exchange + # + # Waits for another thread to arrive at this exchange point (unless the + # current thread is interrupted), and then transfers the given object to + # it, receiving its object in return. The timeout value indicates the + # approximate number of seconds the method should block while waiting + # for the exchange. When the timeout value is `nil` the method will + # block indefinitely. + # + # @param [Object] value the value to exchange with another thread + # @param [Numeric, nil] timeout in seconds, `nil` blocks indefinitely + # + # @!macro exchanger_method_exchange + # + # In some edge cases when a `timeout` is given a return value of `nil` may be + # ambiguous. Specifically, if `nil` is a valid value in the exchange it will + # be impossible to tell whether `nil` is the actual return value or if it + # signifies timeout. When `nil` is a valid value in the exchange consider + # using {#exchange!} or {#try_exchange} instead. + # + # @return [Object] the value exchanged by the other thread or `nil` on timeout + def exchange(value, timeout = nil) + (value = do_exchange(value, timeout)) == CANCEL ? nil : value + end + + # @!macro exchanger_method_do_exchange + # @!macro exchanger_method_exchange_bang + # + # On timeout a {Concurrent::TimeoutError} exception will be raised. + # + # @return [Object] the value exchanged by the other thread + # @raise [Concurrent::TimeoutError] on timeout + def exchange!(value, timeout = nil) + if (value = do_exchange(value, timeout)) == CANCEL + raise Concurrent::TimeoutError + else + value + end + end + + # @!macro exchanger_method_do_exchange + # @!macro exchanger_method_try_exchange + # + # The return value will be a {Concurrent::Maybe} set to `Just` on success or + # `Nothing` on timeout. + # + # @return [Concurrent::Maybe] on success a `Just` maybe will be returned with + # the item exchanged by the other thread as `#value`; on timeout a + # `Nothing` maybe will be returned with {Concurrent::TimeoutError} as `#reason` + # + # @example + # + # exchanger = Concurrent::Exchanger.new + # + # result = exchanger.exchange(:foo, 0.5) + # + # if result.just? + # puts result.value #=> :bar + # else + # puts 'timeout' + # end + def try_exchange(value, timeout = nil) + if (value = do_exchange(value, timeout)) == CANCEL + Concurrent::Maybe.nothing(Concurrent::TimeoutError) + else + Concurrent::Maybe.just(value) + end + end + + private + + # @!macro exchanger_method_do_exchange + # + # @return [Object, CANCEL] the value exchanged by the other thread; {CANCEL} on timeout + def do_exchange(value, timeout) + raise NotImplementedError + end + end + + # @!macro internal_implementation_note + # @!visibility private + class RubyExchanger < AbstractExchanger + # A simplified version of java.util.concurrent.Exchanger written by + # Doug Lea, Bill Scherer, and Michael Scott with assistance from members + # of JCP JSR-166 Expert Group and released to the public domain. It does + # not include the arena or the multi-processor spin loops. + # http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/concurrent/Exchanger.java + + safe_initialization! + + class Node < Concurrent::Synchronization::Object + attr_atomic :value + safe_initialization! + + def initialize(item) + super() + @Item = item + @Latch = Concurrent::CountDownLatch.new + self.value = nil + end + + def latch + @Latch + end + + def item + @Item + end + end + private_constant :Node + + def initialize + super + end + + private + + attr_atomic(:slot) + + # @!macro exchanger_method_do_exchange + # + # @return [Object, CANCEL] the value exchanged by the other thread; {CANCEL} on timeout + def do_exchange(value, timeout) + + # ALGORITHM + # + # From the original Java version: + # + # > The basic idea is to maintain a "slot", which is a reference to + # > a Node containing both an Item to offer and a "hole" waiting to + # > get filled in. If an incoming "occupying" thread sees that the + # > slot is null, it CAS'es (compareAndSets) a Node there and waits + # > for another to invoke exchange. That second "fulfilling" thread + # > sees that the slot is non-null, and so CASes it back to null, + # > also exchanging items by CASing the hole, plus waking up the + # > occupying thread if it is blocked. In each case CAS'es may + # > fail because a slot at first appears non-null but is null upon + # > CAS, or vice-versa. So threads may need to retry these + # > actions. + # + # This version: + # + # An exchange occurs between an "occupier" thread and a "fulfiller" thread. + # The "slot" is used to setup this interaction. The first thread in the + # exchange puts itself into the slot (occupies) and waits for a fulfiller. + # The second thread removes the occupier from the slot and attempts to + # perform the exchange. Removing the occupier also frees the slot for + # another occupier/fulfiller pair. + # + # Because the occupier and the fulfiller are operating independently and + # because there may be contention with other threads, any failed operation + # indicates contention. Both the occupier and the fulfiller operate within + # spin loops. Any failed actions along the happy path will cause the thread + # to repeat the loop and try again. + # + # When a timeout value is given the thread must be cognizant of time spent + # in the spin loop. The remaining time is checked every loop. When the time + # runs out the thread will exit. + # + # A "node" is the data structure used to perform the exchange. Only the + # occupier's node is necessary. It's the node used for the exchange. + # Each node has an "item," a "hole" (self), and a "latch." The item is the + # node's initial value. It never changes. It's what the fulfiller returns on + # success. The occupier's hole is where the fulfiller put its item. It's the + # item that the occupier returns on success. The latch is used for synchronization. + # Because a thread may act as either an occupier or fulfiller (or possibly + # both in periods of high contention) every thread creates a node when + # the exchange method is first called. + # + # The following steps occur within the spin loop. If any actions fail + # the thread will loop and try again, so long as there is time remaining. + # If time runs out the thread will return CANCEL. + # + # Check the slot for an occupier: + # + # * If the slot is empty try to occupy + # * If the slot is full try to fulfill + # + # Attempt to occupy: + # + # * Attempt to CAS myself into the slot + # * Go to sleep and wait to be woken by a fulfiller + # * If the sleep is successful then the fulfiller completed its happy path + # - Return the value from my hole (the value given by the fulfiller) + # * When the sleep fails (time ran out) attempt to cancel the operation + # - Attempt to CAS myself out of the hole + # - If successful there is no contention + # - Return CANCEL + # - On failure, I am competing with a fulfiller + # - Attempt to CAS my hole to CANCEL + # - On success + # - Let the fulfiller deal with my cancel + # - Return CANCEL + # - On failure the fulfiller has completed its happy path + # - Return th value from my hole (the fulfiller's value) + # + # Attempt to fulfill: + # + # * Attempt to CAS the occupier out of the slot + # - On failure loop again + # * Attempt to CAS my item into the occupier's hole + # - On failure the occupier is trying to cancel + # - Loop again + # - On success we are on the happy path + # - Wake the sleeping occupier + # - Return the occupier's item + + value = NULL if value.nil? # The sentinel allows nil to be a valid value + me = Node.new(value) # create my node in case I need to occupy + end_at = Concurrent.monotonic_time + timeout.to_f # The time to give up + + result = loop do + other = slot + if other && compare_and_set_slot(other, nil) + # try to fulfill + if other.compare_and_set_value(nil, value) + # happy path + other.latch.count_down + break other.item + end + elsif other.nil? && compare_and_set_slot(nil, me) + # try to occupy + timeout = end_at - Concurrent.monotonic_time if timeout + if me.latch.wait(timeout) + # happy path + break me.value + else + # attempt to remove myself from the slot + if compare_and_set_slot(me, nil) + break CANCEL + elsif !me.compare_and_set_value(nil, CANCEL) + # I've failed to block the fulfiller + break me.value + end + end + end + break CANCEL if timeout && Concurrent.monotonic_time >= end_at + end + + result == NULL ? nil : result + end + end + + if Concurrent.on_jruby? + + # @!macro internal_implementation_note + # @!visibility private + class JavaExchanger < AbstractExchanger + + def initialize + @exchanger = java.util.concurrent.Exchanger.new + end + + private + + # @!macro exchanger_method_do_exchange + # + # @return [Object, CANCEL] the value exchanged by the other thread; {CANCEL} on timeout + def do_exchange(value, timeout) + result = nil + if timeout.nil? + Synchronization::JRuby.sleep_interruptibly do + result = @exchanger.exchange(value) + end + else + Synchronization::JRuby.sleep_interruptibly do + result = @exchanger.exchange(value, 1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS) + end + end + result + rescue java.util.concurrent.TimeoutException + CANCEL + end + end + end + + # @!visibility private + # @!macro internal_implementation_note + ExchangerImplementation = case + when Concurrent.on_jruby? + JavaExchanger + else + RubyExchanger + end + private_constant :ExchangerImplementation + + # @!macro exchanger + class Exchanger < ExchangerImplementation + + # @!method initialize + # Creates exchanger instance + + # @!method exchange(value, timeout = nil) + # @!macro exchanger_method_do_exchange + # @!macro exchanger_method_exchange + + # @!method exchange!(value, timeout = nil) + # @!macro exchanger_method_do_exchange + # @!macro exchanger_method_exchange_bang + + # @!method try_exchange(value, timeout = nil) + # @!macro exchanger_method_do_exchange + # @!macro exchanger_method_try_exchange + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb new file mode 100644 index 0000000..f3631bf --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb @@ -0,0 +1,131 @@ +require 'concurrent/errors' +require 'concurrent/concern/deprecation' +require 'concurrent/executor/executor_service' +require 'concurrent/synchronization' + +module Concurrent + + # @!macro abstract_executor_service_public_api + # @!visibility private + class AbstractExecutorService < Synchronization::LockableObject + include ExecutorService + include Concern::Deprecation + + # The set of possible fallback policies that may be set at thread pool creation. + FALLBACK_POLICIES = [:abort, :discard, :caller_runs].freeze + + # @!macro executor_service_attr_reader_fallback_policy + attr_reader :fallback_policy + + attr_reader :name + + # Create a new thread pool. + def initialize(opts = {}, &block) + super(&nil) + synchronize do + @auto_terminate = opts.fetch(:auto_terminate, true) + @name = opts.fetch(:name) if opts.key?(:name) + ns_initialize(opts, &block) + end + end + + def to_s + name ? "#{super[0..-2]} name: #{name}>" : super + end + + # @!macro executor_service_method_shutdown + def shutdown + raise NotImplementedError + end + + # @!macro executor_service_method_kill + def kill + raise NotImplementedError + end + + # @!macro executor_service_method_wait_for_termination + def wait_for_termination(timeout = nil) + raise NotImplementedError + end + + # @!macro executor_service_method_running_question + def running? + synchronize { ns_running? } + end + + # @!macro executor_service_method_shuttingdown_question + def shuttingdown? + synchronize { ns_shuttingdown? } + end + + # @!macro executor_service_method_shutdown_question + def shutdown? + synchronize { ns_shutdown? } + end + + # @!macro executor_service_method_auto_terminate_question + def auto_terminate? + synchronize { @auto_terminate } + end + + # @!macro executor_service_method_auto_terminate_setter + def auto_terminate=(value) + deprecated "Method #auto_terminate= has no effect. Set :auto_terminate option when executor is initialized." + end + + private + + # Returns an action which executes the `fallback_policy` once the queue + # size reaches `max_queue`. The reason for the indirection of an action + # is so that the work can be deferred outside of synchronization. + # + # @param [Array] args the arguments to the task which is being handled. + # + # @!visibility private + def fallback_action(*args) + case fallback_policy + when :abort + lambda { raise RejectedExecutionError } + when :discard + lambda { false } + when :caller_runs + lambda { + begin + yield(*args) + rescue => ex + # let it fail + log DEBUG, ex + end + true + } + else + lambda { fail "Unknown fallback policy #{fallback_policy}" } + end + end + + def ns_execute(*args, &task) + raise NotImplementedError + end + + # @!macro executor_service_method_ns_shutdown_execution + # + # Callback method called when an orderly shutdown has completed. + # The default behavior is to signal all waiting threads. + def ns_shutdown_execution + # do nothing + end + + # @!macro executor_service_method_ns_kill_execution + # + # Callback method called when the executor has been killed. + # The default behavior is to do nothing. + def ns_kill_execution + # do nothing + end + + def ns_auto_terminate? + @auto_terminate + end + + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/cached_thread_pool.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/cached_thread_pool.rb new file mode 100644 index 0000000..de50ed1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/cached_thread_pool.rb @@ -0,0 +1,62 @@ +require 'concurrent/utility/engine' +require 'concurrent/executor/thread_pool_executor' + +module Concurrent + + # A thread pool that dynamically grows and shrinks to fit the current workload. + # New threads are created as needed, existing threads are reused, and threads + # that remain idle for too long are killed and removed from the pool. These + # pools are particularly suited to applications that perform a high volume of + # short-lived tasks. + # + # On creation a `CachedThreadPool` has zero running threads. New threads are + # created on the pool as new operations are `#post`. The size of the pool + # will grow until `#max_length` threads are in the pool or until the number + # of threads exceeds the number of running and pending operations. When a new + # operation is post to the pool the first available idle thread will be tasked + # with the new operation. + # + # Should a thread crash for any reason the thread will immediately be removed + # from the pool. Similarly, threads which remain idle for an extended period + # of time will be killed and reclaimed. Thus these thread pools are very + # efficient at reclaiming unused resources. + # + # The API and behavior of this class are based on Java's `CachedThreadPool` + # + # @!macro thread_pool_options + class CachedThreadPool < ThreadPoolExecutor + + # @!macro cached_thread_pool_method_initialize + # + # Create a new thread pool. + # + # @param [Hash] opts the options defining pool behavior. + # @option opts [Symbol] :fallback_policy (`:abort`) the fallback policy + # + # @raise [ArgumentError] if `fallback_policy` is not a known policy + # + # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newCachedThreadPool-- + def initialize(opts = {}) + defaults = { idletime: DEFAULT_THREAD_IDLETIMEOUT } + overrides = { min_threads: 0, + max_threads: DEFAULT_MAX_POOL_SIZE, + max_queue: DEFAULT_MAX_QUEUE_SIZE } + super(defaults.merge(opts).merge(overrides)) + end + + private + + # @!macro cached_thread_pool_method_initialize + # @!visibility private + def ns_initialize(opts) + super(opts) + if Concurrent.on_jruby? + @max_queue = 0 + @executor = java.util.concurrent.Executors.newCachedThreadPool( + DaemonThreadFactory.new(ns_auto_terminate?)) + @executor.setRejectedExecutionHandler(FALLBACK_POLICY_CLASSES[@fallback_policy].new) + @executor.setKeepAliveTime(opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT), java.util.concurrent.TimeUnit::SECONDS) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/executor_service.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/executor_service.rb new file mode 100644 index 0000000..7e34491 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/executor_service.rb @@ -0,0 +1,185 @@ +require 'concurrent/concern/logging' + +module Concurrent + + ################################################################### + + # @!macro executor_service_method_post + # + # Submit a task to the executor for asynchronous processing. + # + # @param [Array] args zero or more arguments to be passed to the task + # + # @yield the asynchronous task to perform + # + # @return [Boolean] `true` if the task is queued, `false` if the executor + # is not running + # + # @raise [ArgumentError] if no task is given + + # @!macro executor_service_method_left_shift + # + # Submit a task to the executor for asynchronous processing. + # + # @param [Proc] task the asynchronous task to perform + # + # @return [self] returns itself + + # @!macro executor_service_method_can_overflow_question + # + # Does the task queue have a maximum size? + # + # @return [Boolean] True if the task queue has a maximum size else false. + + # @!macro executor_service_method_serialized_question + # + # Does this executor guarantee serialization of its operations? + # + # @return [Boolean] True if the executor guarantees that all operations + # will be post in the order they are received and no two operations may + # occur simultaneously. Else false. + + ################################################################### + + # @!macro executor_service_public_api + # + # @!method post(*args, &task) + # @!macro executor_service_method_post + # + # @!method <<(task) + # @!macro executor_service_method_left_shift + # + # @!method can_overflow? + # @!macro executor_service_method_can_overflow_question + # + # @!method serialized? + # @!macro executor_service_method_serialized_question + + ################################################################### + + # @!macro executor_service_attr_reader_fallback_policy + # @return [Symbol] The fallback policy in effect. Either `:abort`, `:discard`, or `:caller_runs`. + + # @!macro executor_service_method_shutdown + # + # Begin an orderly shutdown. Tasks already in the queue will be executed, + # but no new tasks will be accepted. Has no additional effect if the + # thread pool is not running. + + # @!macro executor_service_method_kill + # + # Begin an immediate shutdown. In-progress tasks will be allowed to + # complete but enqueued tasks will be dismissed and no new tasks + # will be accepted. Has no additional effect if the thread pool is + # not running. + + # @!macro executor_service_method_wait_for_termination + # + # Block until executor shutdown is complete or until `timeout` seconds have + # passed. + # + # @note Does not initiate shutdown or termination. Either `shutdown` or `kill` + # must be called before this method (or on another thread). + # + # @param [Integer] timeout the maximum number of seconds to wait for shutdown to complete + # + # @return [Boolean] `true` if shutdown complete or false on `timeout` + + # @!macro executor_service_method_running_question + # + # Is the executor running? + # + # @return [Boolean] `true` when running, `false` when shutting down or shutdown + + # @!macro executor_service_method_shuttingdown_question + # + # Is the executor shuttingdown? + # + # @return [Boolean] `true` when not running and not shutdown, else `false` + + # @!macro executor_service_method_shutdown_question + # + # Is the executor shutdown? + # + # @return [Boolean] `true` when shutdown, `false` when shutting down or running + + # @!macro executor_service_method_auto_terminate_question + # + # Is the executor auto-terminate when the application exits? + # + # @return [Boolean] `true` when auto-termination is enabled else `false`. + + # @!macro executor_service_method_auto_terminate_setter + # + # + # Set the auto-terminate behavior for this executor. + # @deprecated Has no effect + # @param [Boolean] value The new auto-terminate value to set for this executor. + # @return [Boolean] `true` when auto-termination is enabled else `false`. + + ################################################################### + + # @!macro abstract_executor_service_public_api + # + # @!macro executor_service_public_api + # + # @!attribute [r] fallback_policy + # @!macro executor_service_attr_reader_fallback_policy + # + # @!method shutdown + # @!macro executor_service_method_shutdown + # + # @!method kill + # @!macro executor_service_method_kill + # + # @!method wait_for_termination(timeout = nil) + # @!macro executor_service_method_wait_for_termination + # + # @!method running? + # @!macro executor_service_method_running_question + # + # @!method shuttingdown? + # @!macro executor_service_method_shuttingdown_question + # + # @!method shutdown? + # @!macro executor_service_method_shutdown_question + # + # @!method auto_terminate? + # @!macro executor_service_method_auto_terminate_question + # + # @!method auto_terminate=(value) + # @!macro executor_service_method_auto_terminate_setter + + ################################################################### + + # @!macro executor_service_public_api + # @!visibility private + module ExecutorService + include Concern::Logging + + # @!macro executor_service_method_post + def post(*args, &task) + raise NotImplementedError + end + + # @!macro executor_service_method_left_shift + def <<(task) + post(&task) + self + end + + # @!macro executor_service_method_can_overflow_question + # + # @note Always returns `false` + def can_overflow? + false + end + + # @!macro executor_service_method_serialized_question + # + # @note Always returns `false` + def serialized? + false + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb new file mode 100644 index 0000000..4de512a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb @@ -0,0 +1,220 @@ +require 'concurrent/utility/engine' +require 'concurrent/executor/thread_pool_executor' + +module Concurrent + + # @!macro thread_pool_executor_constant_default_max_pool_size + # Default maximum number of threads that will be created in the pool. + + # @!macro thread_pool_executor_constant_default_min_pool_size + # Default minimum number of threads that will be retained in the pool. + + # @!macro thread_pool_executor_constant_default_max_queue_size + # Default maximum number of tasks that may be added to the task queue. + + # @!macro thread_pool_executor_constant_default_thread_timeout + # Default maximum number of seconds a thread in the pool may remain idle + # before being reclaimed. + + # @!macro thread_pool_executor_constant_default_synchronous + # Default value of the :synchronous option. + + # @!macro thread_pool_executor_attr_reader_max_length + # The maximum number of threads that may be created in the pool. + # @return [Integer] The maximum number of threads that may be created in the pool. + + # @!macro thread_pool_executor_attr_reader_min_length + # The minimum number of threads that may be retained in the pool. + # @return [Integer] The minimum number of threads that may be retained in the pool. + + # @!macro thread_pool_executor_attr_reader_largest_length + # The largest number of threads that have been created in the pool since construction. + # @return [Integer] The largest number of threads that have been created in the pool since construction. + + # @!macro thread_pool_executor_attr_reader_scheduled_task_count + # The number of tasks that have been scheduled for execution on the pool since construction. + # @return [Integer] The number of tasks that have been scheduled for execution on the pool since construction. + + # @!macro thread_pool_executor_attr_reader_completed_task_count + # The number of tasks that have been completed by the pool since construction. + # @return [Integer] The number of tasks that have been completed by the pool since construction. + + # @!macro thread_pool_executor_attr_reader_idletime + # The number of seconds that a thread may be idle before being reclaimed. + # @return [Integer] The number of seconds that a thread may be idle before being reclaimed. + + # @!macro thread_pool_executor_attr_reader_synchronous + # Whether or not a value of 0 for :max_queue option means the queue must perform direct hand-off or rather unbounded queue. + # @return [true, false] + + # @!macro thread_pool_executor_attr_reader_max_queue + # The maximum number of tasks that may be waiting in the work queue at any one time. + # When the queue size reaches `max_queue` subsequent tasks will be rejected in + # accordance with the configured `fallback_policy`. + # + # @return [Integer] The maximum number of tasks that may be waiting in the work queue at any one time. + # When the queue size reaches `max_queue` subsequent tasks will be rejected in + # accordance with the configured `fallback_policy`. + + # @!macro thread_pool_executor_attr_reader_length + # The number of threads currently in the pool. + # @return [Integer] The number of threads currently in the pool. + + # @!macro thread_pool_executor_attr_reader_queue_length + # The number of tasks in the queue awaiting execution. + # @return [Integer] The number of tasks in the queue awaiting execution. + + # @!macro thread_pool_executor_attr_reader_remaining_capacity + # Number of tasks that may be enqueued before reaching `max_queue` and rejecting + # new tasks. A value of -1 indicates that the queue may grow without bound. + # + # @return [Integer] Number of tasks that may be enqueued before reaching `max_queue` and rejecting + # new tasks. A value of -1 indicates that the queue may grow without bound. + + # @!macro thread_pool_executor_method_prune_pool + # Prune the thread pool of unneeded threads + # + # What is being pruned is controlled by the min_threads and idletime + # parameters passed at pool creation time + # + # This is a no-op on some pool implementation (e.g. the Java one). The Ruby + # pool will auto-prune each time a new job is posted. You will need to call + # this method explicitely in case your application post jobs in bursts (a + # lot of jobs and then nothing for long periods) + + # @!macro thread_pool_executor_public_api + # + # @!macro abstract_executor_service_public_api + # + # @!attribute [r] max_length + # @!macro thread_pool_executor_attr_reader_max_length + # + # @!attribute [r] min_length + # @!macro thread_pool_executor_attr_reader_min_length + # + # @!attribute [r] largest_length + # @!macro thread_pool_executor_attr_reader_largest_length + # + # @!attribute [r] scheduled_task_count + # @!macro thread_pool_executor_attr_reader_scheduled_task_count + # + # @!attribute [r] completed_task_count + # @!macro thread_pool_executor_attr_reader_completed_task_count + # + # @!attribute [r] idletime + # @!macro thread_pool_executor_attr_reader_idletime + # + # @!attribute [r] max_queue + # @!macro thread_pool_executor_attr_reader_max_queue + # + # @!attribute [r] length + # @!macro thread_pool_executor_attr_reader_length + # + # @!attribute [r] queue_length + # @!macro thread_pool_executor_attr_reader_queue_length + # + # @!attribute [r] remaining_capacity + # @!macro thread_pool_executor_attr_reader_remaining_capacity + # + # @!method can_overflow? + # @!macro executor_service_method_can_overflow_question + # + # @!method prune_pool + # @!macro thread_pool_executor_method_prune_pool + + + + + # @!macro thread_pool_options + # + # **Thread Pool Options** + # + # Thread pools support several configuration options: + # + # * `idletime`: The number of seconds that a thread may be idle before being reclaimed. + # * `name`: The name of the executor (optional). Printed in the executor's `#to_s` output and + # a `-worker-` name is given to its threads if supported by used Ruby + # implementation. `` is uniq for each thread. + # * `max_queue`: The maximum number of tasks that may be waiting in the work queue at + # any one time. When the queue size reaches `max_queue` and no new threads can be created, + # subsequent tasks will be rejected in accordance with the configured `fallback_policy`. + # * `auto_terminate`: When true (default), the threads started will be marked as daemon. + # * `fallback_policy`: The policy defining how rejected tasks are handled. + # + # Three fallback policies are supported: + # + # * `:abort`: Raise a `RejectedExecutionError` exception and discard the task. + # * `:discard`: Discard the task and return false. + # * `:caller_runs`: Execute the task on the calling thread. + # + # **Shutting Down Thread Pools** + # + # Killing a thread pool while tasks are still being processed, either by calling + # the `#kill` method or at application exit, will have unpredictable results. There + # is no way for the thread pool to know what resources are being used by the + # in-progress tasks. When those tasks are killed the impact on those resources + # cannot be predicted. The *best* practice is to explicitly shutdown all thread + # pools using the provided methods: + # + # * Call `#shutdown` to initiate an orderly termination of all in-progress tasks + # * Call `#wait_for_termination` with an appropriate timeout interval an allow + # the orderly shutdown to complete + # * Call `#kill` *only when* the thread pool fails to shutdown in the allotted time + # + # On some runtime platforms (most notably the JVM) the application will not + # exit until all thread pools have been shutdown. To prevent applications from + # "hanging" on exit, all threads can be marked as daemon according to the + # `:auto_terminate` option. + # + # ```ruby + # pool1 = Concurrent::FixedThreadPool.new(5) # threads will be marked as daemon + # pool2 = Concurrent::FixedThreadPool.new(5, auto_terminate: false) # mark threads as non-daemon + # ``` + # + # @note Failure to properly shutdown a thread pool can lead to unpredictable results. + # Please read *Shutting Down Thread Pools* for more information. + # + # @see http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html Java Tutorials: Thread Pools + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html Java Executors class + # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html Java ExecutorService interface + # @see https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#setDaemon-boolean- + + + + + + # @!macro fixed_thread_pool + # + # A thread pool that reuses a fixed number of threads operating off an unbounded queue. + # At any point, at most `num_threads` will be active processing tasks. When all threads are busy new + # tasks `#post` to the thread pool are enqueued until a thread becomes available. + # Should a thread crash for any reason the thread will immediately be removed + # from the pool and replaced. + # + # The API and behavior of this class are based on Java's `FixedThreadPool` + # + # @!macro thread_pool_options + class FixedThreadPool < ThreadPoolExecutor + + # @!macro fixed_thread_pool_method_initialize + # + # Create a new thread pool. + # + # @param [Integer] num_threads the number of threads to allocate + # @param [Hash] opts the options defining pool behavior. + # @option opts [Symbol] :fallback_policy (`:abort`) the fallback policy + # + # @raise [ArgumentError] if `num_threads` is less than or equal to zero + # @raise [ArgumentError] if `fallback_policy` is not a known policy + # + # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newFixedThreadPool-int- + def initialize(num_threads, opts = {}) + raise ArgumentError.new('number of threads must be greater than zero') if num_threads.to_i < 1 + defaults = { max_queue: DEFAULT_MAX_QUEUE_SIZE, + idletime: DEFAULT_THREAD_IDLETIMEOUT } + overrides = { min_threads: num_threads, + max_threads: num_threads } + super(defaults.merge(opts).merge(overrides)) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/immediate_executor.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/immediate_executor.rb new file mode 100644 index 0000000..282df7a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/immediate_executor.rb @@ -0,0 +1,66 @@ +require 'concurrent/atomic/event' +require 'concurrent/executor/abstract_executor_service' +require 'concurrent/executor/serial_executor_service' + +module Concurrent + + # An executor service which runs all operations on the current thread, + # blocking as necessary. Operations are performed in the order they are + # received and no two operations can be performed simultaneously. + # + # This executor service exists mainly for testing an debugging. When used + # it immediately runs every `#post` operation on the current thread, blocking + # that thread until the operation is complete. This can be very beneficial + # during testing because it makes all operations deterministic. + # + # @note Intended for use primarily in testing and debugging. + class ImmediateExecutor < AbstractExecutorService + include SerialExecutorService + + # Creates a new executor + def initialize + @stopped = Concurrent::Event.new + end + + # @!macro executor_service_method_post + def post(*args, &task) + raise ArgumentError.new('no block given') unless block_given? + return false unless running? + task.call(*args) + true + end + + # @!macro executor_service_method_left_shift + def <<(task) + post(&task) + self + end + + # @!macro executor_service_method_running_question + def running? + ! shutdown? + end + + # @!macro executor_service_method_shuttingdown_question + def shuttingdown? + false + end + + # @!macro executor_service_method_shutdown_question + def shutdown? + @stopped.set? + end + + # @!macro executor_service_method_shutdown + def shutdown + @stopped.set + true + end + alias_method :kill, :shutdown + + # @!macro executor_service_method_wait_for_termination + def wait_for_termination(timeout = nil) + @stopped.wait(timeout) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/indirect_immediate_executor.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/indirect_immediate_executor.rb new file mode 100644 index 0000000..4f9769f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/indirect_immediate_executor.rb @@ -0,0 +1,44 @@ +require 'concurrent/executor/immediate_executor' +require 'concurrent/executor/simple_executor_service' + +module Concurrent + # An executor service which runs all operations on a new thread, blocking + # until it completes. Operations are performed in the order they are received + # and no two operations can be performed simultaneously. + # + # This executor service exists mainly for testing an debugging. When used it + # immediately runs every `#post` operation on a new thread, blocking the + # current thread until the operation is complete. This is similar to how the + # ImmediateExecutor works, but the operation has the full stack of the new + # thread at its disposal. This can be helpful when the operations will spawn + # more operations on the same executor and so on - such a situation might + # overflow the single stack in case of an ImmediateExecutor, which is + # inconsistent with how it would behave for a threaded executor. + # + # @note Intended for use primarily in testing and debugging. + class IndirectImmediateExecutor < ImmediateExecutor + # Creates a new executor + def initialize + super + @internal_executor = SimpleExecutorService.new + end + + # @!macro executor_service_method_post + def post(*args, &task) + raise ArgumentError.new("no block given") unless block_given? + return false unless running? + + event = Concurrent::Event.new + @internal_executor.post do + begin + task.call(*args) + ensure + event.set + end + end + event.wait + + true + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb new file mode 100644 index 0000000..e63e898 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb @@ -0,0 +1,103 @@ +if Concurrent.on_jruby? + + require 'concurrent/errors' + require 'concurrent/utility/engine' + require 'concurrent/executor/abstract_executor_service' + + module Concurrent + + # @!macro abstract_executor_service_public_api + # @!visibility private + class JavaExecutorService < AbstractExecutorService + java_import 'java.lang.Runnable' + + FALLBACK_POLICY_CLASSES = { + abort: java.util.concurrent.ThreadPoolExecutor::AbortPolicy, + discard: java.util.concurrent.ThreadPoolExecutor::DiscardPolicy, + caller_runs: java.util.concurrent.ThreadPoolExecutor::CallerRunsPolicy + }.freeze + private_constant :FALLBACK_POLICY_CLASSES + + def post(*args, &task) + raise ArgumentError.new('no block given') unless block_given? + return fallback_action(*args, &task).call unless running? + @executor.submit Job.new(args, task) + true + rescue Java::JavaUtilConcurrent::RejectedExecutionException + raise RejectedExecutionError + end + + def wait_for_termination(timeout = nil) + if timeout.nil? + ok = @executor.awaitTermination(60, java.util.concurrent.TimeUnit::SECONDS) until ok + true + else + @executor.awaitTermination(1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS) + end + end + + def shutdown + synchronize do + @executor.shutdown + nil + end + end + + def kill + synchronize do + @executor.shutdownNow + nil + end + end + + private + + def ns_running? + !(ns_shuttingdown? || ns_shutdown?) + end + + def ns_shuttingdown? + if @executor.respond_to? :isTerminating + @executor.isTerminating + else + false + end + end + + def ns_shutdown? + @executor.isShutdown || @executor.isTerminated + end + + class Job + include Runnable + def initialize(args, block) + @args = args + @block = block + end + + def run + @block.call(*@args) + end + end + private_constant :Job + end + + class DaemonThreadFactory + # hide include from YARD + send :include, java.util.concurrent.ThreadFactory + + def initialize(daemonize = true) + @daemonize = daemonize + end + + def newThread(runnable) + thread = java.util.concurrent.Executors.defaultThreadFactory().newThread(runnable) + thread.setDaemon(@daemonize) + return thread + end + end + + private_constant :DaemonThreadFactory + + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/java_single_thread_executor.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/java_single_thread_executor.rb new file mode 100644 index 0000000..7aa24f2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/java_single_thread_executor.rb @@ -0,0 +1,30 @@ +if Concurrent.on_jruby? + + require 'concurrent/executor/java_executor_service' + require 'concurrent/executor/serial_executor_service' + + module Concurrent + + # @!macro single_thread_executor + # @!macro abstract_executor_service_public_api + # @!visibility private + class JavaSingleThreadExecutor < JavaExecutorService + include SerialExecutorService + + # @!macro single_thread_executor_method_initialize + def initialize(opts = {}) + super(opts) + end + + private + + def ns_initialize(opts) + @executor = java.util.concurrent.Executors.newSingleThreadExecutor( + DaemonThreadFactory.new(ns_auto_terminate?) + ) + @fallback_policy = opts.fetch(:fallback_policy, :discard) + raise ArgumentError.new("#{@fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICY_CLASSES.keys.include?(@fallback_policy) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb new file mode 100644 index 0000000..1213a95 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb @@ -0,0 +1,140 @@ +if Concurrent.on_jruby? + + require 'concurrent/executor/java_executor_service' + + module Concurrent + + # @!macro thread_pool_executor + # @!macro thread_pool_options + # @!visibility private + class JavaThreadPoolExecutor < JavaExecutorService + + # @!macro thread_pool_executor_constant_default_max_pool_size + DEFAULT_MAX_POOL_SIZE = java.lang.Integer::MAX_VALUE # 2147483647 + + # @!macro thread_pool_executor_constant_default_min_pool_size + DEFAULT_MIN_POOL_SIZE = 0 + + # @!macro thread_pool_executor_constant_default_max_queue_size + DEFAULT_MAX_QUEUE_SIZE = 0 + + # @!macro thread_pool_executor_constant_default_thread_timeout + DEFAULT_THREAD_IDLETIMEOUT = 60 + + # @!macro thread_pool_executor_constant_default_synchronous + DEFAULT_SYNCHRONOUS = false + + # @!macro thread_pool_executor_attr_reader_max_length + attr_reader :max_length + + # @!macro thread_pool_executor_attr_reader_max_queue + attr_reader :max_queue + + # @!macro thread_pool_executor_attr_reader_synchronous + attr_reader :synchronous + + # @!macro thread_pool_executor_method_initialize + def initialize(opts = {}) + super(opts) + end + + # @!macro executor_service_method_can_overflow_question + def can_overflow? + @max_queue != 0 + end + + # @!macro thread_pool_executor_attr_reader_min_length + def min_length + @executor.getCorePoolSize + end + + # @!macro thread_pool_executor_attr_reader_max_length + def max_length + @executor.getMaximumPoolSize + end + + # @!macro thread_pool_executor_attr_reader_length + def length + @executor.getPoolSize + end + + # @!macro thread_pool_executor_attr_reader_largest_length + def largest_length + @executor.getLargestPoolSize + end + + # @!macro thread_pool_executor_attr_reader_scheduled_task_count + def scheduled_task_count + @executor.getTaskCount + end + + # @!macro thread_pool_executor_attr_reader_completed_task_count + def completed_task_count + @executor.getCompletedTaskCount + end + + # @!macro thread_pool_executor_attr_reader_idletime + def idletime + @executor.getKeepAliveTime(java.util.concurrent.TimeUnit::SECONDS) + end + + # @!macro thread_pool_executor_attr_reader_queue_length + def queue_length + @executor.getQueue.size + end + + # @!macro thread_pool_executor_attr_reader_remaining_capacity + def remaining_capacity + @max_queue == 0 ? -1 : @executor.getQueue.remainingCapacity + end + + # @!macro executor_service_method_running_question + def running? + super && !@executor.isTerminating + end + + # @!macro thread_pool_executor_method_prune_pool + def prune_pool + end + + private + + def ns_initialize(opts) + min_length = opts.fetch(:min_threads, DEFAULT_MIN_POOL_SIZE).to_i + max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i + idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i + @max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i + @synchronous = opts.fetch(:synchronous, DEFAULT_SYNCHRONOUS) + @fallback_policy = opts.fetch(:fallback_policy, :abort) + + raise ArgumentError.new("`synchronous` cannot be set unless `max_queue` is 0") if @synchronous && @max_queue > 0 + raise ArgumentError.new("`max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if max_length < DEFAULT_MIN_POOL_SIZE + raise ArgumentError.new("`max_threads` cannot be greater than #{DEFAULT_MAX_POOL_SIZE}") if max_length > DEFAULT_MAX_POOL_SIZE + raise ArgumentError.new("`min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if min_length < DEFAULT_MIN_POOL_SIZE + raise ArgumentError.new("`min_threads` cannot be more than `max_threads`") if min_length > max_length + raise ArgumentError.new("#{fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICY_CLASSES.include?(@fallback_policy) + + if @max_queue == 0 + if @synchronous + queue = java.util.concurrent.SynchronousQueue.new + else + queue = java.util.concurrent.LinkedBlockingQueue.new + end + else + queue = java.util.concurrent.LinkedBlockingQueue.new(@max_queue) + end + + @executor = java.util.concurrent.ThreadPoolExecutor.new( + min_length, + max_length, + idletime, + java.util.concurrent.TimeUnit::SECONDS, + queue, + DaemonThreadFactory.new(ns_auto_terminate?), + FALLBACK_POLICY_CLASSES[@fallback_policy].new) + + end + end + + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb new file mode 100644 index 0000000..1f7301b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb @@ -0,0 +1,82 @@ +require 'concurrent/executor/abstract_executor_service' +require 'concurrent/atomic/event' + +module Concurrent + + # @!macro abstract_executor_service_public_api + # @!visibility private + class RubyExecutorService < AbstractExecutorService + safe_initialization! + + def initialize(*args, &block) + super + @StopEvent = Event.new + @StoppedEvent = Event.new + end + + def post(*args, &task) + raise ArgumentError.new('no block given') unless block_given? + deferred_action = synchronize { + if running? + ns_execute(*args, &task) + else + fallback_action(*args, &task) + end + } + if deferred_action + deferred_action.call + else + true + end + end + + def shutdown + synchronize do + break unless running? + stop_event.set + ns_shutdown_execution + end + true + end + + def kill + synchronize do + break if shutdown? + stop_event.set + ns_kill_execution + stopped_event.set + end + true + end + + def wait_for_termination(timeout = nil) + stopped_event.wait(timeout) + end + + private + + def stop_event + @StopEvent + end + + def stopped_event + @StoppedEvent + end + + def ns_shutdown_execution + stopped_event.set + end + + def ns_running? + !stop_event.set? + end + + def ns_shuttingdown? + !(ns_running? || ns_shutdown?) + end + + def ns_shutdown? + stopped_event.set? + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/ruby_single_thread_executor.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/ruby_single_thread_executor.rb new file mode 100644 index 0000000..916337d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/ruby_single_thread_executor.rb @@ -0,0 +1,21 @@ +require 'concurrent/executor/ruby_thread_pool_executor' + +module Concurrent + + # @!macro single_thread_executor + # @!macro abstract_executor_service_public_api + # @!visibility private + class RubySingleThreadExecutor < RubyThreadPoolExecutor + + # @!macro single_thread_executor_method_initialize + def initialize(opts = {}) + super( + min_threads: 1, + max_threads: 1, + max_queue: 0, + idletime: DEFAULT_THREAD_IDLETIMEOUT, + fallback_policy: opts.fetch(:fallback_policy, :discard), + ) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb new file mode 100644 index 0000000..298dd7f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb @@ -0,0 +1,366 @@ +require 'thread' +require 'concurrent/atomic/event' +require 'concurrent/concern/logging' +require 'concurrent/executor/ruby_executor_service' +require 'concurrent/utility/monotonic_time' + +module Concurrent + + # @!macro thread_pool_executor + # @!macro thread_pool_options + # @!visibility private + class RubyThreadPoolExecutor < RubyExecutorService + + # @!macro thread_pool_executor_constant_default_max_pool_size + DEFAULT_MAX_POOL_SIZE = 2_147_483_647 # java.lang.Integer::MAX_VALUE + + # @!macro thread_pool_executor_constant_default_min_pool_size + DEFAULT_MIN_POOL_SIZE = 0 + + # @!macro thread_pool_executor_constant_default_max_queue_size + DEFAULT_MAX_QUEUE_SIZE = 0 + + # @!macro thread_pool_executor_constant_default_thread_timeout + DEFAULT_THREAD_IDLETIMEOUT = 60 + + # @!macro thread_pool_executor_constant_default_synchronous + DEFAULT_SYNCHRONOUS = false + + # @!macro thread_pool_executor_attr_reader_max_length + attr_reader :max_length + + # @!macro thread_pool_executor_attr_reader_min_length + attr_reader :min_length + + # @!macro thread_pool_executor_attr_reader_idletime + attr_reader :idletime + + # @!macro thread_pool_executor_attr_reader_max_queue + attr_reader :max_queue + + # @!macro thread_pool_executor_attr_reader_synchronous + attr_reader :synchronous + + # @!macro thread_pool_executor_method_initialize + def initialize(opts = {}) + super(opts) + end + + # @!macro thread_pool_executor_attr_reader_largest_length + def largest_length + synchronize { @largest_length } + end + + # @!macro thread_pool_executor_attr_reader_scheduled_task_count + def scheduled_task_count + synchronize { @scheduled_task_count } + end + + # @!macro thread_pool_executor_attr_reader_completed_task_count + def completed_task_count + synchronize { @completed_task_count } + end + + # @!macro executor_service_method_can_overflow_question + def can_overflow? + synchronize { ns_limited_queue? } + end + + # @!macro thread_pool_executor_attr_reader_length + def length + synchronize { @pool.length } + end + + # @!macro thread_pool_executor_attr_reader_queue_length + def queue_length + synchronize { @queue.length } + end + + # @!macro thread_pool_executor_attr_reader_remaining_capacity + def remaining_capacity + synchronize do + if ns_limited_queue? + @max_queue - @queue.length + else + -1 + end + end + end + + # @!visibility private + def remove_busy_worker(worker) + synchronize { ns_remove_busy_worker worker } + end + + # @!visibility private + def ready_worker(worker, last_message) + synchronize { ns_ready_worker worker, last_message } + end + + # @!visibility private + def worker_died(worker) + synchronize { ns_worker_died worker } + end + + # @!visibility private + def worker_task_completed + synchronize { @completed_task_count += 1 } + end + + # @!macro thread_pool_executor_method_prune_pool + def prune_pool + synchronize { ns_prune_pool } + end + + private + + # @!visibility private + def ns_initialize(opts) + @min_length = opts.fetch(:min_threads, DEFAULT_MIN_POOL_SIZE).to_i + @max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i + @idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i + @max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i + @synchronous = opts.fetch(:synchronous, DEFAULT_SYNCHRONOUS) + @fallback_policy = opts.fetch(:fallback_policy, :abort) + + raise ArgumentError.new("`synchronous` cannot be set unless `max_queue` is 0") if @synchronous && @max_queue > 0 + raise ArgumentError.new("#{@fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICIES.include?(@fallback_policy) + raise ArgumentError.new("`max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if @max_length < DEFAULT_MIN_POOL_SIZE + raise ArgumentError.new("`max_threads` cannot be greater than #{DEFAULT_MAX_POOL_SIZE}") if @max_length > DEFAULT_MAX_POOL_SIZE + raise ArgumentError.new("`min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if @min_length < DEFAULT_MIN_POOL_SIZE + raise ArgumentError.new("`min_threads` cannot be more than `max_threads`") if min_length > max_length + + @pool = [] # all workers + @ready = [] # used as a stash (most idle worker is at the start) + @queue = [] # used as queue + # @ready or @queue is empty at all times + @scheduled_task_count = 0 + @completed_task_count = 0 + @largest_length = 0 + @workers_counter = 0 + @ruby_pid = $$ # detects if Ruby has forked + + @gc_interval = opts.fetch(:gc_interval, @idletime / 2.0).to_i # undocumented + @next_gc_time = Concurrent.monotonic_time + @gc_interval + end + + # @!visibility private + def ns_limited_queue? + @max_queue != 0 + end + + # @!visibility private + def ns_execute(*args, &task) + ns_reset_if_forked + + if ns_assign_worker(*args, &task) || ns_enqueue(*args, &task) + @scheduled_task_count += 1 + else + return fallback_action(*args, &task) + end + + ns_prune_pool if @next_gc_time < Concurrent.monotonic_time + nil + end + + # @!visibility private + def ns_shutdown_execution + ns_reset_if_forked + + if @pool.empty? + # nothing to do + stopped_event.set + end + + if @queue.empty? + # no more tasks will be accepted, just stop all workers + @pool.each(&:stop) + end + end + + # @!visibility private + def ns_kill_execution + # TODO log out unprocessed tasks in queue + # TODO try to shutdown first? + @pool.each(&:kill) + @pool.clear + @ready.clear + end + + # tries to assign task to a worker, tries to get one from @ready or to create new one + # @return [true, false] if task is assigned to a worker + # + # @!visibility private + def ns_assign_worker(*args, &task) + # keep growing if the pool is not at the minimum yet + worker, _ = (@ready.pop if @pool.size >= @min_length) || ns_add_busy_worker + if worker + worker << [task, args] + true + else + false + end + rescue ThreadError + # Raised when the operating system refuses to create the new thread + return false + end + + # tries to enqueue task + # @return [true, false] if enqueued + # + # @!visibility private + def ns_enqueue(*args, &task) + return false if @synchronous + + if !ns_limited_queue? || @queue.size < @max_queue + @queue << [task, args] + true + else + false + end + end + + # @!visibility private + def ns_worker_died(worker) + ns_remove_busy_worker worker + replacement_worker = ns_add_busy_worker + ns_ready_worker replacement_worker, Concurrent.monotonic_time, false if replacement_worker + end + + # creates new worker which has to receive work to do after it's added + # @return [nil, Worker] nil of max capacity is reached + # + # @!visibility private + def ns_add_busy_worker + return if @pool.size >= @max_length + + @workers_counter += 1 + @pool << (worker = Worker.new(self, @workers_counter)) + @largest_length = @pool.length if @pool.length > @largest_length + worker + end + + # handle ready worker, giving it new job or assigning back to @ready + # + # @!visibility private + def ns_ready_worker(worker, last_message, success = true) + task_and_args = @queue.shift + if task_and_args + worker << task_and_args + else + # stop workers when !running?, do not return them to @ready + if running? + raise unless last_message + @ready.push([worker, last_message]) + else + worker.stop + end + end + end + + # removes a worker which is not in not tracked in @ready + # + # @!visibility private + def ns_remove_busy_worker(worker) + @pool.delete(worker) + stopped_event.set if @pool.empty? && !running? + true + end + + # try oldest worker if it is idle for enough time, it's returned back at the start + # + # @!visibility private + def ns_prune_pool + now = Concurrent.monotonic_time + stopped_workers = 0 + while !@ready.empty? && (@pool.size - stopped_workers > @min_length) + worker, last_message = @ready.first + if now - last_message > self.idletime + stopped_workers += 1 + @ready.shift + worker << :stop + else break + end + end + + @next_gc_time = Concurrent.monotonic_time + @gc_interval + end + + def ns_reset_if_forked + if $$ != @ruby_pid + @queue.clear + @ready.clear + @pool.clear + @scheduled_task_count = 0 + @completed_task_count = 0 + @largest_length = 0 + @workers_counter = 0 + @ruby_pid = $$ + end + end + + # @!visibility private + class Worker + include Concern::Logging + + def initialize(pool, id) + # instance variables accessed only under pool's lock so no need to sync here again + @queue = Queue.new + @pool = pool + @thread = create_worker @queue, pool, pool.idletime + + if @thread.respond_to?(:name=) + @thread.name = [pool.name, 'worker', id].compact.join('-') + end + end + + def <<(message) + @queue << message + end + + def stop + @queue << :stop + end + + def kill + @thread.kill + end + + private + + def create_worker(queue, pool, idletime) + Thread.new(queue, pool, idletime) do |my_queue, my_pool, my_idletime| + catch(:stop) do + loop do + + case message = my_queue.pop + when :stop + my_pool.remove_busy_worker(self) + throw :stop + + else + task, args = message + run_task my_pool, task, args + my_pool.ready_worker(self, Concurrent.monotonic_time) + end + end + end + end + end + + def run_task(pool, task, args) + task.call(*args) + pool.worker_task_completed + rescue => ex + # let it fail + log DEBUG, ex + rescue Exception => ex + log ERROR, ex + pool.worker_died(self) + throw :stop + end + end + + private_constant :Worker + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb new file mode 100644 index 0000000..17acfd5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb @@ -0,0 +1,35 @@ +require 'concurrent/synchronization' + +module Concurrent + + # A simple utility class that executes a callable and returns and array of three elements: + # success - indicating if the callable has been executed without errors + # value - filled by the callable result if it has been executed without errors, nil otherwise + # reason - the error risen by the callable if it has been executed with errors, nil otherwise + class SafeTaskExecutor < Synchronization::LockableObject + + def initialize(task, opts = {}) + @task = task + @exception_class = opts.fetch(:rescue_exception, false) ? Exception : StandardError + super() # ensures visibility + end + + # @return [Array] + def execute(*args) + success = true + value = reason = nil + + synchronize do + begin + value = @task.call(*args) + success = true + rescue @exception_class => ex + reason = ex + success = false + end + end + + [success, value, reason] + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/serial_executor_service.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/serial_executor_service.rb new file mode 100644 index 0000000..f1c38ec --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/serial_executor_service.rb @@ -0,0 +1,34 @@ +require 'concurrent/executor/executor_service' + +module Concurrent + + # Indicates that the including `ExecutorService` guarantees + # that all operations will occur in the order they are post and that no + # two operations may occur simultaneously. This module provides no + # functionality and provides no guarantees. That is the responsibility + # of the including class. This module exists solely to allow the including + # object to be interrogated for its serialization status. + # + # @example + # class Foo + # include Concurrent::SerialExecutor + # end + # + # foo = Foo.new + # + # foo.is_a? Concurrent::ExecutorService #=> true + # foo.is_a? Concurrent::SerialExecutor #=> true + # foo.serialized? #=> true + # + # @!visibility private + module SerialExecutorService + include ExecutorService + + # @!macro executor_service_method_serialized_question + # + # @note Always returns `true` + def serialized? + true + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb new file mode 100644 index 0000000..d314e90 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb @@ -0,0 +1,107 @@ +require 'concurrent/errors' +require 'concurrent/concern/logging' +require 'concurrent/synchronization' + +module Concurrent + + # Ensures passed jobs in a serialized order never running at the same time. + class SerializedExecution < Synchronization::LockableObject + include Concern::Logging + + def initialize() + super() + synchronize { ns_initialize } + end + + Job = Struct.new(:executor, :args, :block) do + def call + block.call(*args) + end + end + + # Submit a task to the executor for asynchronous processing. + # + # @param [Executor] executor to be used for this job + # + # @param [Array] args zero or more arguments to be passed to the task + # + # @yield the asynchronous task to perform + # + # @return [Boolean] `true` if the task is queued, `false` if the executor + # is not running + # + # @raise [ArgumentError] if no task is given + def post(executor, *args, &task) + posts [[executor, args, task]] + true + end + + # As {#post} but allows to submit multiple tasks at once, it's guaranteed that they will not + # be interleaved by other tasks. + # + # @param [Array, Proc)>] posts array of triplets where + # first is a {ExecutorService}, second is array of args for task, third is a task (Proc) + def posts(posts) + # if can_overflow? + # raise ArgumentError, 'SerializedExecution does not support thread-pools which can overflow' + # end + + return nil if posts.empty? + + jobs = posts.map { |executor, args, task| Job.new executor, args, task } + + job_to_post = synchronize do + if @being_executed + @stash.push(*jobs) + nil + else + @being_executed = true + @stash.push(*jobs[1..-1]) + jobs.first + end + end + + call_job job_to_post if job_to_post + true + end + + private + + def ns_initialize + @being_executed = false + @stash = [] + end + + def call_job(job) + did_it_run = begin + job.executor.post { work(job) } + true + rescue RejectedExecutionError => ex + false + end + + # TODO not the best idea to run it myself + unless did_it_run + begin + work job + rescue => ex + # let it fail + log DEBUG, ex + end + end + end + + # ensures next job is executed if any is stashed + def work(job) + job.call + ensure + synchronize do + job = @stash.shift || (@being_executed = false) + end + + # TODO maybe be able to tell caching pool to just enqueue this job, because the current one end at the end + # of this block + call_job job if job + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/serialized_execution_delegator.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/serialized_execution_delegator.rb new file mode 100644 index 0000000..8197781 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/serialized_execution_delegator.rb @@ -0,0 +1,28 @@ +require 'delegate' +require 'concurrent/executor/serial_executor_service' +require 'concurrent/executor/serialized_execution' + +module Concurrent + + # A wrapper/delegator for any `ExecutorService` that + # guarantees serialized execution of tasks. + # + # @see [SimpleDelegator](http://www.ruby-doc.org/stdlib-2.1.2/libdoc/delegate/rdoc/SimpleDelegator.html) + # @see Concurrent::SerializedExecution + class SerializedExecutionDelegator < SimpleDelegator + include SerialExecutorService + + def initialize(executor) + @executor = executor + @serializer = SerializedExecution.new + super(executor) + end + + # @!macro executor_service_method_post + def post(*args, &task) + raise ArgumentError.new('no block given') unless block_given? + return false unless running? + @serializer.post(@executor, *args, &task) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb new file mode 100644 index 0000000..b87fed5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb @@ -0,0 +1,100 @@ +require 'concurrent/atomics' +require 'concurrent/executor/executor_service' + +module Concurrent + + # An executor service in which every operation spawns a new, + # independently operating thread. + # + # This is perhaps the most inefficient executor service in this + # library. It exists mainly for testing an debugging. Thread creation + # and management is expensive in Ruby and this executor performs no + # resource pooling. This can be very beneficial during testing and + # debugging because it decouples the using code from the underlying + # executor implementation. In production this executor will likely + # lead to suboptimal performance. + # + # @note Intended for use primarily in testing and debugging. + class SimpleExecutorService < RubyExecutorService + + # @!macro executor_service_method_post + def self.post(*args) + raise ArgumentError.new('no block given') unless block_given? + Thread.new(*args) do + Thread.current.abort_on_exception = false + yield(*args) + end + true + end + + # @!macro executor_service_method_left_shift + def self.<<(task) + post(&task) + self + end + + # @!macro executor_service_method_post + def post(*args, &task) + raise ArgumentError.new('no block given') unless block_given? + return false unless running? + @count.increment + Thread.new(*args) do + Thread.current.abort_on_exception = false + begin + yield(*args) + ensure + @count.decrement + @stopped.set if @running.false? && @count.value == 0 + end + end + end + + # @!macro executor_service_method_left_shift + def <<(task) + post(&task) + self + end + + # @!macro executor_service_method_running_question + def running? + @running.true? + end + + # @!macro executor_service_method_shuttingdown_question + def shuttingdown? + @running.false? && ! @stopped.set? + end + + # @!macro executor_service_method_shutdown_question + def shutdown? + @stopped.set? + end + + # @!macro executor_service_method_shutdown + def shutdown + @running.make_false + @stopped.set if @count.value == 0 + true + end + + # @!macro executor_service_method_kill + def kill + @running.make_false + @stopped.set + true + end + + # @!macro executor_service_method_wait_for_termination + def wait_for_termination(timeout = nil) + @stopped.wait(timeout) + end + + private + + def ns_initialize(*args) + @running = Concurrent::AtomicBoolean.new(true) + @stopped = Concurrent::Event.new + @count = Concurrent::AtomicFixnum.new(0) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/single_thread_executor.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/single_thread_executor.rb new file mode 100644 index 0000000..f1474ea --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/single_thread_executor.rb @@ -0,0 +1,57 @@ +require 'concurrent/utility/engine' +require 'concurrent/executor/ruby_single_thread_executor' + +module Concurrent + + if Concurrent.on_jruby? + require 'concurrent/executor/java_single_thread_executor' + end + + SingleThreadExecutorImplementation = case + when Concurrent.on_jruby? + JavaSingleThreadExecutor + else + RubySingleThreadExecutor + end + private_constant :SingleThreadExecutorImplementation + + # @!macro single_thread_executor + # + # A thread pool with a single thread an unlimited queue. Should the thread + # die for any reason it will be removed and replaced, thus ensuring that + # the executor will always remain viable and available to process jobs. + # + # A common pattern for background processing is to create a single thread + # on which an infinite loop is run. The thread's loop blocks on an input + # source (perhaps blocking I/O or a queue) and processes each input as it + # is received. This pattern has several issues. The thread itself is highly + # susceptible to errors during processing. Also, the thread itself must be + # constantly monitored and restarted should it die. `SingleThreadExecutor` + # encapsulates all these bahaviors. The task processor is highly resilient + # to errors from within tasks. Also, should the thread die it will + # automatically be restarted. + # + # The API and behavior of this class are based on Java's `SingleThreadExecutor`. + # + # @!macro abstract_executor_service_public_api + class SingleThreadExecutor < SingleThreadExecutorImplementation + + # @!macro single_thread_executor_method_initialize + # + # Create a new thread pool. + # + # @option opts [Symbol] :fallback_policy (:discard) the policy for handling new + # tasks that are received when the queue size has reached + # `max_queue` or the executor has shut down + # + # @raise [ArgumentError] if `:fallback_policy` is not one of the values specified + # in `FALLBACK_POLICIES` + # + # @see http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html + # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html + + # @!method initialize(opts = {}) + # @!macro single_thread_executor_method_initialize + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/thread_pool_executor.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/thread_pool_executor.rb new file mode 100644 index 0000000..253d46a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/thread_pool_executor.rb @@ -0,0 +1,88 @@ +require 'concurrent/utility/engine' +require 'concurrent/executor/ruby_thread_pool_executor' + +module Concurrent + + if Concurrent.on_jruby? + require 'concurrent/executor/java_thread_pool_executor' + end + + ThreadPoolExecutorImplementation = case + when Concurrent.on_jruby? + JavaThreadPoolExecutor + else + RubyThreadPoolExecutor + end + private_constant :ThreadPoolExecutorImplementation + + # @!macro thread_pool_executor + # + # An abstraction composed of one or more threads and a task queue. Tasks + # (blocks or `proc` objects) are submitted to the pool and added to the queue. + # The threads in the pool remove the tasks and execute them in the order + # they were received. + # + # A `ThreadPoolExecutor` will automatically adjust the pool size according + # to the bounds set by `min-threads` and `max-threads`. When a new task is + # submitted and fewer than `min-threads` threads are running, a new thread + # is created to handle the request, even if other worker threads are idle. + # If there are more than `min-threads` but less than `max-threads` threads + # running, a new thread will be created only if the queue is full. + # + # Threads that are idle for too long will be garbage collected, down to the + # configured minimum options. Should a thread crash it, too, will be garbage collected. + # + # `ThreadPoolExecutor` is based on the Java class of the same name. From + # the official Java documentation; + # + # > Thread pools address two different problems: they usually provide + # > improved performance when executing large numbers of asynchronous tasks, + # > due to reduced per-task invocation overhead, and they provide a means + # > of bounding and managing the resources, including threads, consumed + # > when executing a collection of tasks. Each ThreadPoolExecutor also + # > maintains some basic statistics, such as the number of completed tasks. + # > + # > To be useful across a wide range of contexts, this class provides many + # > adjustable parameters and extensibility hooks. However, programmers are + # > urged to use the more convenient Executors factory methods + # > [CachedThreadPool] (unbounded thread pool, with automatic thread reclamation), + # > [FixedThreadPool] (fixed size thread pool) and [SingleThreadExecutor] (single + # > background thread), that preconfigure settings for the most common usage + # > scenarios. + # + # @!macro thread_pool_options + # + # @!macro thread_pool_executor_public_api + class ThreadPoolExecutor < ThreadPoolExecutorImplementation + + # @!macro thread_pool_executor_method_initialize + # + # Create a new thread pool. + # + # @param [Hash] opts the options which configure the thread pool. + # + # @option opts [Integer] :max_threads (DEFAULT_MAX_POOL_SIZE) the maximum + # number of threads to be created + # @option opts [Integer] :min_threads (DEFAULT_MIN_POOL_SIZE) When a new task is submitted + # and fewer than `min_threads` are running, a new thread is created + # @option opts [Integer] :idletime (DEFAULT_THREAD_IDLETIMEOUT) the maximum + # number of seconds a thread may be idle before being reclaimed + # @option opts [Integer] :max_queue (DEFAULT_MAX_QUEUE_SIZE) the maximum + # number of tasks allowed in the work queue at any one time; a value of + # zero means the queue may grow without bound + # @option opts [Symbol] :fallback_policy (:abort) the policy for handling new + # tasks that are received when the queue size has reached + # `max_queue` or the executor has shut down + # @option opts [Boolean] :synchronous (DEFAULT_SYNCHRONOUS) whether or not a value of 0 + # for :max_queue means the queue must perform direct hand-off rather than unbounded. + # @raise [ArgumentError] if `:max_threads` is less than one + # @raise [ArgumentError] if `:min_threads` is less than zero + # @raise [ArgumentError] if `:fallback_policy` is not one of the values specified + # in `FALLBACK_POLICIES` + # + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html + + # @!method initialize(opts = {}) + # @!macro thread_pool_executor_method_initialize + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/timer_set.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/timer_set.rb new file mode 100644 index 0000000..0dfaf12 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/timer_set.rb @@ -0,0 +1,172 @@ +require 'concurrent/scheduled_task' +require 'concurrent/atomic/event' +require 'concurrent/collection/non_concurrent_priority_queue' +require 'concurrent/executor/executor_service' +require 'concurrent/executor/single_thread_executor' + +require 'concurrent/options' + +module Concurrent + + # Executes a collection of tasks, each after a given delay. A master task + # monitors the set and schedules each task for execution at the appropriate + # time. Tasks are run on the global thread pool or on the supplied executor. + # Each task is represented as a `ScheduledTask`. + # + # @see Concurrent::ScheduledTask + # + # @!macro monotonic_clock_warning + class TimerSet < RubyExecutorService + + # Create a new set of timed tasks. + # + # @!macro executor_options + # + # @param [Hash] opts the options used to specify the executor on which to perform actions + # @option opts [Executor] :executor when set use the given `Executor` instance. + # Three special values are also supported: `:task` returns the global task pool, + # `:operation` returns the global operation pool, and `:immediate` returns a new + # `ImmediateExecutor` object. + def initialize(opts = {}) + super(opts) + end + + # Post a task to be execute run after a given delay (in seconds). If the + # delay is less than 1/100th of a second the task will be immediately post + # to the executor. + # + # @param [Float] delay the number of seconds to wait for before executing the task. + # @param [Array] args the arguments passed to the task on execution. + # + # @yield the task to be performed. + # + # @return [Concurrent::ScheduledTask, false] IVar representing the task if the post + # is successful; false after shutdown. + # + # @raise [ArgumentError] if the intended execution time is not in the future. + # @raise [ArgumentError] if no block is given. + def post(delay, *args, &task) + raise ArgumentError.new('no block given') unless block_given? + return false unless running? + opts = { executor: @task_executor, + args: args, + timer_set: self } + task = ScheduledTask.execute(delay, opts, &task) # may raise exception + task.unscheduled? ? false : task + end + + # Begin an immediate shutdown. In-progress tasks will be allowed to + # complete but enqueued tasks will be dismissed and no new tasks + # will be accepted. Has no additional effect if the thread pool is + # not running. + def kill + shutdown + end + + private :<< + + private + + # Initialize the object. + # + # @param [Hash] opts the options to create the object with. + # @!visibility private + def ns_initialize(opts) + @queue = Collection::NonConcurrentPriorityQueue.new(order: :min) + @task_executor = Options.executor_from_options(opts) || Concurrent.global_io_executor + @timer_executor = SingleThreadExecutor.new + @condition = Event.new + @ruby_pid = $$ # detects if Ruby has forked + end + + # Post the task to the internal queue. + # + # @note This is intended as a callback method from ScheduledTask + # only. It is not intended to be used directly. Post a task + # by using the `SchedulesTask#execute` method. + # + # @!visibility private + def post_task(task) + synchronize { ns_post_task(task) } + end + + # @!visibility private + def ns_post_task(task) + return false unless ns_running? + ns_reset_if_forked + if (task.initial_delay) <= 0.01 + task.executor.post { task.process_task } + else + @queue.push(task) + # only post the process method when the queue is empty + @timer_executor.post(&method(:process_tasks)) if @queue.length == 1 + @condition.set + end + true + end + + # Remove the given task from the queue. + # + # @note This is intended as a callback method from `ScheduledTask` + # only. It is not intended to be used directly. Cancel a task + # by using the `ScheduledTask#cancel` method. + # + # @!visibility private + def remove_task(task) + synchronize { @queue.delete(task) } + end + + # `ExecutorService` callback called during shutdown. + # + # @!visibility private + def ns_shutdown_execution + ns_reset_if_forked + @queue.clear + @timer_executor.kill + stopped_event.set + end + + def ns_reset_if_forked + if $$ != @ruby_pid + @queue.clear + @condition.reset + @ruby_pid = $$ + end + end + + # Run a loop and execute tasks in the scheduled order and at the approximate + # scheduled time. If no tasks remain the thread will exit gracefully so that + # garbage collection can occur. If there are no ready tasks it will sleep + # for up to 60 seconds waiting for the next scheduled task. + # + # @!visibility private + def process_tasks + loop do + task = synchronize { @condition.reset; @queue.peek } + break unless task + + now = Concurrent.monotonic_time + diff = task.schedule_time - now + + if diff <= 0 + # We need to remove the task from the queue before passing + # it to the executor, to avoid race conditions where we pass + # the peek'ed task to the executor and then pop a different + # one that's been added in the meantime. + # + # Note that there's no race condition between the peek and + # this pop - this pop could retrieve a different task from + # the peek, but that task would be due to fire now anyway + # (because @queue is a priority queue, and this thread is + # the only reader, so whatever timer is at the head of the + # queue now must have the same pop time, or a closer one, as + # when we peeked). + task = synchronize { @queue.pop } + task.executor.post { task.process_task } + else + @condition.wait([diff, 60].min) + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executors.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executors.rb new file mode 100644 index 0000000..eb1972c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executors.rb @@ -0,0 +1,20 @@ +require 'concurrent/executor/abstract_executor_service' +require 'concurrent/executor/cached_thread_pool' +require 'concurrent/executor/executor_service' +require 'concurrent/executor/fixed_thread_pool' +require 'concurrent/executor/immediate_executor' +require 'concurrent/executor/indirect_immediate_executor' +require 'concurrent/executor/java_executor_service' +require 'concurrent/executor/java_single_thread_executor' +require 'concurrent/executor/java_thread_pool_executor' +require 'concurrent/executor/ruby_executor_service' +require 'concurrent/executor/ruby_single_thread_executor' +require 'concurrent/executor/ruby_thread_pool_executor' +require 'concurrent/executor/cached_thread_pool' +require 'concurrent/executor/safe_task_executor' +require 'concurrent/executor/serial_executor_service' +require 'concurrent/executor/serialized_execution' +require 'concurrent/executor/serialized_execution_delegator' +require 'concurrent/executor/single_thread_executor' +require 'concurrent/executor/thread_pool_executor' +require 'concurrent/executor/timer_set' diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/future.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/future.rb new file mode 100644 index 0000000..1af182e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/future.rb @@ -0,0 +1,141 @@ +require 'thread' +require 'concurrent/constants' +require 'concurrent/errors' +require 'concurrent/ivar' +require 'concurrent/executor/safe_task_executor' + +require 'concurrent/options' + +# TODO (pitr-ch 14-Mar-2017): deprecate, Future, Promise, etc. + + +module Concurrent + + # {include:file:docs-source/future.md} + # + # @!macro copy_options + # + # @see http://ruby-doc.org/stdlib-2.1.1/libdoc/observer/rdoc/Observable.html Ruby Observable module + # @see http://clojuredocs.org/clojure_core/clojure.core/future Clojure's future function + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html java.util.concurrent.Future + class Future < IVar + + # Create a new `Future` in the `:unscheduled` state. + # + # @yield the asynchronous operation to perform + # + # @!macro executor_and_deref_options + # + # @option opts [object, Array] :args zero or more arguments to be passed the task + # block on execution + # + # @raise [ArgumentError] if no block is given + def initialize(opts = {}, &block) + raise ArgumentError.new('no block given') unless block_given? + super(NULL, opts.merge(__task_from_block__: block), &nil) + end + + # Execute an `:unscheduled` `Future`. Immediately sets the state to `:pending` and + # passes the block to a new thread/thread pool for eventual execution. + # Does nothing if the `Future` is in any state other than `:unscheduled`. + # + # @return [Future] a reference to `self` + # + # @example Instance and execute in separate steps + # future = Concurrent::Future.new{ sleep(1); 42 } + # future.state #=> :unscheduled + # future.execute + # future.state #=> :pending + # + # @example Instance and execute in one line + # future = Concurrent::Future.new{ sleep(1); 42 }.execute + # future.state #=> :pending + def execute + if compare_and_set_state(:pending, :unscheduled) + @executor.post{ safe_execute(@task, @args) } + self + end + end + + # Create a new `Future` object with the given block, execute it, and return the + # `:pending` object. + # + # @yield the asynchronous operation to perform + # + # @!macro executor_and_deref_options + # + # @option opts [object, Array] :args zero or more arguments to be passed the task + # block on execution + # + # @raise [ArgumentError] if no block is given + # + # @return [Future] the newly created `Future` in the `:pending` state + # + # @example + # future = Concurrent::Future.execute{ sleep(1); 42 } + # future.state #=> :pending + def self.execute(opts = {}, &block) + Future.new(opts, &block).execute + end + + # @!macro ivar_set_method + def set(value = NULL, &block) + check_for_block_or_value!(block_given?, value) + synchronize do + if @state != :unscheduled + raise MultipleAssignmentError + else + @task = block || Proc.new { value } + end + end + execute + end + + # Attempt to cancel the operation if it has not already processed. + # The operation can only be cancelled while still `pending`. It cannot + # be cancelled once it has begun processing or has completed. + # + # @return [Boolean] was the operation successfully cancelled. + def cancel + if compare_and_set_state(:cancelled, :pending) + complete(false, nil, CancelledOperationError.new) + true + else + false + end + end + + # Has the operation been successfully cancelled? + # + # @return [Boolean] + def cancelled? + state == :cancelled + end + + # Wait the given number of seconds for the operation to complete. + # On timeout attempt to cancel the operation. + # + # @param [Numeric] timeout the maximum time in seconds to wait. + # @return [Boolean] true if the operation completed before the timeout + # else false + def wait_or_cancel(timeout) + wait(timeout) + if complete? + true + else + cancel + false + end + end + + protected + + def ns_initialize(value, opts) + super + @state = :unscheduled + @task = opts[:__task_from_block__] + @executor = Options.executor_from_options(opts) || Concurrent.global_io_executor + @args = get_arguments_from(opts) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/hash.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/hash.rb new file mode 100644 index 0000000..92df66b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/hash.rb @@ -0,0 +1,59 @@ +require 'concurrent/utility/engine' +require 'concurrent/thread_safe/util' + +module Concurrent + + # @!macro concurrent_hash + # + # A thread-safe subclass of Hash. This version locks against the object + # itself for every method call, ensuring only one thread can be reading + # or writing at a time. This includes iteration methods like `#each`, + # which takes the lock repeatedly when reading an item. + # + # @see http://ruby-doc.org/core/Hash.html Ruby standard library `Hash` + + # @!macro internal_implementation_note + HashImplementation = case + when Concurrent.on_cruby? + # Hash is thread-safe in practice because CRuby runs + # threads one at a time and does not do context + # switching during the execution of C functions. + ::Hash + + when Concurrent.on_jruby? + require 'jruby/synchronized' + + class JRubyHash < ::Hash + include JRuby::Synchronized + end + JRubyHash + + when Concurrent.on_rbx? + require 'monitor' + require 'concurrent/thread_safe/util/data_structures' + + class RbxHash < ::Hash + end + ThreadSafe::Util.make_synchronized_on_rbx RbxHash + RbxHash + + when Concurrent.on_truffleruby? + require 'concurrent/thread_safe/util/data_structures' + + class TruffleRubyHash < ::Hash + end + + ThreadSafe::Util.make_synchronized_on_truffleruby TruffleRubyHash + TruffleRubyHash + + else + warn 'Possibly unsupported Ruby implementation' + ::Hash + end + private_constant :HashImplementation + + # @!macro concurrent_hash + class Hash < HashImplementation + end + +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/immutable_struct.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/immutable_struct.rb new file mode 100644 index 0000000..d275595 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/immutable_struct.rb @@ -0,0 +1,101 @@ +require 'concurrent/synchronization/abstract_struct' +require 'concurrent/synchronization' + +module Concurrent + + # A thread-safe, immutable variation of Ruby's standard `Struct`. + # + # @see http://ruby-doc.org/core/Struct.html Ruby standard library `Struct` + module ImmutableStruct + include Synchronization::AbstractStruct + + def self.included(base) + base.safe_initialization! + end + + # @!macro struct_values + def values + ns_values + end + + alias_method :to_a, :values + + # @!macro struct_values_at + def values_at(*indexes) + ns_values_at(indexes) + end + + # @!macro struct_inspect + def inspect + ns_inspect + end + + alias_method :to_s, :inspect + + # @!macro struct_merge + def merge(other, &block) + ns_merge(other, &block) + end + + # @!macro struct_to_h + def to_h + ns_to_h + end + + # @!macro struct_get + def [](member) + ns_get(member) + end + + # @!macro struct_equality + def ==(other) + ns_equality(other) + end + + # @!macro struct_each + def each(&block) + return enum_for(:each) unless block_given? + ns_each(&block) + end + + # @!macro struct_each_pair + def each_pair(&block) + return enum_for(:each_pair) unless block_given? + ns_each_pair(&block) + end + + # @!macro struct_select + def select(&block) + return enum_for(:select) unless block_given? + ns_select(&block) + end + + private + + # @!visibility private + def initialize_copy(original) + super(original) + ns_initialize_copy + end + + # @!macro struct_new + def self.new(*args, &block) + clazz_name = nil + if args.length == 0 + raise ArgumentError.new('wrong number of arguments (0 for 1+)') + elsif args.length > 0 && args.first.is_a?(String) + clazz_name = args.shift + end + FACTORY.define_struct(clazz_name, args, &block) + end + + FACTORY = Class.new(Synchronization::LockableObject) do + def define_struct(name, members, &block) + synchronize do + Synchronization::AbstractStruct.define_struct_class(ImmutableStruct, Synchronization::Object, name, members, &block) + end + end + end.new + private_constant :FACTORY + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/ivar.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/ivar.rb new file mode 100644 index 0000000..2a724db --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/ivar.rb @@ -0,0 +1,207 @@ +require 'concurrent/constants' +require 'concurrent/errors' +require 'concurrent/collection/copy_on_write_observer_set' +require 'concurrent/concern/obligation' +require 'concurrent/concern/observable' +require 'concurrent/synchronization' + +module Concurrent + + # An `IVar` is like a future that you can assign. As a future is a value that + # is being computed that you can wait on, an `IVar` is a value that is waiting + # to be assigned, that you can wait on. `IVars` are single assignment and + # deterministic. + # + # Then, express futures as an asynchronous computation that assigns an `IVar`. + # The `IVar` becomes the primitive on which [futures](Future) and + # [dataflow](Dataflow) are built. + # + # An `IVar` is a single-element container that is normally created empty, and + # can only be set once. The I in `IVar` stands for immutable. Reading an + # `IVar` normally blocks until it is set. It is safe to set and read an `IVar` + # from different threads. + # + # If you want to have some parallel task set the value in an `IVar`, you want + # a `Future`. If you want to create a graph of parallel tasks all executed + # when the values they depend on are ready you want `dataflow`. `IVar` is + # generally a low-level primitive. + # + # ## Examples + # + # Create, set and get an `IVar` + # + # ```ruby + # ivar = Concurrent::IVar.new + # ivar.set 14 + # ivar.value #=> 14 + # ivar.set 2 # would now be an error + # ``` + # + # ## See Also + # + # 1. For the theory: Arvind, R. Nikhil, and K. Pingali. + # [I-Structures: Data structures for parallel computing](http://dl.acm.org/citation.cfm?id=69562). + # In Proceedings of Workshop on Graph Reduction, 1986. + # 2. For recent application: + # [DataDrivenFuture in Habanero Java from Rice](http://www.cs.rice.edu/~vs3/hjlib/doc/edu/rice/hj/api/HjDataDrivenFuture.html). + class IVar < Synchronization::LockableObject + include Concern::Obligation + include Concern::Observable + + # Create a new `IVar` in the `:pending` state with the (optional) initial value. + # + # @param [Object] value the initial value + # @param [Hash] opts the options to create a message with + # @option opts [String] :dup_on_deref (false) call `#dup` before returning + # the data + # @option opts [String] :freeze_on_deref (false) call `#freeze` before + # returning the data + # @option opts [String] :copy_on_deref (nil) call the given `Proc` passing + # the internal value and returning the value returned from the proc + def initialize(value = NULL, opts = {}, &block) + if value != NULL && block_given? + raise ArgumentError.new('provide only a value or a block') + end + super(&nil) + synchronize { ns_initialize(value, opts, &block) } + end + + # Add an observer on this object that will receive notification on update. + # + # Upon completion the `IVar` will notify all observers in a thread-safe way. + # The `func` method of the observer will be called with three arguments: the + # `Time` at which the `Future` completed the asynchronous operation, the + # final `value` (or `nil` on rejection), and the final `reason` (or `nil` on + # fulfillment). + # + # @param [Object] observer the object that will be notified of changes + # @param [Symbol] func symbol naming the method to call when this + # `Observable` has changes` + def add_observer(observer = nil, func = :update, &block) + raise ArgumentError.new('cannot provide both an observer and a block') if observer && block + direct_notification = false + + if block + observer = block + func = :call + end + + synchronize do + if event.set? + direct_notification = true + else + observers.add_observer(observer, func) + end + end + + observer.send(func, Time.now, self.value, reason) if direct_notification + observer + end + + # @!macro ivar_set_method + # Set the `IVar` to a value and wake or notify all threads waiting on it. + # + # @!macro ivar_set_parameters_and_exceptions + # @param [Object] value the value to store in the `IVar` + # @yield A block operation to use for setting the value + # @raise [ArgumentError] if both a value and a block are given + # @raise [Concurrent::MultipleAssignmentError] if the `IVar` has already + # been set or otherwise completed + # + # @return [IVar] self + def set(value = NULL) + check_for_block_or_value!(block_given?, value) + raise MultipleAssignmentError unless compare_and_set_state(:processing, :pending) + + begin + value = yield if block_given? + complete_without_notification(true, value, nil) + rescue => ex + complete_without_notification(false, nil, ex) + end + + notify_observers(self.value, reason) + self + end + + # @!macro ivar_fail_method + # Set the `IVar` to failed due to some error and wake or notify all threads waiting on it. + # + # @param [Object] reason for the failure + # @raise [Concurrent::MultipleAssignmentError] if the `IVar` has already + # been set or otherwise completed + # @return [IVar] self + def fail(reason = StandardError.new) + complete(false, nil, reason) + end + + # Attempt to set the `IVar` with the given value or block. Return a + # boolean indicating the success or failure of the set operation. + # + # @!macro ivar_set_parameters_and_exceptions + # + # @return [Boolean] true if the value was set else false + def try_set(value = NULL, &block) + set(value, &block) + true + rescue MultipleAssignmentError + false + end + + protected + + # @!visibility private + def ns_initialize(value, opts) + value = yield if block_given? + init_obligation + self.observers = Collection::CopyOnWriteObserverSet.new + set_deref_options(opts) + + @state = :pending + if value != NULL + ns_complete_without_notification(true, value, nil) + end + end + + # @!visibility private + def safe_execute(task, args = []) + if compare_and_set_state(:processing, :pending) + success, val, reason = SafeTaskExecutor.new(task, rescue_exception: true).execute(*@args) + complete(success, val, reason) + yield(success, val, reason) if block_given? + end + end + + # @!visibility private + def complete(success, value, reason) + complete_without_notification(success, value, reason) + notify_observers(self.value, reason) + self + end + + # @!visibility private + def complete_without_notification(success, value, reason) + synchronize { ns_complete_without_notification(success, value, reason) } + self + end + + # @!visibility private + def notify_observers(value, reason) + observers.notify_and_delete_observers{ [Time.now, value, reason] } + end + + # @!visibility private + def ns_complete_without_notification(success, value, reason) + raise MultipleAssignmentError if [:fulfilled, :rejected].include? @state + set_state(success, value, reason) + event.set + end + + # @!visibility private + def check_for_block_or_value!(block_given, value) # :nodoc: + if (block_given && value != NULL) || (! block_given && value == NULL) + raise ArgumentError.new('must set with either a value or a block') + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/map.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/map.rb new file mode 100644 index 0000000..808757f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/map.rb @@ -0,0 +1,346 @@ +require 'thread' +require 'concurrent/constants' +require 'concurrent/synchronization' +require 'concurrent/utility/engine' + +module Concurrent + # @!visibility private + module Collection + + # @!visibility private + MapImplementation = case + when Concurrent.on_jruby? + # noinspection RubyResolve + JRubyMapBackend + when Concurrent.on_cruby? + require 'concurrent/collection/map/mri_map_backend' + MriMapBackend + when Concurrent.on_truffleruby? && defined?(::TruffleRuby::ConcurrentMap) + require 'concurrent/collection/map/truffleruby_map_backend' + TruffleRubyMapBackend + when Concurrent.on_truffleruby? || Concurrent.on_rbx? + require 'concurrent/collection/map/atomic_reference_map_backend' + AtomicReferenceMapBackend + else + warn 'Concurrent::Map: unsupported Ruby engine, using a fully synchronized Concurrent::Map implementation' + require 'concurrent/collection/map/synchronized_map_backend' + SynchronizedMapBackend + end + end + + # `Concurrent::Map` is a hash-like object and should have much better performance + # characteristics, especially under high concurrency, than `Concurrent::Hash`. + # However, `Concurrent::Map `is not strictly semantically equivalent to a ruby `Hash` + # -- for instance, it does not necessarily retain ordering by insertion time as `Hash` + # does. For most uses it should do fine though, and we recommend you consider + # `Concurrent::Map` instead of `Concurrent::Hash` for your concurrency-safe hash needs. + class Map < Collection::MapImplementation + + # @!macro map.atomic_method + # This method is atomic. + + # @!macro map.atomic_method_with_block + # This method is atomic. + # @note Atomic methods taking a block do not allow the `self` instance + # to be used within the block. Doing so will cause a deadlock. + + # @!method compute_if_absent(key) + # Compute and store new value for key if the key is absent. + # @param [Object] key + # @yield new value + # @yieldreturn [Object] new value + # @return [Object] new value or current value + # @!macro map.atomic_method_with_block + + # @!method compute_if_present(key) + # Compute and store new value for key if the key is present. + # @param [Object] key + # @yield new value + # @yieldparam old_value [Object] + # @yieldreturn [Object, nil] new value, when nil the key is removed + # @return [Object, nil] new value or nil + # @!macro map.atomic_method_with_block + + # @!method compute(key) + # Compute and store new value for key. + # @param [Object] key + # @yield compute new value from old one + # @yieldparam old_value [Object, nil] old_value, or nil when key is absent + # @yieldreturn [Object, nil] new value, when nil the key is removed + # @return [Object, nil] new value or nil + # @!macro map.atomic_method_with_block + + # @!method merge_pair(key, value) + # If the key is absent, the value is stored, otherwise new value is + # computed with a block. + # @param [Object] key + # @param [Object] value + # @yield compute new value from old one + # @yieldparam old_value [Object] old value + # @yieldreturn [Object, nil] new value, when nil the key is removed + # @return [Object, nil] new value or nil + # @!macro map.atomic_method_with_block + + # @!method replace_pair(key, old_value, new_value) + # Replaces old_value with new_value if key exists and current value + # matches old_value + # @param [Object] key + # @param [Object] old_value + # @param [Object] new_value + # @return [true, false] true if replaced + # @!macro map.atomic_method + + # @!method replace_if_exists(key, new_value) + # Replaces current value with new_value if key exists + # @param [Object] key + # @param [Object] new_value + # @return [Object, nil] old value or nil + # @!macro map.atomic_method + + # @!method get_and_set(key, value) + # Get the current value under key and set new value. + # @param [Object] key + # @param [Object] value + # @return [Object, nil] old value or nil when the key was absent + # @!macro map.atomic_method + + # @!method delete(key) + # Delete key and its value. + # @param [Object] key + # @return [Object, nil] old value or nil when the key was absent + # @!macro map.atomic_method + + # @!method delete_pair(key, value) + # Delete pair and its value if current value equals the provided value. + # @param [Object] key + # @param [Object] value + # @return [true, false] true if deleted + # @!macro map.atomic_method + + # + def initialize(options = nil, &block) + if options.kind_of?(::Hash) + validate_options_hash!(options) + else + options = nil + end + + super(options) + @default_proc = block + end + + # Get a value with key + # @param [Object] key + # @return [Object] the value + def [](key) + if value = super # non-falsy value is an existing mapping, return it right away + value + # re-check is done with get_or_default(key, NULL) instead of a simple !key?(key) in order to avoid a race condition, whereby by the time the current thread gets to the key?(key) call + # a key => value mapping might have already been created by a different thread (key?(key) would then return true, this elsif branch wouldn't be taken and an incorrent +nil+ value + # would be returned) + # note: nil == value check is not technically necessary + elsif @default_proc && nil == value && NULL == (value = get_or_default(key, NULL)) + @default_proc.call(self, key) + else + value + end + end + + # Set a value with key + # @param [Object] key + # @param [Object] value + # @return [Object] the new value + def []=(key, value) + super + end + + alias_method :get, :[] + alias_method :put, :[]= + + # Get a value with key, or default_value when key is absent, + # or fail when no default_value is given. + # @param [Object] key + # @param [Object] default_value + # @yield default value for a key + # @yieldparam key [Object] + # @yieldreturn [Object] default value + # @return [Object] the value or default value + # @raise [KeyError] when key is missing and no default_value is provided + # @!macro map_method_not_atomic + # @note The "fetch-then-act" methods of `Map` are not atomic. `Map` is intended + # to be use as a concurrency primitive with strong happens-before + # guarantees. It is not intended to be used as a high-level abstraction + # supporting complex operations. All read and write operations are + # thread safe, but no guarantees are made regarding race conditions + # between the fetch operation and yielding to the block. Additionally, + # this method does not support recursion. This is due to internal + # constraints that are very unlikely to change in the near future. + def fetch(key, default_value = NULL) + if NULL != (value = get_or_default(key, NULL)) + value + elsif block_given? + yield key + elsif NULL != default_value + default_value + else + raise_fetch_no_key + end + end + + # Fetch value with key, or store default value when key is absent, + # or fail when no default_value is given. This is a two step operation, + # therefore not atomic. The store can overwrite other concurrently + # stored value. + # @param [Object] key + # @param [Object] default_value + # @yield default value for a key + # @yieldparam key [Object] + # @yieldreturn [Object] default value + # @return [Object] the value or default value + # @!macro map.atomic_method_with_block + def fetch_or_store(key, default_value = NULL) + fetch(key) do + put(key, block_given? ? yield(key) : (NULL == default_value ? raise_fetch_no_key : default_value)) + end + end + + # Insert value into map with key if key is absent in one atomic step. + # @param [Object] key + # @param [Object] value + # @return [Object, nil] the previous value when key was present or nil when there was no key + def put_if_absent(key, value) + computed = false + result = compute_if_absent(key) do + computed = true + value + end + computed ? nil : result + end unless method_defined?(:put_if_absent) + + # Is the value stored in the map. Iterates over all values. + # @param [Object] value + # @return [true, false] + def value?(value) + each_value do |v| + return true if value.equal?(v) + end + false + end + + # All keys + # @return [::Array] keys + def keys + arr = [] + each_pair { |k, v| arr << k } + arr + end unless method_defined?(:keys) + + # All values + # @return [::Array] values + def values + arr = [] + each_pair { |k, v| arr << v } + arr + end unless method_defined?(:values) + + # Iterates over each key. + # @yield for each key in the map + # @yieldparam key [Object] + # @return [self] + # @!macro map.atomic_method_with_block + def each_key + each_pair { |k, v| yield k } + end unless method_defined?(:each_key) + + # Iterates over each value. + # @yield for each value in the map + # @yieldparam value [Object] + # @return [self] + # @!macro map.atomic_method_with_block + def each_value + each_pair { |k, v| yield v } + end unless method_defined?(:each_value) + + # Iterates over each key value pair. + # @yield for each key value pair in the map + # @yieldparam key [Object] + # @yieldparam value [Object] + # @return [self] + # @!macro map.atomic_method_with_block + def each_pair + return enum_for :each_pair unless block_given? + super + end + + alias_method :each, :each_pair unless method_defined?(:each) + + # Find key of a value. + # @param [Object] value + # @return [Object, nil] key or nil when not found + def key(value) + each_pair { |k, v| return k if v == value } + nil + end unless method_defined?(:key) + + # Is map empty? + # @return [true, false] + def empty? + each_pair { |k, v| return false } + true + end unless method_defined?(:empty?) + + # The size of map. + # @return [Integer] size + def size + count = 0 + each_pair { |k, v| count += 1 } + count + end unless method_defined?(:size) + + # @!visibility private + def marshal_dump + raise TypeError, "can't dump hash with default proc" if @default_proc + h = {} + each_pair { |k, v| h[k] = v } + h + end + + # @!visibility private + def marshal_load(hash) + initialize + populate_from(hash) + end + + undef :freeze + + # @!visibility private + def inspect + format '%s entries=%d default_proc=%s>', to_s[0..-2], size.to_s, @default_proc.inspect + end + + private + + def raise_fetch_no_key + raise KeyError, 'key not found' + end + + def initialize_copy(other) + super + populate_from(other) + end + + def populate_from(hash) + hash.each_pair { |k, v| self[k] = v } + self + end + + def validate_options_hash!(options) + if (initial_capacity = options[:initial_capacity]) && (!initial_capacity.kind_of?(Integer) || initial_capacity < 0) + raise ArgumentError, ":initial_capacity must be a positive Integer" + end + if (load_factor = options[:load_factor]) && (!load_factor.kind_of?(Numeric) || load_factor <= 0 || load_factor > 1) + raise ArgumentError, ":load_factor must be a number between 0 and 1" + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/maybe.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/maybe.rb new file mode 100644 index 0000000..7ba3d3e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/maybe.rb @@ -0,0 +1,229 @@ +require 'concurrent/synchronization' + +module Concurrent + + # A `Maybe` encapsulates an optional value. A `Maybe` either contains a value + # of (represented as `Just`), or it is empty (represented as `Nothing`). Using + # `Maybe` is a good way to deal with errors or exceptional cases without + # resorting to drastic measures such as exceptions. + # + # `Maybe` is a replacement for the use of `nil` with better type checking. + # + # For compatibility with {Concurrent::Concern::Obligation} the predicate and + # accessor methods are aliased as `fulfilled?`, `rejected?`, `value`, and + # `reason`. + # + # ## Motivation + # + # A common pattern in languages with pattern matching, such as Erlang and + # Haskell, is to return *either* a value *or* an error from a function + # Consider this Erlang code: + # + # ```erlang + # case file:consult("data.dat") of + # {ok, Terms} -> do_something_useful(Terms); + # {error, Reason} -> lager:error(Reason) + # end. + # ``` + # + # In this example the standard library function `file:consult` returns a + # [tuple](http://erlang.org/doc/reference_manual/data_types.html#id69044) + # with two elements: an [atom](http://erlang.org/doc/reference_manual/data_types.html#id64134) + # (similar to a ruby symbol) and a variable containing ancillary data. On + # success it returns the atom `ok` and the data from the file. On failure it + # returns `error` and a string with an explanation of the problem. With this + # pattern there is no ambiguity regarding success or failure. If the file is + # empty the return value cannot be misinterpreted as an error. And when an + # error occurs the return value provides useful information. + # + # In Ruby we tend to return `nil` when an error occurs or else we raise an + # exception. Both of these idioms are problematic. Returning `nil` is + # ambiguous because `nil` may also be a valid value. It also lacks + # information pertaining to the nature of the error. Raising an exception + # is both expensive and usurps the normal flow of control. All of these + # problems can be solved with the use of a `Maybe`. + # + # A `Maybe` is unambiguous with regard to whether or not it contains a value. + # When `Just` it contains a value, when `Nothing` it does not. When `Just` + # the value it contains may be `nil`, which is perfectly valid. When + # `Nothing` the reason for the lack of a value is contained as well. The + # previous Erlang example can be duplicated in Ruby in a principled way by + # having functions return `Maybe` objects: + # + # ```ruby + # result = MyFileUtils.consult("data.dat") # returns a Maybe + # if result.just? + # do_something_useful(result.value) # or result.just + # else + # logger.error(result.reason) # or result.nothing + # end + # ``` + # + # @example Returning a Maybe from a Function + # module MyFileUtils + # def self.consult(path) + # file = File.open(path, 'r') + # Concurrent::Maybe.just(file.read) + # rescue => ex + # return Concurrent::Maybe.nothing(ex) + # ensure + # file.close if file + # end + # end + # + # maybe = MyFileUtils.consult('bogus.file') + # maybe.just? #=> false + # maybe.nothing? #=> true + # maybe.reason #=> # + # + # maybe = MyFileUtils.consult('README.md') + # maybe.just? #=> true + # maybe.nothing? #=> false + # maybe.value #=> "# Concurrent Ruby\n[![Gem Version..." + # + # @example Using Maybe with a Block + # result = Concurrent::Maybe.from do + # Client.find(10) # Client is an ActiveRecord model + # end + # + # # -- if the record was found + # result.just? #=> true + # result.value #=> # + # + # # -- if the record was not found + # result.just? #=> false + # result.reason #=> ActiveRecord::RecordNotFound + # + # @example Using Maybe with the Null Object Pattern + # # In a Rails controller... + # result = ClientService.new(10).find # returns a Maybe + # render json: result.or(NullClient.new) + # + # @see https://hackage.haskell.org/package/base-4.2.0.1/docs/Data-Maybe.html Haskell Data.Maybe + # @see https://github.com/purescript/purescript-maybe/blob/master/docs/Data.Maybe.md PureScript Data.Maybe + class Maybe < Synchronization::Object + include Comparable + safe_initialization! + + # Indicates that the given attribute has not been set. + # When `Just` the {#nothing} getter will return `NONE`. + # When `Nothing` the {#just} getter will return `NONE`. + NONE = ::Object.new.freeze + + # The value of a `Maybe` when `Just`. Will be `NONE` when `Nothing`. + attr_reader :just + + # The reason for the `Maybe` when `Nothing`. Will be `NONE` when `Just`. + attr_reader :nothing + + private_class_method :new + + # Create a new `Maybe` using the given block. + # + # Runs the given block passing all function arguments to the block as block + # arguments. If the block runs to completion without raising an exception + # a new `Just` is created with the value set to the return value of the + # block. If the block raises an exception a new `Nothing` is created with + # the reason being set to the raised exception. + # + # @param [Array] args Zero or more arguments to pass to the block. + # @yield The block from which to create a new `Maybe`. + # @yieldparam [Array] args Zero or more block arguments passed as + # arguments to the function. + # + # @return [Maybe] The newly created object. + # + # @raise [ArgumentError] when no block given. + def self.from(*args) + raise ArgumentError.new('no block given') unless block_given? + begin + value = yield(*args) + return new(value, NONE) + rescue => ex + return new(NONE, ex) + end + end + + # Create a new `Just` with the given value. + # + # @param [Object] value The value to set for the new `Maybe` object. + # + # @return [Maybe] The newly created object. + def self.just(value) + return new(value, NONE) + end + + # Create a new `Nothing` with the given (optional) reason. + # + # @param [Exception] error The reason to set for the new `Maybe` object. + # When given a string a new `StandardError` will be created with the + # argument as the message. When no argument is given a new + # `StandardError` with an empty message will be created. + # + # @return [Maybe] The newly created object. + def self.nothing(error = '') + if error.is_a?(Exception) + nothing = error + else + nothing = StandardError.new(error.to_s) + end + return new(NONE, nothing) + end + + # Is this `Maybe` a `Just` (successfully fulfilled with a value)? + # + # @return [Boolean] True if `Just` or false if `Nothing`. + def just? + ! nothing? + end + alias :fulfilled? :just? + + # Is this `Maybe` a `nothing` (rejected with an exception upon fulfillment)? + # + # @return [Boolean] True if `Nothing` or false if `Just`. + def nothing? + @nothing != NONE + end + alias :rejected? :nothing? + + alias :value :just + + alias :reason :nothing + + # Comparison operator. + # + # @return [Integer] 0 if self and other are both `Nothing`; + # -1 if self is `Nothing` and other is `Just`; + # 1 if self is `Just` and other is nothing; + # `self.just <=> other.just` if both self and other are `Just`. + def <=>(other) + if nothing? + other.nothing? ? 0 : -1 + else + other.nothing? ? 1 : just <=> other.just + end + end + + # Return either the value of self or the given default value. + # + # @return [Object] The value of self when `Just`; else the given default. + def or(other) + just? ? just : other + end + + private + + # Create a new `Maybe` with the given attributes. + # + # @param [Object] just The value when `Just` else `NONE`. + # @param [Exception, Object] nothing The exception when `Nothing` else `NONE`. + # + # @return [Maybe] The new `Maybe`. + # + # @!visibility private + def initialize(just, nothing) + @just = just + @nothing = nothing + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/mutable_struct.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/mutable_struct.rb new file mode 100644 index 0000000..55361e7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/mutable_struct.rb @@ -0,0 +1,239 @@ +require 'concurrent/synchronization/abstract_struct' +require 'concurrent/synchronization' + +module Concurrent + + # An thread-safe variation of Ruby's standard `Struct`. Values can be set at + # construction or safely changed at any time during the object's lifecycle. + # + # @see http://ruby-doc.org/core/Struct.html Ruby standard library `Struct` + module MutableStruct + include Synchronization::AbstractStruct + + # @!macro struct_new + # + # Factory for creating new struct classes. + # + # ``` + # new([class_name] [, member_name]+>) -> StructClass click to toggle source + # new([class_name] [, member_name]+>) {|StructClass| block } -> StructClass + # new(value, ...) -> obj + # StructClass[value, ...] -> obj + # ``` + # + # The first two forms are used to create a new struct subclass `class_name` + # that can contain a value for each member_name . This subclass can be + # used to create instances of the structure like any other Class . + # + # If the `class_name` is omitted an anonymous struct class will be created. + # Otherwise, the name of this struct will appear as a constant in the struct class, + # so it must be unique for all structs under this base class and must start with a + # capital letter. Assigning a struct class to a constant also gives the class + # the name of the constant. + # + # If a block is given it will be evaluated in the context of `StructClass`, passing + # the created class as a parameter. This is the recommended way to customize a struct. + # Subclassing an anonymous struct creates an extra anonymous class that will never be used. + # + # The last two forms create a new instance of a struct subclass. The number of value + # parameters must be less than or equal to the number of attributes defined for the + # struct. Unset parameters default to nil. Passing more parameters than number of attributes + # will raise an `ArgumentError`. + # + # @see http://ruby-doc.org/core/Struct.html#method-c-new Ruby standard library `Struct#new` + + # @!macro struct_values + # + # Returns the values for this struct as an Array. + # + # @return [Array] the values for this struct + # + def values + synchronize { ns_values } + end + alias_method :to_a, :values + + # @!macro struct_values_at + # + # Returns the struct member values for each selector as an Array. + # + # A selector may be either an Integer offset or a Range of offsets (as in `Array#values_at`). + # + # @param [Fixnum, Range] indexes the index(es) from which to obatin the values (in order) + def values_at(*indexes) + synchronize { ns_values_at(indexes) } + end + + # @!macro struct_inspect + # + # Describe the contents of this struct in a string. + # + # @return [String] the contents of this struct in a string + def inspect + synchronize { ns_inspect } + end + alias_method :to_s, :inspect + + # @!macro struct_merge + # + # Returns a new struct containing the contents of `other` and the contents + # of `self`. If no block is specified, the value for entries with duplicate + # keys will be that of `other`. Otherwise the value for each duplicate key + # is determined by calling the block with the key, its value in `self` and + # its value in `other`. + # + # @param [Hash] other the hash from which to set the new values + # @yield an options block for resolving duplicate keys + # @yieldparam [String, Symbol] member the name of the member which is duplicated + # @yieldparam [Object] selfvalue the value of the member in `self` + # @yieldparam [Object] othervalue the value of the member in `other` + # + # @return [Synchronization::AbstractStruct] a new struct with the new values + # + # @raise [ArgumentError] of given a member that is not defined in the struct + def merge(other, &block) + synchronize { ns_merge(other, &block) } + end + + # @!macro struct_to_h + # + # Returns a hash containing the names and values for the struct’s members. + # + # @return [Hash] the names and values for the struct’s members + def to_h + synchronize { ns_to_h } + end + + # @!macro struct_get + # + # Attribute Reference + # + # @param [Symbol, String, Integer] member the string or symbol name of the member + # for which to obtain the value or the member's index + # + # @return [Object] the value of the given struct member or the member at the given index. + # + # @raise [NameError] if the member does not exist + # @raise [IndexError] if the index is out of range. + def [](member) + synchronize { ns_get(member) } + end + + # @!macro struct_equality + # + # Equality + # + # @return [Boolean] true if other has the same struct subclass and has + # equal member values (according to `Object#==`) + def ==(other) + synchronize { ns_equality(other) } + end + + # @!macro struct_each + # + # Yields the value of each struct member in order. If no block is given + # an enumerator is returned. + # + # @yield the operation to be performed on each struct member + # @yieldparam [Object] value each struct value (in order) + def each(&block) + return enum_for(:each) unless block_given? + synchronize { ns_each(&block) } + end + + # @!macro struct_each_pair + # + # Yields the name and value of each struct member in order. If no block is + # given an enumerator is returned. + # + # @yield the operation to be performed on each struct member/value pair + # @yieldparam [Object] member each struct member (in order) + # @yieldparam [Object] value each struct value (in order) + def each_pair(&block) + return enum_for(:each_pair) unless block_given? + synchronize { ns_each_pair(&block) } + end + + # @!macro struct_select + # + # Yields each member value from the struct to the block and returns an Array + # containing the member values from the struct for which the given block + # returns a true value (equivalent to `Enumerable#select`). + # + # @yield the operation to be performed on each struct member + # @yieldparam [Object] value each struct value (in order) + # + # @return [Array] an array containing each value for which the block returns true + def select(&block) + return enum_for(:select) unless block_given? + synchronize { ns_select(&block) } + end + + # @!macro struct_set + # + # Attribute Assignment + # + # Sets the value of the given struct member or the member at the given index. + # + # @param [Symbol, String, Integer] member the string or symbol name of the member + # for which to obtain the value or the member's index + # + # @return [Object] the value of the given struct member or the member at the given index. + # + # @raise [NameError] if the name does not exist + # @raise [IndexError] if the index is out of range. + def []=(member, value) + if member.is_a? Integer + length = synchronize { @values.length } + if member >= length + raise IndexError.new("offset #{member} too large for struct(size:#{length})") + end + synchronize { @values[member] = value } + else + send("#{member}=", value) + end + rescue NoMethodError + raise NameError.new("no member '#{member}' in struct") + end + + private + + # @!visibility private + def initialize_copy(original) + synchronize do + super(original) + ns_initialize_copy + end + end + + # @!macro struct_new + def self.new(*args, &block) + clazz_name = nil + if args.length == 0 + raise ArgumentError.new('wrong number of arguments (0 for 1+)') + elsif args.length > 0 && args.first.is_a?(String) + clazz_name = args.shift + end + FACTORY.define_struct(clazz_name, args, &block) + end + + FACTORY = Class.new(Synchronization::LockableObject) do + def define_struct(name, members, &block) + synchronize do + clazz = Synchronization::AbstractStruct.define_struct_class(MutableStruct, Synchronization::LockableObject, name, members, &block) + members.each_with_index do |member, index| + clazz.send :remove_method, member + clazz.send(:define_method, member) do + synchronize { @values[index] } + end + clazz.send(:define_method, "#{member}=") do |value| + synchronize { @values[index] = value } + end + end + clazz + end + end + end.new + private_constant :FACTORY + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/mvar.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/mvar.rb new file mode 100644 index 0000000..9034711 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/mvar.rb @@ -0,0 +1,242 @@ +require 'concurrent/concern/dereferenceable' +require 'concurrent/synchronization' + +module Concurrent + + # An `MVar` is a synchronized single element container. They are empty or + # contain one item. Taking a value from an empty `MVar` blocks, as does + # putting a value into a full one. You can either think of them as blocking + # queue of length one, or a special kind of mutable variable. + # + # On top of the fundamental `#put` and `#take` operations, we also provide a + # `#mutate` that is atomic with respect to operations on the same instance. + # These operations all support timeouts. + # + # We also support non-blocking operations `#try_put!` and `#try_take!`, a + # `#set!` that ignores existing values, a `#value` that returns the value + # without removing it or returns `MVar::EMPTY`, and a `#modify!` that yields + # `MVar::EMPTY` if the `MVar` is empty and can be used to set `MVar::EMPTY`. + # You shouldn't use these operations in the first instance. + # + # `MVar` is a [Dereferenceable](Dereferenceable). + # + # `MVar` is related to M-structures in Id, `MVar` in Haskell and `SyncVar` in Scala. + # + # Note that unlike the original Haskell paper, our `#take` is blocking. This is how + # Haskell and Scala do it today. + # + # @!macro copy_options + # + # ## See Also + # + # 1. P. Barth, R. Nikhil, and Arvind. [M-Structures: Extending a parallel, non- strict, functional language with state](http://dl.acm.org/citation.cfm?id=652538). In Proceedings of the 5th + # ACM Conference on Functional Programming Languages and Computer Architecture (FPCA), 1991. + # + # 2. S. Peyton Jones, A. Gordon, and S. Finne. [Concurrent Haskell](http://dl.acm.org/citation.cfm?id=237794). + # In Proceedings of the 23rd Symposium on Principles of Programming Languages + # (PoPL), 1996. + class MVar < Synchronization::Object + include Concern::Dereferenceable + safe_initialization! + + # Unique value that represents that an `MVar` was empty + EMPTY = ::Object.new + + # Unique value that represents that an `MVar` timed out before it was able + # to produce a value. + TIMEOUT = ::Object.new + + # Create a new `MVar`, either empty or with an initial value. + # + # @param [Hash] opts the options controlling how the future will be processed + # + # @!macro deref_options + def initialize(value = EMPTY, opts = {}) + @value = value + @mutex = Mutex.new + @empty_condition = ConditionVariable.new + @full_condition = ConditionVariable.new + set_deref_options(opts) + end + + # Remove the value from an `MVar`, leaving it empty, and blocking if there + # isn't a value. A timeout can be set to limit the time spent blocked, in + # which case it returns `TIMEOUT` if the time is exceeded. + # @return [Object] the value that was taken, or `TIMEOUT` + def take(timeout = nil) + @mutex.synchronize do + wait_for_full(timeout) + + # If we timed out we'll still be empty + if unlocked_full? + value = @value + @value = EMPTY + @empty_condition.signal + apply_deref_options(value) + else + TIMEOUT + end + end + end + + # acquires lock on the from an `MVAR`, yields the value to provided block, + # and release lock. A timeout can be set to limit the time spent blocked, + # in which case it returns `TIMEOUT` if the time is exceeded. + # @return [Object] the value returned by the block, or `TIMEOUT` + def borrow(timeout = nil) + @mutex.synchronize do + wait_for_full(timeout) + + # if we timeoud out we'll still be empty + if unlocked_full? + yield @value + else + TIMEOUT + end + end + end + + # Put a value into an `MVar`, blocking if there is already a value until + # it is empty. A timeout can be set to limit the time spent blocked, in + # which case it returns `TIMEOUT` if the time is exceeded. + # @return [Object] the value that was put, or `TIMEOUT` + def put(value, timeout = nil) + @mutex.synchronize do + wait_for_empty(timeout) + + # If we timed out we won't be empty + if unlocked_empty? + @value = value + @full_condition.signal + apply_deref_options(value) + else + TIMEOUT + end + end + end + + # Atomically `take`, yield the value to a block for transformation, and then + # `put` the transformed value. Returns the transformed value. A timeout can + # be set to limit the time spent blocked, in which case it returns `TIMEOUT` + # if the time is exceeded. + # @return [Object] the transformed value, or `TIMEOUT` + def modify(timeout = nil) + raise ArgumentError.new('no block given') unless block_given? + + @mutex.synchronize do + wait_for_full(timeout) + + # If we timed out we'll still be empty + if unlocked_full? + value = @value + @value = yield value + @full_condition.signal + apply_deref_options(value) + else + TIMEOUT + end + end + end + + # Non-blocking version of `take`, that returns `EMPTY` instead of blocking. + def try_take! + @mutex.synchronize do + if unlocked_full? + value = @value + @value = EMPTY + @empty_condition.signal + apply_deref_options(value) + else + EMPTY + end + end + end + + # Non-blocking version of `put`, that returns whether or not it was successful. + def try_put!(value) + @mutex.synchronize do + if unlocked_empty? + @value = value + @full_condition.signal + true + else + false + end + end + end + + # Non-blocking version of `put` that will overwrite an existing value. + def set!(value) + @mutex.synchronize do + old_value = @value + @value = value + @full_condition.signal + apply_deref_options(old_value) + end + end + + # Non-blocking version of `modify` that will yield with `EMPTY` if there is no value yet. + def modify! + raise ArgumentError.new('no block given') unless block_given? + + @mutex.synchronize do + value = @value + @value = yield value + if unlocked_empty? + @empty_condition.signal + else + @full_condition.signal + end + apply_deref_options(value) + end + end + + # Returns if the `MVar` is currently empty. + def empty? + @mutex.synchronize { @value == EMPTY } + end + + # Returns if the `MVar` currently contains a value. + def full? + !empty? + end + + protected + + def synchronize(&block) + @mutex.synchronize(&block) + end + + private + + def unlocked_empty? + @value == EMPTY + end + + def unlocked_full? + ! unlocked_empty? + end + + def wait_for_full(timeout) + wait_while(@full_condition, timeout) { unlocked_empty? } + end + + def wait_for_empty(timeout) + wait_while(@empty_condition, timeout) { unlocked_full? } + end + + def wait_while(condition, timeout) + if timeout.nil? + while yield + condition.wait(@mutex) + end + else + stop = Concurrent.monotonic_time + timeout + while yield && timeout > 0.0 + condition.wait(@mutex, timeout) + timeout = stop - Concurrent.monotonic_time + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/options.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/options.rb new file mode 100644 index 0000000..bdd22a9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/options.rb @@ -0,0 +1,42 @@ +require 'concurrent/configuration' + +module Concurrent + + # @!visibility private + module Options + + # Get the requested `Executor` based on the values set in the options hash. + # + # @param [Hash] opts the options defining the requested executor + # @option opts [Executor] :executor when set use the given `Executor` instance. + # Three special values are also supported: `:fast` returns the global fast executor, + # `:io` returns the global io executor, and `:immediate` returns a new + # `ImmediateExecutor` object. + # + # @return [Executor, nil] the requested thread pool, or nil when no option specified + # + # @!visibility private + def self.executor_from_options(opts = {}) # :nodoc: + if identifier = opts.fetch(:executor, nil) + executor(identifier) + else + nil + end + end + + def self.executor(executor_identifier) + case executor_identifier + when :fast + Concurrent.global_fast_executor + when :io + Concurrent.global_io_executor + when :immediate + Concurrent.global_immediate_executor + when Concurrent::ExecutorService + executor_identifier + else + raise ArgumentError, "executor not recognized by '#{executor_identifier}'" + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/promise.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/promise.rb new file mode 100644 index 0000000..e917791 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/promise.rb @@ -0,0 +1,580 @@ +require 'thread' +require 'concurrent/constants' +require 'concurrent/errors' +require 'concurrent/ivar' +require 'concurrent/executor/safe_task_executor' + +require 'concurrent/options' + +module Concurrent + + PromiseExecutionError = Class.new(StandardError) + + # Promises are inspired by the JavaScript [Promises/A](http://wiki.commonjs.org/wiki/Promises/A) + # and [Promises/A+](http://promises-aplus.github.io/promises-spec/) specifications. + # + # > A promise represents the eventual value returned from the single + # > completion of an operation. + # + # Promises are similar to futures and share many of the same behaviours. + # Promises are far more robust, however. Promises can be chained in a tree + # structure where each promise may have zero or more children. Promises are + # chained using the `then` method. The result of a call to `then` is always + # another promise. Promises are resolved asynchronously (with respect to the + # main thread) but in a strict order: parents are guaranteed to be resolved + # before their children, children before their younger siblings. The `then` + # method takes two parameters: an optional block to be executed upon parent + # resolution and an optional callable to be executed upon parent failure. The + # result of each promise is passed to each of its children upon resolution. + # When a promise is rejected all its children will be summarily rejected and + # will receive the reason. + # + # Promises have several possible states: *:unscheduled*, *:pending*, + # *:processing*, *:rejected*, or *:fulfilled*. These are also aggregated as + # `#incomplete?` and `#complete?`. When a Promise is created it is set to + # *:unscheduled*. Once the `#execute` method is called the state becomes + # *:pending*. Once a job is pulled from the thread pool's queue and is given + # to a thread for processing (often immediately upon `#post`) the state + # becomes *:processing*. The future will remain in this state until processing + # is complete. A future that is in the *:unscheduled*, *:pending*, or + # *:processing* is considered `#incomplete?`. A `#complete?` Promise is either + # *:rejected*, indicating that an exception was thrown during processing, or + # *:fulfilled*, indicating success. If a Promise is *:fulfilled* its `#value` + # will be updated to reflect the result of the operation. If *:rejected* the + # `reason` will be updated with a reference to the thrown exception. The + # predicate methods `#unscheduled?`, `#pending?`, `#rejected?`, and + # `#fulfilled?` can be called at any time to obtain the state of the Promise, + # as can the `#state` method, which returns a symbol. + # + # Retrieving the value of a promise is done through the `value` (alias: + # `deref`) method. Obtaining the value of a promise is a potentially blocking + # operation. When a promise is *rejected* a call to `value` will return `nil` + # immediately. When a promise is *fulfilled* a call to `value` will + # immediately return the current value. When a promise is *pending* a call to + # `value` will block until the promise is either *rejected* or *fulfilled*. A + # *timeout* value can be passed to `value` to limit how long the call will + # block. If `nil` the call will block indefinitely. If `0` the call will not + # block. Any other integer or float value will indicate the maximum number of + # seconds to block. + # + # Promises run on the global thread pool. + # + # @!macro copy_options + # + # ### Examples + # + # Start by requiring promises + # + # ```ruby + # require 'concurrent' + # ``` + # + # Then create one + # + # ```ruby + # p = Concurrent::Promise.execute do + # # do something + # 42 + # end + # ``` + # + # Promises can be chained using the `then` method. The `then` method accepts a + # block and an executor, to be executed on fulfillment, and a callable argument to be executed + # on rejection. The result of the each promise is passed as the block argument + # to chained promises. + # + # ```ruby + # p = Concurrent::Promise.new{10}.then{|x| x * 2}.then{|result| result - 10 }.execute + # ``` + # + # And so on, and so on, and so on... + # + # ```ruby + # p = Concurrent::Promise.fulfill(20). + # then{|result| result - 10 }. + # then{|result| result * 3 }. + # then(executor: different_executor){|result| result % 5 }.execute + # ``` + # + # The initial state of a newly created Promise depends on the state of its parent: + # - if parent is *unscheduled* the child will be *unscheduled* + # - if parent is *pending* the child will be *pending* + # - if parent is *fulfilled* the child will be *pending* + # - if parent is *rejected* the child will be *pending* (but will ultimately be *rejected*) + # + # Promises are executed asynchronously from the main thread. By the time a + # child Promise finishes intialization it may be in a different state than its + # parent (by the time a child is created its parent may have completed + # execution and changed state). Despite being asynchronous, however, the order + # of execution of Promise objects in a chain (or tree) is strictly defined. + # + # There are multiple ways to create and execute a new `Promise`. Both ways + # provide identical behavior: + # + # ```ruby + # # create, operate, then execute + # p1 = Concurrent::Promise.new{ "Hello World!" } + # p1.state #=> :unscheduled + # p1.execute + # + # # create and immediately execute + # p2 = Concurrent::Promise.new{ "Hello World!" }.execute + # + # # execute during creation + # p3 = Concurrent::Promise.execute{ "Hello World!" } + # ``` + # + # Once the `execute` method is called a `Promise` becomes `pending`: + # + # ```ruby + # p = Concurrent::Promise.execute{ "Hello, world!" } + # p.state #=> :pending + # p.pending? #=> true + # ``` + # + # Wait a little bit, and the promise will resolve and provide a value: + # + # ```ruby + # p = Concurrent::Promise.execute{ "Hello, world!" } + # sleep(0.1) + # + # p.state #=> :fulfilled + # p.fulfilled? #=> true + # p.value #=> "Hello, world!" + # ``` + # + # If an exception occurs, the promise will be rejected and will provide + # a reason for the rejection: + # + # ```ruby + # p = Concurrent::Promise.execute{ raise StandardError.new("Here comes the Boom!") } + # sleep(0.1) + # + # p.state #=> :rejected + # p.rejected? #=> true + # p.reason #=> "#" + # ``` + # + # #### Rejection + # + # When a promise is rejected all its children will be rejected and will + # receive the rejection `reason` as the rejection callable parameter: + # + # ```ruby + # p = Concurrent::Promise.execute { Thread.pass; raise StandardError } + # + # c1 = p.then(-> reason { 42 }) + # c2 = p.then(-> reason { raise 'Boom!' }) + # + # c1.wait.state #=> :fulfilled + # c1.value #=> 45 + # c2.wait.state #=> :rejected + # c2.reason #=> # + # ``` + # + # Once a promise is rejected it will continue to accept children that will + # receive immediately rejection (they will be executed asynchronously). + # + # #### Aliases + # + # The `then` method is the most generic alias: it accepts a block to be + # executed upon parent fulfillment and a callable to be executed upon parent + # rejection. At least one of them should be passed. The default block is `{ + # |result| result }` that fulfills the child with the parent value. The + # default callable is `{ |reason| raise reason }` that rejects the child with + # the parent reason. + # + # - `on_success { |result| ... }` is the same as `then {|result| ... }` + # - `rescue { |reason| ... }` is the same as `then(Proc.new { |reason| ... } )` + # - `rescue` is aliased by `catch` and `on_error` + class Promise < IVar + + # Initialize a new Promise with the provided options. + # + # @!macro executor_and_deref_options + # + # @!macro promise_init_options + # + # @option opts [Promise] :parent the parent `Promise` when building a chain/tree + # @option opts [Proc] :on_fulfill fulfillment handler + # @option opts [Proc] :on_reject rejection handler + # @option opts [object, Array] :args zero or more arguments to be passed + # the task block on execution + # + # @yield The block operation to be performed asynchronously. + # + # @raise [ArgumentError] if no block is given + # + # @see http://wiki.commonjs.org/wiki/Promises/A + # @see http://promises-aplus.github.io/promises-spec/ + def initialize(opts = {}, &block) + opts.delete_if { |k, v| v.nil? } + super(NULL, opts.merge(__promise_body_from_block__: block), &nil) + end + + # Create a new `Promise` and fulfill it immediately. + # + # @!macro executor_and_deref_options + # + # @!macro promise_init_options + # + # @raise [ArgumentError] if no block is given + # + # @return [Promise] the newly created `Promise` + def self.fulfill(value, opts = {}) + Promise.new(opts).tap { |p| p.send(:synchronized_set_state!, true, value, nil) } + end + + # Create a new `Promise` and reject it immediately. + # + # @!macro executor_and_deref_options + # + # @!macro promise_init_options + # + # @raise [ArgumentError] if no block is given + # + # @return [Promise] the newly created `Promise` + def self.reject(reason, opts = {}) + Promise.new(opts).tap { |p| p.send(:synchronized_set_state!, false, nil, reason) } + end + + # Execute an `:unscheduled` `Promise`. Immediately sets the state to `:pending` and + # passes the block to a new thread/thread pool for eventual execution. + # Does nothing if the `Promise` is in any state other than `:unscheduled`. + # + # @return [Promise] a reference to `self` + def execute + if root? + if compare_and_set_state(:pending, :unscheduled) + set_pending + realize(@promise_body) + end + else + compare_and_set_state(:pending, :unscheduled) + @parent.execute + end + self + end + + # @!macro ivar_set_method + # + # @raise [Concurrent::PromiseExecutionError] if not the root promise + def set(value = NULL, &block) + raise PromiseExecutionError.new('supported only on root promise') unless root? + check_for_block_or_value!(block_given?, value) + synchronize do + if @state != :unscheduled + raise MultipleAssignmentError + else + @promise_body = block || Proc.new { |result| value } + end + end + execute + end + + # @!macro ivar_fail_method + # + # @raise [Concurrent::PromiseExecutionError] if not the root promise + def fail(reason = StandardError.new) + set { raise reason } + end + + # Create a new `Promise` object with the given block, execute it, and return the + # `:pending` object. + # + # @!macro executor_and_deref_options + # + # @!macro promise_init_options + # + # @return [Promise] the newly created `Promise` in the `:pending` state + # + # @raise [ArgumentError] if no block is given + # + # @example + # promise = Concurrent::Promise.execute{ sleep(1); 42 } + # promise.state #=> :pending + def self.execute(opts = {}, &block) + new(opts, &block).execute + end + + # Chain a new promise off the current promise. + # + # @return [Promise] the new promise + # @yield The block operation to be performed asynchronously. + # @overload then(rescuer, executor, &block) + # @param [Proc] rescuer An optional rescue block to be executed if the + # promise is rejected. + # @param [ThreadPool] executor An optional thread pool executor to be used + # in the new Promise + # @overload then(rescuer, executor: executor, &block) + # @param [Proc] rescuer An optional rescue block to be executed if the + # promise is rejected. + # @param [ThreadPool] executor An optional thread pool executor to be used + # in the new Promise + def then(*args, &block) + if args.last.is_a?(::Hash) + executor = args.pop[:executor] + rescuer = args.first + else + rescuer, executor = args + end + + executor ||= @executor + + raise ArgumentError.new('rescuers and block are both missing') if rescuer.nil? && !block_given? + block = Proc.new { |result| result } unless block_given? + child = Promise.new( + parent: self, + executor: executor, + on_fulfill: block, + on_reject: rescuer + ) + + synchronize do + child.state = :pending if @state == :pending + child.on_fulfill(apply_deref_options(@value)) if @state == :fulfilled + child.on_reject(@reason) if @state == :rejected + @children << child + end + + child + end + + # Chain onto this promise an action to be undertaken on success + # (fulfillment). + # + # @yield The block to execute + # + # @return [Promise] self + def on_success(&block) + raise ArgumentError.new('no block given') unless block_given? + self.then(&block) + end + + # Chain onto this promise an action to be undertaken on failure + # (rejection). + # + # @yield The block to execute + # + # @return [Promise] self + def rescue(&block) + self.then(block) + end + + alias_method :catch, :rescue + alias_method :on_error, :rescue + + # Yield the successful result to the block that returns a promise. If that + # promise is also successful the result is the result of the yielded promise. + # If either part fails the whole also fails. + # + # @example + # Promise.execute { 1 }.flat_map { |v| Promise.execute { v + 2 } }.value! #=> 3 + # + # @return [Promise] + def flat_map(&block) + child = Promise.new( + parent: self, + executor: ImmediateExecutor.new, + ) + + on_error { |e| child.on_reject(e) } + on_success do |result1| + begin + inner = block.call(result1) + inner.execute + inner.on_success { |result2| child.on_fulfill(result2) } + inner.on_error { |e| child.on_reject(e) } + rescue => e + child.on_reject(e) + end + end + + child + end + + # Builds a promise that produces the result of promises in an Array + # and fails if any of them fails. + # + # @overload zip(*promises) + # @param [Array] promises + # + # @overload zip(*promises, opts) + # @param [Array] promises + # @param [Hash] opts the configuration options + # @option opts [Executor] :executor (ImmediateExecutor.new) when set use the given `Executor` instance. + # @option opts [Boolean] :execute (true) execute promise before returning + # + # @return [Promise] + def self.zip(*promises) + opts = promises.last.is_a?(::Hash) ? promises.pop.dup : {} + opts[:executor] ||= ImmediateExecutor.new + zero = if !opts.key?(:execute) || opts.delete(:execute) + fulfill([], opts) + else + Promise.new(opts) { [] } + end + + promises.reduce(zero) do |p1, p2| + p1.flat_map do |results| + p2.then do |next_result| + results << next_result + end + end + end + end + + # Builds a promise that produces the result of self and others in an Array + # and fails if any of them fails. + # + # @overload zip(*promises) + # @param [Array] others + # + # @overload zip(*promises, opts) + # @param [Array] others + # @param [Hash] opts the configuration options + # @option opts [Executor] :executor (ImmediateExecutor.new) when set use the given `Executor` instance. + # @option opts [Boolean] :execute (true) execute promise before returning + # + # @return [Promise] + def zip(*others) + self.class.zip(self, *others) + end + + # Aggregates a collection of promises and executes the `then` condition + # if all aggregated promises succeed. Executes the `rescue` handler with + # a `Concurrent::PromiseExecutionError` if any of the aggregated promises + # fail. Upon execution will execute any of the aggregate promises that + # were not already executed. + # + # @!macro promise_self_aggregate + # + # The returned promise will not yet have been executed. Additional `#then` + # and `#rescue` handlers may still be provided. Once the returned promise + # is execute the aggregate promises will be also be executed (if they have + # not been executed already). The results of the aggregate promises will + # be checked upon completion. The necessary `#then` and `#rescue` blocks + # on the aggregating promise will then be executed as appropriate. If the + # `#rescue` handlers are executed the raises exception will be + # `Concurrent::PromiseExecutionError`. + # + # @param [Array] promises Zero or more promises to aggregate + # @return [Promise] an unscheduled (not executed) promise that aggregates + # the promises given as arguments + def self.all?(*promises) + aggregate(:all?, *promises) + end + + # Aggregates a collection of promises and executes the `then` condition + # if any aggregated promises succeed. Executes the `rescue` handler with + # a `Concurrent::PromiseExecutionError` if any of the aggregated promises + # fail. Upon execution will execute any of the aggregate promises that + # were not already executed. + # + # @!macro promise_self_aggregate + def self.any?(*promises) + aggregate(:any?, *promises) + end + + protected + + def ns_initialize(value, opts) + super + + @executor = Options.executor_from_options(opts) || Concurrent.global_io_executor + @args = get_arguments_from(opts) + + @parent = opts.fetch(:parent) { nil } + @on_fulfill = opts.fetch(:on_fulfill) { Proc.new { |result| result } } + @on_reject = opts.fetch(:on_reject) { Proc.new { |reason| raise reason } } + + @promise_body = opts[:__promise_body_from_block__] || Proc.new { |result| result } + @state = :unscheduled + @children = [] + end + + # Aggregate a collection of zero or more promises under a composite promise, + # execute the aggregated promises and collect them into a standard Ruby array, + # call the given Ruby `Ennnumerable` predicate (such as `any?`, `all?`, `none?`, + # or `one?`) on the collection checking for the success or failure of each, + # then executing the composite's `#then` handlers if the predicate returns + # `true` or executing the composite's `#rescue` handlers if the predicate + # returns false. + # + # @!macro promise_self_aggregate + def self.aggregate(method, *promises) + composite = Promise.new do + completed = promises.collect do |promise| + promise.execute if promise.unscheduled? + promise.wait + promise + end + unless completed.empty? || completed.send(method){|promise| promise.fulfilled? } + raise PromiseExecutionError + end + end + composite + end + + # @!visibility private + def set_pending + synchronize do + @state = :pending + @children.each { |c| c.set_pending } + end + end + + # @!visibility private + def root? # :nodoc: + @parent.nil? + end + + # @!visibility private + def on_fulfill(result) + realize Proc.new { @on_fulfill.call(result) } + nil + end + + # @!visibility private + def on_reject(reason) + realize Proc.new { @on_reject.call(reason) } + nil + end + + # @!visibility private + def notify_child(child) + if_state(:fulfilled) { child.on_fulfill(apply_deref_options(@value)) } + if_state(:rejected) { child.on_reject(@reason) } + end + + # @!visibility private + def complete(success, value, reason) + children_to_notify = synchronize do + set_state!(success, value, reason) + @children.dup + end + + children_to_notify.each { |child| notify_child(child) } + observers.notify_and_delete_observers{ [Time.now, self.value, reason] } + end + + # @!visibility private + def realize(task) + @executor.post do + success, value, reason = SafeTaskExecutor.new(task, rescue_exception: true).execute(*@args) + complete(success, value, reason) + end + end + + # @!visibility private + def set_state!(success, value, reason) + set_state(success, value, reason) + event.set + end + + # @!visibility private + def synchronized_set_state!(success, value, reason) + synchronize { set_state!(success, value, reason) } + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/promises.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/promises.rb new file mode 100644 index 0000000..76af4d5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/promises.rb @@ -0,0 +1,2167 @@ +require 'concurrent/synchronization' +require 'concurrent/atomic/atomic_boolean' +require 'concurrent/atomic/atomic_fixnum' +require 'concurrent/collection/lock_free_stack' +require 'concurrent/errors' +require 'concurrent/re_include' + +module Concurrent + + # {include:file:docs-source/promises-main.md} + module Promises + + # @!macro promises.param.default_executor + # @param [Executor, :io, :fast] default_executor Instance of an executor or a name of the + # global executor. Default executor propagates to chained futures unless overridden with + # executor parameter or changed with {AbstractEventFuture#with_default_executor}. + # + # @!macro promises.param.executor + # @param [Executor, :io, :fast] executor Instance of an executor or a name of the + # global executor. The task is executed on it, default executor remains unchanged. + # + # @!macro promises.param.args + # @param [Object] args arguments which are passed to the task when it's executed. + # (It might be prepended with other arguments, see the @yeild section). + # + # @!macro promises.shortcut.on + # Shortcut of {#$0_on} with default `:io` executor supplied. + # @see #$0_on + # + # @!macro promises.shortcut.using + # Shortcut of {#$0_using} with default `:io` executor supplied. + # @see #$0_using + # + # @!macro promise.param.task-future + # @yieldreturn will become result of the returned Future. + # Its returned value becomes {Future#value} fulfilling it, + # raised exception becomes {Future#reason} rejecting it. + # + # @!macro promise.param.callback + # @yieldreturn is forgotten. + + # Container of all {Future}, {Event} factory methods. They are never constructed directly with + # new. + module FactoryMethods + extend ReInclude + extend self + + module Configuration + # @return [Executor, :io, :fast] the executor which is used when none is supplied + # to a factory method. The method can be overridden in the receivers of + # `include FactoryMethod` + def default_executor + :io + end + end + + include Configuration + + # @!macro promises.shortcut.on + # @return [ResolvableEvent] + def resolvable_event + resolvable_event_on default_executor + end + + # Created resolvable event, user is responsible for resolving the event once by + # {Promises::ResolvableEvent#resolve}. + # + # @!macro promises.param.default_executor + # @return [ResolvableEvent] + def resolvable_event_on(default_executor = self.default_executor) + ResolvableEventPromise.new(default_executor).future + end + + # @!macro promises.shortcut.on + # @return [ResolvableFuture] + def resolvable_future + resolvable_future_on default_executor + end + + # Creates resolvable future, user is responsible for resolving the future once by + # {Promises::ResolvableFuture#resolve}, {Promises::ResolvableFuture#fulfill}, + # or {Promises::ResolvableFuture#reject} + # + # @!macro promises.param.default_executor + # @return [ResolvableFuture] + def resolvable_future_on(default_executor = self.default_executor) + ResolvableFuturePromise.new(default_executor).future + end + + # @!macro promises.shortcut.on + # @return [Future] + def future(*args, &task) + future_on(default_executor, *args, &task) + end + + # Constructs new Future which will be resolved after block is evaluated on default executor. + # Evaluation begins immediately. + # + # @!macro promises.param.default_executor + # @!macro promises.param.args + # @yield [*args] to the task. + # @!macro promise.param.task-future + # @return [Future] + def future_on(default_executor, *args, &task) + ImmediateEventPromise.new(default_executor).future.then(*args, &task) + end + + # Creates resolved future with will be either fulfilled with the given value or rejection with + # the given reason. + # + # @param [true, false] fulfilled + # @param [Object] value + # @param [Object] reason + # @!macro promises.param.default_executor + # @return [Future] + def resolved_future(fulfilled, value, reason, default_executor = self.default_executor) + ImmediateFuturePromise.new(default_executor, fulfilled, value, reason).future + end + + # Creates resolved future with will be fulfilled with the given value. + # + # @!macro promises.param.default_executor + # @param [Object] value + # @return [Future] + def fulfilled_future(value, default_executor = self.default_executor) + resolved_future true, value, nil, default_executor + end + + # Creates resolved future with will be rejected with the given reason. + # + # @!macro promises.param.default_executor + # @param [Object] reason + # @return [Future] + def rejected_future(reason, default_executor = self.default_executor) + resolved_future false, nil, reason, default_executor + end + + # Creates resolved event. + # + # @!macro promises.param.default_executor + # @return [Event] + def resolved_event(default_executor = self.default_executor) + ImmediateEventPromise.new(default_executor).event + end + + # General constructor. Behaves differently based on the argument's type. It's provided for convenience + # but it's better to be explicit. + # + # @see rejected_future, resolved_event, fulfilled_future + # @!macro promises.param.default_executor + # @return [Event, Future] + # + # @overload make_future(nil, default_executor = self.default_executor) + # @param [nil] nil + # @return [Event] resolved event. + # + # @overload make_future(a_future, default_executor = self.default_executor) + # @param [Future] a_future + # @return [Future] a future which will be resolved when a_future is. + # + # @overload make_future(an_event, default_executor = self.default_executor) + # @param [Event] an_event + # @return [Event] an event which will be resolved when an_event is. + # + # @overload make_future(exception, default_executor = self.default_executor) + # @param [Exception] exception + # @return [Future] a rejected future with the exception as its reason. + # + # @overload make_future(value, default_executor = self.default_executor) + # @param [Object] value when none of the above overloads fits + # @return [Future] a fulfilled future with the value. + def make_future(argument = nil, default_executor = self.default_executor) + case argument + when AbstractEventFuture + # returning wrapper would change nothing + argument + when Exception + rejected_future argument, default_executor + when nil + resolved_event default_executor + else + fulfilled_future argument, default_executor + end + end + + # @!macro promises.shortcut.on + # @return [Future, Event] + def delay(*args, &task) + delay_on default_executor, *args, &task + end + + # Creates new event or future which is resolved only after it is touched, + # see {Concurrent::AbstractEventFuture#touch}. + # + # @!macro promises.param.default_executor + # @overload delay_on(default_executor, *args, &task) + # If task is provided it returns a {Future} representing the result of the task. + # @!macro promises.param.args + # @yield [*args] to the task. + # @!macro promise.param.task-future + # @return [Future] + # @overload delay_on(default_executor) + # If no task is provided, it returns an {Event} + # @return [Event] + def delay_on(default_executor, *args, &task) + event = DelayPromise.new(default_executor).event + task ? event.chain(*args, &task) : event + end + + # @!macro promises.shortcut.on + # @return [Future, Event] + def schedule(intended_time, *args, &task) + schedule_on default_executor, intended_time, *args, &task + end + + # Creates new event or future which is resolved in intended_time. + # + # @!macro promises.param.default_executor + # @!macro promises.param.intended_time + # @param [Numeric, Time] intended_time `Numeric` means to run in `intended_time` seconds. + # `Time` means to run on `intended_time`. + # @overload schedule_on(default_executor, intended_time, *args, &task) + # If task is provided it returns a {Future} representing the result of the task. + # @!macro promises.param.args + # @yield [*args] to the task. + # @!macro promise.param.task-future + # @return [Future] + # @overload schedule_on(default_executor, intended_time) + # If no task is provided, it returns an {Event} + # @return [Event] + def schedule_on(default_executor, intended_time, *args, &task) + event = ScheduledPromise.new(default_executor, intended_time).event + task ? event.chain(*args, &task) : event + end + + # @!macro promises.shortcut.on + # @return [Future] + def zip_futures(*futures_and_or_events) + zip_futures_on default_executor, *futures_and_or_events + end + + # Creates new future which is resolved after all futures_and_or_events are resolved. + # Its value is array of zipped future values. Its reason is array of reasons for rejection. + # If there is an error it rejects. + # @!macro promises.event-conversion + # If event is supplied, which does not have value and can be only resolved, it's + # represented as `:fulfilled` with value `nil`. + # + # @!macro promises.param.default_executor + # @param [AbstractEventFuture] futures_and_or_events + # @return [Future] + def zip_futures_on(default_executor, *futures_and_or_events) + ZipFuturesPromise.new_blocked_by(futures_and_or_events, default_executor).future + end + + alias_method :zip, :zip_futures + + # @!macro promises.shortcut.on + # @return [Event] + def zip_events(*futures_and_or_events) + zip_events_on default_executor, *futures_and_or_events + end + + # Creates new event which is resolved after all futures_and_or_events are resolved. + # (Future is resolved when fulfilled or rejected.) + # + # @!macro promises.param.default_executor + # @param [AbstractEventFuture] futures_and_or_events + # @return [Event] + def zip_events_on(default_executor, *futures_and_or_events) + ZipEventsPromise.new_blocked_by(futures_and_or_events, default_executor).event + end + + # @!macro promises.shortcut.on + # @return [Future] + def any_resolved_future(*futures_and_or_events) + any_resolved_future_on default_executor, *futures_and_or_events + end + + alias_method :any, :any_resolved_future + + # Creates new future which is resolved after first futures_and_or_events is resolved. + # Its result equals result of the first resolved future. + # @!macro promises.any-touch + # If resolved it does not propagate {Concurrent::AbstractEventFuture#touch}, leaving delayed + # futures un-executed if they are not required any more. + # @!macro promises.event-conversion + # + # @!macro promises.param.default_executor + # @param [AbstractEventFuture] futures_and_or_events + # @return [Future] + def any_resolved_future_on(default_executor, *futures_and_or_events) + AnyResolvedFuturePromise.new_blocked_by(futures_and_or_events, default_executor).future + end + + # @!macro promises.shortcut.on + # @return [Future] + def any_fulfilled_future(*futures_and_or_events) + any_fulfilled_future_on default_executor, *futures_and_or_events + end + + # Creates new future which is resolved after first of futures_and_or_events is fulfilled. + # Its result equals result of the first resolved future or if all futures_and_or_events reject, + # it has reason of the last resolved future. + # @!macro promises.any-touch + # @!macro promises.event-conversion + # + # @!macro promises.param.default_executor + # @param [AbstractEventFuture] futures_and_or_events + # @return [Future] + def any_fulfilled_future_on(default_executor, *futures_and_or_events) + AnyFulfilledFuturePromise.new_blocked_by(futures_and_or_events, default_executor).future + end + + # @!macro promises.shortcut.on + # @return [Future] + def any_event(*futures_and_or_events) + any_event_on default_executor, *futures_and_or_events + end + + # Creates new event which becomes resolved after first of the futures_and_or_events resolves. + # @!macro promises.any-touch + # + # @!macro promises.param.default_executor + # @param [AbstractEventFuture] futures_and_or_events + # @return [Event] + def any_event_on(default_executor, *futures_and_or_events) + AnyResolvedEventPromise.new_blocked_by(futures_and_or_events, default_executor).event + end + + # TODO consider adding first(count, *futures) + # TODO consider adding zip_by(slice, *futures) processing futures in slices + # TODO or rather a generic aggregator taking a function + end + + module InternalStates + # @!visibility private + class State + def resolved? + raise NotImplementedError + end + + def to_sym + raise NotImplementedError + end + end + + # @!visibility private + class Pending < State + def resolved? + false + end + + def to_sym + :pending + end + end + + # @!visibility private + class Reserved < Pending + end + + # @!visibility private + class ResolvedWithResult < State + def resolved? + true + end + + def to_sym + :resolved + end + + def result + [fulfilled?, value, reason] + end + + def fulfilled? + raise NotImplementedError + end + + def value + raise NotImplementedError + end + + def reason + raise NotImplementedError + end + + def apply + raise NotImplementedError + end + end + + # @!visibility private + class Fulfilled < ResolvedWithResult + + def initialize(value) + @Value = value + end + + def fulfilled? + true + end + + def apply(args, block) + block.call value, *args + end + + def value + @Value + end + + def reason + nil + end + + def to_sym + :fulfilled + end + end + + # @!visibility private + class FulfilledArray < Fulfilled + def apply(args, block) + block.call(*value, *args) + end + end + + # @!visibility private + class Rejected < ResolvedWithResult + def initialize(reason) + @Reason = reason + end + + def fulfilled? + false + end + + def value + nil + end + + def reason + @Reason + end + + def to_sym + :rejected + end + + def apply(args, block) + block.call reason, *args + end + end + + # @!visibility private + class PartiallyRejected < ResolvedWithResult + def initialize(value, reason) + super() + @Value = value + @Reason = reason + end + + def fulfilled? + false + end + + def to_sym + :rejected + end + + def value + @Value + end + + def reason + @Reason + end + + def apply(args, block) + block.call(*reason, *args) + end + end + + # @!visibility private + PENDING = Pending.new + # @!visibility private + RESERVED = Reserved.new + # @!visibility private + RESOLVED = Fulfilled.new(nil) + + def RESOLVED.to_sym + :resolved + end + end + + private_constant :InternalStates + + # @!macro promises.shortcut.event-future + # @see Event#$0 + # @see Future#$0 + + # @!macro promises.param.timeout + # @param [Numeric] timeout the maximum time in second to wait. + + # @!macro promises.warn.blocks + # @note This function potentially blocks current thread until the Future is resolved. + # Be careful it can deadlock. Try to chain instead. + + # Common ancestor of {Event} and {Future} classes, many shared methods are defined here. + class AbstractEventFuture < Synchronization::Object + safe_initialization! + attr_atomic(:internal_state) + private :internal_state=, :swap_internal_state, :compare_and_set_internal_state, :update_internal_state + # @!method internal_state + # @!visibility private + + include InternalStates + + def initialize(promise, default_executor) + super() + @Lock = Mutex.new + @Condition = ConditionVariable.new + @Promise = promise + @DefaultExecutor = default_executor + @Callbacks = LockFreeStack.new + @Waiters = AtomicFixnum.new 0 + self.internal_state = PENDING + end + + private :initialize + + # Returns its state. + # @return [Symbol] + # + # @overload an_event.state + # @return [:pending, :resolved] + # @overload a_future.state + # Both :fulfilled, :rejected implies :resolved. + # @return [:pending, :fulfilled, :rejected] + def state + internal_state.to_sym + end + + # Is it in pending state? + # @return [Boolean] + def pending? + !internal_state.resolved? + end + + # Is it in resolved state? + # @return [Boolean] + def resolved? + internal_state.resolved? + end + + # Propagates touch. Requests all the delayed futures, which it depends on, to be + # executed. This method is called by any other method requiring resolved state, like {#wait}. + # @return [self] + def touch + @Promise.touch + self + end + + # @!macro promises.touches + # Calls {Concurrent::AbstractEventFuture#touch}. + + # @!macro promises.method.wait + # Wait (block the Thread) until receiver is {#resolved?}. + # @!macro promises.touches + # + # @!macro promises.warn.blocks + # @!macro promises.param.timeout + # @return [self, true, false] self implies timeout was not used, true implies timeout was used + # and it was resolved, false implies it was not resolved within timeout. + def wait(timeout = nil) + result = wait_until_resolved(timeout) + timeout ? result : self + end + + # Returns default executor. + # @return [Executor] default executor + # @see #with_default_executor + # @see FactoryMethods#future_on + # @see FactoryMethods#resolvable_future + # @see FactoryMethods#any_fulfilled_future_on + # @see similar + def default_executor + @DefaultExecutor + end + + # @!macro promises.shortcut.on + # @return [Future] + def chain(*args, &task) + chain_on @DefaultExecutor, *args, &task + end + + # Chains the task to be executed asynchronously on executor after it is resolved. + # + # @!macro promises.param.executor + # @!macro promises.param.args + # @return [Future] + # @!macro promise.param.task-future + # + # @overload an_event.chain_on(executor, *args, &task) + # @yield [*args] to the task. + # @overload a_future.chain_on(executor, *args, &task) + # @yield [fulfilled, value, reason, *args] to the task. + # @yieldparam [true, false] fulfilled + # @yieldparam [Object] value + # @yieldparam [Object] reason + def chain_on(executor, *args, &task) + ChainPromise.new_blocked_by1(self, @DefaultExecutor, executor, args, &task).future + end + + # @return [String] Short string representation. + def to_s + format '%s %s>', super[0..-2], state + end + + alias_method :inspect, :to_s + + # Resolves the resolvable when receiver is resolved. + # + # @param [Resolvable] resolvable + # @return [self] + def chain_resolvable(resolvable) + on_resolution! { resolvable.resolve_with internal_state } + end + + alias_method :tangle, :chain_resolvable + + # @!macro promises.shortcut.using + # @return [self] + def on_resolution(*args, &callback) + on_resolution_using @DefaultExecutor, *args, &callback + end + + # Stores the callback to be executed synchronously on resolving thread after it is + # resolved. + # + # @!macro promises.param.args + # @!macro promise.param.callback + # @return [self] + # + # @overload an_event.on_resolution!(*args, &callback) + # @yield [*args] to the callback. + # @overload a_future.on_resolution!(*args, &callback) + # @yield [fulfilled, value, reason, *args] to the callback. + # @yieldparam [true, false] fulfilled + # @yieldparam [Object] value + # @yieldparam [Object] reason + def on_resolution!(*args, &callback) + add_callback :callback_on_resolution, args, callback + end + + # Stores the callback to be executed asynchronously on executor after it is resolved. + # + # @!macro promises.param.executor + # @!macro promises.param.args + # @!macro promise.param.callback + # @return [self] + # + # @overload an_event.on_resolution_using(executor, *args, &callback) + # @yield [*args] to the callback. + # @overload a_future.on_resolution_using(executor, *args, &callback) + # @yield [fulfilled, value, reason, *args] to the callback. + # @yieldparam [true, false] fulfilled + # @yieldparam [Object] value + # @yieldparam [Object] reason + def on_resolution_using(executor, *args, &callback) + add_callback :async_callback_on_resolution, executor, args, callback + end + + # @!macro promises.method.with_default_executor + # Crates new object with same class with the executor set as its new default executor. + # Any futures depending on it will use the new default executor. + # @!macro promises.shortcut.event-future + # @abstract + # @return [AbstractEventFuture] + def with_default_executor(executor) + raise NotImplementedError + end + + # @!visibility private + def resolve_with(state, raise_on_reassign = true, reserved = false) + if compare_and_set_internal_state(reserved ? RESERVED : PENDING, state) + # go to synchronized block only if there were waiting threads + @Lock.synchronize { @Condition.broadcast } unless @Waiters.value == 0 + call_callbacks state + else + return rejected_resolution(raise_on_reassign, state) + end + self + end + + # For inspection. + # @!visibility private + # @return [Array] + def blocks + @Callbacks.each_with_object([]) do |(method, args), promises| + promises.push(args[0]) if method == :callback_notify_blocked + end + end + + # For inspection. + # @!visibility private + def callbacks + @Callbacks.each.to_a + end + + # For inspection. + # @!visibility private + def promise + @Promise + end + + # For inspection. + # @!visibility private + def touched? + promise.touched? + end + + # For inspection. + # @!visibility private + def waiting_threads + @Waiters.each.to_a + end + + # @!visibility private + def add_callback_notify_blocked(promise, index) + add_callback :callback_notify_blocked, promise, index + end + + # @!visibility private + def add_callback_clear_delayed_node(node) + add_callback(:callback_clear_delayed_node, node) + end + + # @!visibility private + def with_hidden_resolvable + # TODO (pitr-ch 10-Dec-2018): documentation, better name if in edge + self + end + + private + + def add_callback(method, *args) + state = internal_state + if state.resolved? + call_callback method, state, args + else + @Callbacks.push [method, args] + state = internal_state + # take back if it was resolved in the meanwhile + call_callbacks state if state.resolved? + end + self + end + + def callback_clear_delayed_node(state, node) + node.value = nil + end + + # @return [Boolean] + def wait_until_resolved(timeout) + return true if resolved? + + touch + + @Lock.synchronize do + @Waiters.increment + begin + unless resolved? + @Condition.wait @Lock, timeout + end + ensure + # JRuby may raise ConcurrencyError + @Waiters.decrement + end + end + resolved? + end + + def call_callback(method, state, args) + self.send method, state, *args + end + + def call_callbacks(state) + method, args = @Callbacks.pop + while method + call_callback method, state, args + method, args = @Callbacks.pop + end + end + + def with_async(executor, *args, &block) + Concurrent.executor(executor).post(*args, &block) + end + + def async_callback_on_resolution(state, executor, args, callback) + with_async(executor, state, args, callback) do |st, ar, cb| + callback_on_resolution st, ar, cb + end + end + + def callback_notify_blocked(state, promise, index) + promise.on_blocker_resolution self, index + end + end + + # Represents an event which will happen in future (will be resolved). The event is either + # pending or resolved. It should be always resolved. Use {Future} to communicate rejections and + # cancellation. + class Event < AbstractEventFuture + + alias_method :then, :chain + + + # @!macro promises.method.zip + # Creates a new event or a future which will be resolved when receiver and other are. + # Returns an event if receiver and other are events, otherwise returns a future. + # If just one of the parties is Future then the result + # of the returned future is equal to the result of the supplied future. If both are futures + # then the result is as described in {FactoryMethods#zip_futures_on}. + # + # @return [Future, Event] + def zip(other) + if other.is_a?(Future) + ZipFutureEventPromise.new_blocked_by2(other, self, @DefaultExecutor).future + else + ZipEventEventPromise.new_blocked_by2(self, other, @DefaultExecutor).event + end + end + + alias_method :&, :zip + + # Creates a new event which will be resolved when the first of receiver, `event_or_future` + # resolves. + # + # @return [Event] + def any(event_or_future) + AnyResolvedEventPromise.new_blocked_by2(self, event_or_future, @DefaultExecutor).event + end + + alias_method :|, :any + + # Creates new event dependent on receiver which will not evaluate until touched, see {#touch}. + # In other words, it inserts delay into the chain of Futures making rest of it lazy evaluated. + # + # @return [Event] + def delay + event = DelayPromise.new(@DefaultExecutor).event + ZipEventEventPromise.new_blocked_by2(self, event, @DefaultExecutor).event + end + + # @!macro promise.method.schedule + # Creates new event dependent on receiver scheduled to execute on/in intended_time. + # In time is interpreted from the moment the receiver is resolved, therefore it inserts + # delay into the chain. + # + # @!macro promises.param.intended_time + # @return [Event] + def schedule(intended_time) + chain do + event = ScheduledPromise.new(@DefaultExecutor, intended_time).event + ZipEventEventPromise.new_blocked_by2(self, event, @DefaultExecutor).event + end.flat_event + end + + # Converts event to a future. The future is fulfilled when the event is resolved, the future may never fail. + # + # @return [Future] + def to_future + future = Promises.resolvable_future + ensure + chain_resolvable(future) + end + + # Returns self, since this is event + # @return [Event] + def to_event + self + end + + # @!macro promises.method.with_default_executor + # @return [Event] + def with_default_executor(executor) + EventWrapperPromise.new_blocked_by1(self, executor).event + end + + private + + def rejected_resolution(raise_on_reassign, state) + Concurrent::MultipleAssignmentError.new('Event can be resolved only once') if raise_on_reassign + return false + end + + def callback_on_resolution(state, args, callback) + callback.call(*args) + end + end + + # Represents a value which will become available in future. May reject with a reason instead, + # e.g. when the tasks raises an exception. + class Future < AbstractEventFuture + + # Is it in fulfilled state? + # @return [Boolean] + def fulfilled? + state = internal_state + state.resolved? && state.fulfilled? + end + + # Is it in rejected state? + # @return [Boolean] + def rejected? + state = internal_state + state.resolved? && !state.fulfilled? + end + + # @!macro promises.warn.nil + # @note Make sure returned `nil` is not confused with timeout, no value when rejected, + # no reason when fulfilled, etc. + # Use more exact methods if needed, like {#wait}, {#value!}, {#result}, etc. + + # @!macro promises.method.value + # Return value of the future. + # @!macro promises.touches + # + # @!macro promises.warn.blocks + # @!macro promises.warn.nil + # @!macro promises.param.timeout + # @!macro promises.param.timeout_value + # @param [Object] timeout_value a value returned by the method when it times out + # @return [Object, nil, timeout_value] the value of the Future when fulfilled, + # timeout_value on timeout, + # nil on rejection. + def value(timeout = nil, timeout_value = nil) + if wait_until_resolved timeout + internal_state.value + else + timeout_value + end + end + + # Returns reason of future's rejection. + # @!macro promises.touches + # + # @!macro promises.warn.blocks + # @!macro promises.warn.nil + # @!macro promises.param.timeout + # @!macro promises.param.timeout_value + # @return [Object, timeout_value] the reason, or timeout_value on timeout, or nil on fulfillment. + def reason(timeout = nil, timeout_value = nil) + if wait_until_resolved timeout + internal_state.reason + else + timeout_value + end + end + + # Returns triplet fulfilled?, value, reason. + # @!macro promises.touches + # + # @!macro promises.warn.blocks + # @!macro promises.param.timeout + # @return [Array(Boolean, Object, Object), nil] triplet of fulfilled?, value, reason, or nil + # on timeout. + def result(timeout = nil) + internal_state.result if wait_until_resolved timeout + end + + # @!macro promises.method.wait + # @raise [Exception] {#reason} on rejection + def wait!(timeout = nil) + result = wait_until_resolved!(timeout) + timeout ? result : self + end + + # @!macro promises.method.value + # @return [Object, nil, timeout_value] the value of the Future when fulfilled, + # or nil on rejection, + # or timeout_value on timeout. + # @raise [Exception] {#reason} on rejection + def value!(timeout = nil, timeout_value = nil) + if wait_until_resolved! timeout + internal_state.value + else + timeout_value + end + end + + # Allows rejected Future to be risen with `raise` method. + # If the reason is not an exception `Runtime.new(reason)` is returned. + # + # @example + # raise Promises.rejected_future(StandardError.new("boom")) + # raise Promises.rejected_future("or just boom") + # @raise [Concurrent::Error] when raising not rejected future + # @return [Exception] + def exception(*args) + raise Concurrent::Error, 'it is not rejected' unless rejected? + raise ArgumentError unless args.size <= 1 + reason = Array(internal_state.reason).flatten.compact + if reason.size > 1 + ex = Concurrent::MultipleErrors.new reason + ex.set_backtrace(caller) + ex + else + ex = if reason[0].respond_to? :exception + reason[0].exception(*args) + else + RuntimeError.new(reason[0]).exception(*args) + end + ex.set_backtrace Array(ex.backtrace) + caller + ex + end + end + + # @!macro promises.shortcut.on + # @return [Future] + def then(*args, &task) + then_on @DefaultExecutor, *args, &task + end + + # Chains the task to be executed asynchronously on executor after it fulfills. Does not run + # the task if it rejects. It will resolve though, triggering any dependent futures. + # + # @!macro promises.param.executor + # @!macro promises.param.args + # @!macro promise.param.task-future + # @return [Future] + # @yield [value, *args] to the task. + def then_on(executor, *args, &task) + ThenPromise.new_blocked_by1(self, @DefaultExecutor, executor, args, &task).future + end + + # @!macro promises.shortcut.on + # @return [Future] + def rescue(*args, &task) + rescue_on @DefaultExecutor, *args, &task + end + + # Chains the task to be executed asynchronously on executor after it rejects. Does not run + # the task if it fulfills. It will resolve though, triggering any dependent futures. + # + # @!macro promises.param.executor + # @!macro promises.param.args + # @!macro promise.param.task-future + # @return [Future] + # @yield [reason, *args] to the task. + def rescue_on(executor, *args, &task) + RescuePromise.new_blocked_by1(self, @DefaultExecutor, executor, args, &task).future + end + + # @!macro promises.method.zip + # @return [Future] + def zip(other) + if other.is_a?(Future) + ZipFuturesPromise.new_blocked_by2(self, other, @DefaultExecutor).future + else + ZipFutureEventPromise.new_blocked_by2(self, other, @DefaultExecutor).future + end + end + + alias_method :&, :zip + + # Creates a new event which will be resolved when the first of receiver, `event_or_future` + # resolves. Returning future will have value nil if event_or_future is event and resolves + # first. + # + # @return [Future] + def any(event_or_future) + AnyResolvedFuturePromise.new_blocked_by2(self, event_or_future, @DefaultExecutor).future + end + + alias_method :|, :any + + # Creates new future dependent on receiver which will not evaluate until touched, see {#touch}. + # In other words, it inserts delay into the chain of Futures making rest of it lazy evaluated. + # + # @return [Future] + def delay + event = DelayPromise.new(@DefaultExecutor).event + ZipFutureEventPromise.new_blocked_by2(self, event, @DefaultExecutor).future + end + + # @!macro promise.method.schedule + # @return [Future] + def schedule(intended_time) + chain do + event = ScheduledPromise.new(@DefaultExecutor, intended_time).event + ZipFutureEventPromise.new_blocked_by2(self, event, @DefaultExecutor).future + end.flat + end + + # @!macro promises.method.with_default_executor + # @return [Future] + def with_default_executor(executor) + FutureWrapperPromise.new_blocked_by1(self, executor).future + end + + # Creates new future which will have result of the future returned by receiver. If receiver + # rejects it will have its rejection. + # + # @param [Integer] level how many levels of futures should flatten + # @return [Future] + def flat_future(level = 1) + FlatFuturePromise.new_blocked_by1(self, level, @DefaultExecutor).future + end + + alias_method :flat, :flat_future + + # Creates new event which will be resolved when the returned event by receiver is. + # Be careful if the receiver rejects it will just resolve since Event does not hold reason. + # + # @return [Event] + def flat_event + FlatEventPromise.new_blocked_by1(self, @DefaultExecutor).event + end + + # @!macro promises.shortcut.using + # @return [self] + def on_fulfillment(*args, &callback) + on_fulfillment_using @DefaultExecutor, *args, &callback + end + + # Stores the callback to be executed synchronously on resolving thread after it is + # fulfilled. Does nothing on rejection. + # + # @!macro promises.param.args + # @!macro promise.param.callback + # @return [self] + # @yield [value, *args] to the callback. + def on_fulfillment!(*args, &callback) + add_callback :callback_on_fulfillment, args, callback + end + + # Stores the callback to be executed asynchronously on executor after it is + # fulfilled. Does nothing on rejection. + # + # @!macro promises.param.executor + # @!macro promises.param.args + # @!macro promise.param.callback + # @return [self] + # @yield [value, *args] to the callback. + def on_fulfillment_using(executor, *args, &callback) + add_callback :async_callback_on_fulfillment, executor, args, callback + end + + # @!macro promises.shortcut.using + # @return [self] + def on_rejection(*args, &callback) + on_rejection_using @DefaultExecutor, *args, &callback + end + + # Stores the callback to be executed synchronously on resolving thread after it is + # rejected. Does nothing on fulfillment. + # + # @!macro promises.param.args + # @!macro promise.param.callback + # @return [self] + # @yield [reason, *args] to the callback. + def on_rejection!(*args, &callback) + add_callback :callback_on_rejection, args, callback + end + + # Stores the callback to be executed asynchronously on executor after it is + # rejected. Does nothing on fulfillment. + # + # @!macro promises.param.executor + # @!macro promises.param.args + # @!macro promise.param.callback + # @return [self] + # @yield [reason, *args] to the callback. + def on_rejection_using(executor, *args, &callback) + add_callback :async_callback_on_rejection, executor, args, callback + end + + # Allows to use futures as green threads. The receiver has to evaluate to a future which + # represents what should be done next. It basically flattens indefinitely until non Future + # values is returned which becomes result of the returned future. Any encountered exception + # will become reason of the returned future. + # + # @return [Future] + # @param [#call(value)] run_test + # an object which when called returns either Future to keep running with + # or nil, then the run completes with the value. + # The run_test can be used to extract the Future from deeper structure, + # or to distinguish Future which is a resulting value from a future + # which is suppose to continue running. + # @example + # body = lambda do |v| + # v += 1 + # v < 5 ? Promises.future(v, &body) : v + # end + # Promises.future(0, &body).run.value! # => 5 + def run(run_test = method(:run_test)) + RunFuturePromise.new_blocked_by1(self, @DefaultExecutor, run_test).future + end + + # @!visibility private + def apply(args, block) + internal_state.apply args, block + end + + # Converts future to event which is resolved when future is resolved by fulfillment or rejection. + # + # @return [Event] + def to_event + event = Promises.resolvable_event + ensure + chain_resolvable(event) + end + + # Returns self, since this is a future + # @return [Future] + def to_future + self + end + + # @return [String] Short string representation. + def to_s + if resolved? + format '%s with %s>', super[0..-2], (fulfilled? ? value : reason).inspect + else + super + end + end + + alias_method :inspect, :to_s + + private + + def run_test(v) + v if v.is_a?(Future) + end + + def rejected_resolution(raise_on_reassign, state) + if raise_on_reassign + if internal_state == RESERVED + raise Concurrent::MultipleAssignmentError.new( + "Future can be resolved only once. It is already reserved.") + else + raise Concurrent::MultipleAssignmentError.new( + "Future can be resolved only once. It's #{result}, trying to set #{state.result}.", + current_result: result, + new_result: state.result) + end + end + return false + end + + def wait_until_resolved!(timeout = nil) + result = wait_until_resolved(timeout) + raise self if rejected? + result + end + + def async_callback_on_fulfillment(state, executor, args, callback) + with_async(executor, state, args, callback) do |st, ar, cb| + callback_on_fulfillment st, ar, cb + end + end + + def async_callback_on_rejection(state, executor, args, callback) + with_async(executor, state, args, callback) do |st, ar, cb| + callback_on_rejection st, ar, cb + end + end + + def callback_on_fulfillment(state, args, callback) + state.apply args, callback if state.fulfilled? + end + + def callback_on_rejection(state, args, callback) + state.apply args, callback unless state.fulfilled? + end + + def callback_on_resolution(state, args, callback) + callback.call(*state.result, *args) + end + + end + + # Marker module of Future, Event resolved manually. + module Resolvable + include InternalStates + end + + # A Event which can be resolved by user. + class ResolvableEvent < Event + include Resolvable + + # @!macro raise_on_reassign + # @raise [MultipleAssignmentError] when already resolved and raise_on_reassign is true. + + # @!macro promise.param.raise_on_reassign + # @param [Boolean] raise_on_reassign should method raise exception if already resolved + # @return [self, false] false is returner when raise_on_reassign is false and the receiver + # is already resolved. + # + + # Makes the event resolved, which triggers all dependent futures. + # + # @!macro promise.param.raise_on_reassign + # @!macro promise.param.reserved + # @param [true, false] reserved + # Set to true if the resolvable is {#reserve}d by you, + # marks resolution of reserved resolvable events and futures explicitly. + # Advanced feature, ignore unless you use {Resolvable#reserve} from edge. + def resolve(raise_on_reassign = true, reserved = false) + resolve_with RESOLVED, raise_on_reassign, reserved + end + + # Creates new event wrapping receiver, effectively hiding the resolve method. + # + # @return [Event] + def with_hidden_resolvable + @with_hidden_resolvable ||= EventWrapperPromise.new_blocked_by1(self, @DefaultExecutor).event + end + + # Behaves as {AbstractEventFuture#wait} but has one additional optional argument + # resolve_on_timeout. + # + # @param [true, false] resolve_on_timeout + # If it times out and the argument is true it will also resolve the event. + # @return [self, true, false] + # @see AbstractEventFuture#wait + def wait(timeout = nil, resolve_on_timeout = false) + super(timeout) or if resolve_on_timeout + # if it fails to resolve it was resolved in the meantime + # so return true as if there was no timeout + !resolve(false) + else + false + end + end + end + + # A Future which can be resolved by user. + class ResolvableFuture < Future + include Resolvable + + # Makes the future resolved with result of triplet `fulfilled?`, `value`, `reason`, + # which triggers all dependent futures. + # + # @param [true, false] fulfilled + # @param [Object] value + # @param [Object] reason + # @!macro promise.param.raise_on_reassign + # @!macro promise.param.reserved + def resolve(fulfilled = true, value = nil, reason = nil, raise_on_reassign = true, reserved = false) + resolve_with(fulfilled ? Fulfilled.new(value) : Rejected.new(reason), raise_on_reassign, reserved) + end + + # Makes the future fulfilled with `value`, + # which triggers all dependent futures. + # + # @param [Object] value + # @!macro promise.param.raise_on_reassign + # @!macro promise.param.reserved + def fulfill(value, raise_on_reassign = true, reserved = false) + resolve_with Fulfilled.new(value), raise_on_reassign, reserved + end + + # Makes the future rejected with `reason`, + # which triggers all dependent futures. + # + # @param [Object] reason + # @!macro promise.param.raise_on_reassign + # @!macro promise.param.reserved + def reject(reason, raise_on_reassign = true, reserved = false) + resolve_with Rejected.new(reason), raise_on_reassign, reserved + end + + # Evaluates the block and sets its result as future's value fulfilling, if the block raises + # an exception the future rejects with it. + # + # @yield [*args] to the block. + # @yieldreturn [Object] value + # @return [self] + def evaluate_to(*args, &block) + promise.evaluate_to(*args, block) + end + + # Evaluates the block and sets its result as future's value fulfilling, if the block raises + # an exception the future rejects with it. + # + # @yield [*args] to the block. + # @yieldreturn [Object] value + # @return [self] + # @raise [Exception] also raise reason on rejection. + def evaluate_to!(*args, &block) + promise.evaluate_to(*args, block).wait! + end + + # @!macro promises.resolvable.resolve_on_timeout + # @param [::Array(true, Object, nil), ::Array(false, nil, Exception), nil] resolve_on_timeout + # If it times out and the argument is not nil it will also resolve the future + # to the provided resolution. + + # Behaves as {AbstractEventFuture#wait} but has one additional optional argument + # resolve_on_timeout. + # + # @!macro promises.resolvable.resolve_on_timeout + # @return [self, true, false] + # @see AbstractEventFuture#wait + def wait(timeout = nil, resolve_on_timeout = nil) + super(timeout) or if resolve_on_timeout + # if it fails to resolve it was resolved in the meantime + # so return true as if there was no timeout + !resolve(*resolve_on_timeout, false) + else + false + end + end + + # Behaves as {Future#wait!} but has one additional optional argument + # resolve_on_timeout. + # + # @!macro promises.resolvable.resolve_on_timeout + # @return [self, true, false] + # @raise [Exception] {#reason} on rejection + # @see Future#wait! + def wait!(timeout = nil, resolve_on_timeout = nil) + super(timeout) or if resolve_on_timeout + if resolve(*resolve_on_timeout, false) + false + else + # if it fails to resolve it was resolved in the meantime + # so return true as if there was no timeout + raise self if rejected? + true + end + else + false + end + end + + # Behaves as {Future#value} but has one additional optional argument + # resolve_on_timeout. + # + # @!macro promises.resolvable.resolve_on_timeout + # @return [Object, timeout_value, nil] + # @see Future#value + def value(timeout = nil, timeout_value = nil, resolve_on_timeout = nil) + if wait_until_resolved timeout + internal_state.value + else + if resolve_on_timeout + unless resolve(*resolve_on_timeout, false) + # if it fails to resolve it was resolved in the meantime + # so return value as if there was no timeout + return internal_state.value + end + end + timeout_value + end + end + + # Behaves as {Future#value!} but has one additional optional argument + # resolve_on_timeout. + # + # @!macro promises.resolvable.resolve_on_timeout + # @return [Object, timeout_value, nil] + # @raise [Exception] {#reason} on rejection + # @see Future#value! + def value!(timeout = nil, timeout_value = nil, resolve_on_timeout = nil) + if wait_until_resolved! timeout + internal_state.value + else + if resolve_on_timeout + unless resolve(*resolve_on_timeout, false) + # if it fails to resolve it was resolved in the meantime + # so return value as if there was no timeout + raise self if rejected? + return internal_state.value + end + end + timeout_value + end + end + + # Behaves as {Future#reason} but has one additional optional argument + # resolve_on_timeout. + # + # @!macro promises.resolvable.resolve_on_timeout + # @return [Exception, timeout_value, nil] + # @see Future#reason + def reason(timeout = nil, timeout_value = nil, resolve_on_timeout = nil) + if wait_until_resolved timeout + internal_state.reason + else + if resolve_on_timeout + unless resolve(*resolve_on_timeout, false) + # if it fails to resolve it was resolved in the meantime + # so return value as if there was no timeout + return internal_state.reason + end + end + timeout_value + end + end + + # Behaves as {Future#result} but has one additional optional argument + # resolve_on_timeout. + # + # @!macro promises.resolvable.resolve_on_timeout + # @return [::Array(Boolean, Object, Exception), nil] + # @see Future#result + def result(timeout = nil, resolve_on_timeout = nil) + if wait_until_resolved timeout + internal_state.result + else + if resolve_on_timeout + unless resolve(*resolve_on_timeout, false) + # if it fails to resolve it was resolved in the meantime + # so return value as if there was no timeout + internal_state.result + end + end + # otherwise returns nil + end + end + + # Creates new future wrapping receiver, effectively hiding the resolve method and similar. + # + # @return [Future] + def with_hidden_resolvable + @with_hidden_resolvable ||= FutureWrapperPromise.new_blocked_by1(self, @DefaultExecutor).future + end + end + + # @abstract + # @private + class AbstractPromise < Synchronization::Object + safe_initialization! + include InternalStates + + def initialize(future) + super() + @Future = future + end + + def future + @Future + end + + alias_method :event, :future + + def default_executor + future.default_executor + end + + def state + future.state + end + + def touch + end + + def to_s + format '%s %s>', super[0..-2], @Future + end + + alias_method :inspect, :to_s + + def delayed_because + nil + end + + private + + def resolve_with(new_state, raise_on_reassign = true) + @Future.resolve_with(new_state, raise_on_reassign) + end + + # @return [Future] + def evaluate_to(*args, block) + resolve_with Fulfilled.new(block.call(*args)) + rescue Exception => error + resolve_with Rejected.new(error) + raise error unless error.is_a?(StandardError) + end + end + + class ResolvableEventPromise < AbstractPromise + def initialize(default_executor) + super ResolvableEvent.new(self, default_executor) + end + end + + class ResolvableFuturePromise < AbstractPromise + def initialize(default_executor) + super ResolvableFuture.new(self, default_executor) + end + + public :evaluate_to + end + + # @abstract + class InnerPromise < AbstractPromise + end + + # @abstract + class BlockedPromise < InnerPromise + + private_class_method :new + + def self.new_blocked_by1(blocker, *args, &block) + blocker_delayed = blocker.promise.delayed_because + promise = new(blocker_delayed, 1, *args, &block) + blocker.add_callback_notify_blocked promise, 0 + promise + end + + def self.new_blocked_by2(blocker1, blocker2, *args, &block) + blocker_delayed1 = blocker1.promise.delayed_because + blocker_delayed2 = blocker2.promise.delayed_because + delayed = if blocker_delayed1 && blocker_delayed2 + # TODO (pitr-ch 23-Dec-2016): use arrays when we know it will not grow (only flat adds delay) + LockFreeStack.of2(blocker_delayed1, blocker_delayed2) + else + blocker_delayed1 || blocker_delayed2 + end + promise = new(delayed, 2, *args, &block) + blocker1.add_callback_notify_blocked promise, 0 + blocker2.add_callback_notify_blocked promise, 1 + promise + end + + def self.new_blocked_by(blockers, *args, &block) + delayed = blockers.reduce(nil) { |d, f| add_delayed d, f.promise.delayed_because } + promise = new(delayed, blockers.size, *args, &block) + blockers.each_with_index { |f, i| f.add_callback_notify_blocked promise, i } + promise + end + + def self.add_delayed(delayed1, delayed2) + if delayed1 && delayed2 + delayed1.push delayed2 + delayed1 + else + delayed1 || delayed2 + end + end + + def initialize(delayed, blockers_count, future) + super(future) + @Delayed = delayed + @Countdown = AtomicFixnum.new blockers_count + end + + def on_blocker_resolution(future, index) + countdown = process_on_blocker_resolution(future, index) + resolvable = resolvable?(countdown, future, index) + + on_resolvable(future, index) if resolvable + end + + def delayed_because + @Delayed + end + + def touch + clear_and_propagate_touch + end + + # for inspection only + def blocked_by + blocked_by = [] + ObjectSpace.each_object(AbstractEventFuture) { |o| blocked_by.push o if o.blocks.include? self } + blocked_by + end + + private + + def clear_and_propagate_touch(stack_or_element = @Delayed) + return if stack_or_element.nil? + + if stack_or_element.is_a? LockFreeStack + stack_or_element.clear_each { |element| clear_and_propagate_touch element } + else + stack_or_element.touch unless stack_or_element.nil? # if still present + end + end + + # @return [true,false] if resolvable + def resolvable?(countdown, future, index) + countdown.zero? + end + + def process_on_blocker_resolution(future, index) + @Countdown.decrement + end + + def on_resolvable(resolved_future, index) + raise NotImplementedError + end + end + + # @abstract + class BlockedTaskPromise < BlockedPromise + def initialize(delayed, blockers_count, default_executor, executor, args, &task) + raise ArgumentError, 'no block given' unless block_given? + super delayed, 1, Future.new(self, default_executor) + @Executor = executor + @Task = task + @Args = args + end + + def executor + @Executor + end + end + + class ThenPromise < BlockedTaskPromise + private + + def initialize(delayed, blockers_count, default_executor, executor, args, &task) + super delayed, blockers_count, default_executor, executor, args, &task + end + + def on_resolvable(resolved_future, index) + if resolved_future.fulfilled? + Concurrent.executor(@Executor).post(resolved_future, @Args, @Task) do |future, args, task| + evaluate_to lambda { future.apply args, task } + end + else + resolve_with resolved_future.internal_state + end + end + end + + class RescuePromise < BlockedTaskPromise + private + + def initialize(delayed, blockers_count, default_executor, executor, args, &task) + super delayed, blockers_count, default_executor, executor, args, &task + end + + def on_resolvable(resolved_future, index) + if resolved_future.rejected? + Concurrent.executor(@Executor).post(resolved_future, @Args, @Task) do |future, args, task| + evaluate_to lambda { future.apply args, task } + end + else + resolve_with resolved_future.internal_state + end + end + end + + class ChainPromise < BlockedTaskPromise + private + + def on_resolvable(resolved_future, index) + if Future === resolved_future + Concurrent.executor(@Executor).post(resolved_future, @Args, @Task) do |future, args, task| + evaluate_to(*future.result, *args, task) + end + else + Concurrent.executor(@Executor).post(@Args, @Task) do |args, task| + evaluate_to(*args, task) + end + end + end + end + + # will be immediately resolved + class ImmediateEventPromise < InnerPromise + def initialize(default_executor) + super Event.new(self, default_executor).resolve_with(RESOLVED) + end + end + + class ImmediateFuturePromise < InnerPromise + def initialize(default_executor, fulfilled, value, reason) + super Future.new(self, default_executor). + resolve_with(fulfilled ? Fulfilled.new(value) : Rejected.new(reason)) + end + end + + class AbstractFlatPromise < BlockedPromise + + def initialize(delayed_because, blockers_count, event_or_future) + delayed = LockFreeStack.of1(self) + super(delayed, blockers_count, event_or_future) + # noinspection RubyArgCount + @Touched = AtomicBoolean.new false + @DelayedBecause = delayed_because || LockFreeStack.new + + event_or_future.add_callback_clear_delayed_node delayed.peek + end + + def touch + if @Touched.make_true + clear_and_propagate_touch @DelayedBecause + end + end + + private + + def touched? + @Touched.value + end + + def on_resolvable(resolved_future, index) + resolve_with resolved_future.internal_state + end + + def resolvable?(countdown, future, index) + !@Future.internal_state.resolved? && super(countdown, future, index) + end + + def add_delayed_of(future) + delayed = future.promise.delayed_because + if touched? + clear_and_propagate_touch delayed + else + BlockedPromise.add_delayed @DelayedBecause, delayed + clear_and_propagate_touch @DelayedBecause if touched? + end + end + + end + + class FlatEventPromise < AbstractFlatPromise + + private + + def initialize(delayed, blockers_count, default_executor) + super delayed, 2, Event.new(self, default_executor) + end + + def process_on_blocker_resolution(future, index) + countdown = super(future, index) + if countdown.nonzero? + internal_state = future.internal_state + + unless internal_state.fulfilled? + resolve_with RESOLVED + return countdown + end + + value = internal_state.value + case value + when AbstractEventFuture + add_delayed_of value + value.add_callback_notify_blocked self, nil + countdown + else + resolve_with RESOLVED + end + end + countdown + end + + end + + class FlatFuturePromise < AbstractFlatPromise + + private + + def initialize(delayed, blockers_count, levels, default_executor) + raise ArgumentError, 'levels has to be higher than 0' if levels < 1 + # flat promise may result to a future having delayed futures, therefore we have to have empty stack + # to be able to add new delayed futures + super delayed || LockFreeStack.new, 1 + levels, Future.new(self, default_executor) + end + + def process_on_blocker_resolution(future, index) + countdown = super(future, index) + if countdown.nonzero? + internal_state = future.internal_state + + unless internal_state.fulfilled? + resolve_with internal_state + return countdown + end + + value = internal_state.value + case value + when AbstractEventFuture + add_delayed_of value + value.add_callback_notify_blocked self, nil + countdown + else + evaluate_to(lambda { raise TypeError, "returned value #{value.inspect} is not a Future" }) + end + end + countdown + end + + end + + class RunFuturePromise < AbstractFlatPromise + + private + + def initialize(delayed, blockers_count, default_executor, run_test) + super delayed, 1, Future.new(self, default_executor) + @RunTest = run_test + end + + def process_on_blocker_resolution(future, index) + internal_state = future.internal_state + + unless internal_state.fulfilled? + resolve_with internal_state + return 0 + end + + value = internal_state.value + continuation_future = @RunTest.call value + + if continuation_future + add_delayed_of continuation_future + continuation_future.add_callback_notify_blocked self, nil + else + resolve_with internal_state + end + + 1 + end + end + + class ZipEventEventPromise < BlockedPromise + def initialize(delayed, blockers_count, default_executor) + super delayed, 2, Event.new(self, default_executor) + end + + private + + def on_resolvable(resolved_future, index) + resolve_with RESOLVED + end + end + + class ZipFutureEventPromise < BlockedPromise + def initialize(delayed, blockers_count, default_executor) + super delayed, 2, Future.new(self, default_executor) + @result = nil + end + + private + + def process_on_blocker_resolution(future, index) + # first blocking is future, take its result + @result = future.internal_state if index == 0 + # super has to be called after above to piggyback on volatile @Countdown + super future, index + end + + def on_resolvable(resolved_future, index) + resolve_with @result + end + end + + class EventWrapperPromise < BlockedPromise + def initialize(delayed, blockers_count, default_executor) + super delayed, 1, Event.new(self, default_executor) + end + + private + + def on_resolvable(resolved_future, index) + resolve_with RESOLVED + end + end + + class FutureWrapperPromise < BlockedPromise + def initialize(delayed, blockers_count, default_executor) + super delayed, 1, Future.new(self, default_executor) + end + + private + + def on_resolvable(resolved_future, index) + resolve_with resolved_future.internal_state + end + end + + class ZipFuturesPromise < BlockedPromise + + private + + def initialize(delayed, blockers_count, default_executor) + super(delayed, blockers_count, Future.new(self, default_executor)) + @Resolutions = ::Array.new(blockers_count, nil) + + on_resolvable nil, nil if blockers_count == 0 + end + + def process_on_blocker_resolution(future, index) + # TODO (pitr-ch 18-Dec-2016): Can we assume that array will never break under parallel access when never re-sized? + @Resolutions[index] = future.internal_state # has to be set before countdown in super + super future, index + end + + def on_resolvable(resolved_future, index) + all_fulfilled = true + values = ::Array.new(@Resolutions.size) + reasons = ::Array.new(@Resolutions.size) + + @Resolutions.each_with_index do |internal_state, i| + fulfilled, values[i], reasons[i] = internal_state.result + all_fulfilled &&= fulfilled + end + + if all_fulfilled + resolve_with FulfilledArray.new(values) + else + resolve_with PartiallyRejected.new(values, reasons) + end + end + end + + class ZipEventsPromise < BlockedPromise + + private + + def initialize(delayed, blockers_count, default_executor) + super delayed, blockers_count, Event.new(self, default_executor) + + on_resolvable nil, nil if blockers_count == 0 + end + + def on_resolvable(resolved_future, index) + resolve_with RESOLVED + end + end + + # @abstract + class AbstractAnyPromise < BlockedPromise + end + + class AnyResolvedEventPromise < AbstractAnyPromise + + private + + def initialize(delayed, blockers_count, default_executor) + super delayed, blockers_count, Event.new(self, default_executor) + end + + def resolvable?(countdown, future, index) + true + end + + def on_resolvable(resolved_future, index) + resolve_with RESOLVED, false + end + end + + class AnyResolvedFuturePromise < AbstractAnyPromise + + private + + def initialize(delayed, blockers_count, default_executor) + super delayed, blockers_count, Future.new(self, default_executor) + end + + def resolvable?(countdown, future, index) + true + end + + def on_resolvable(resolved_future, index) + resolve_with resolved_future.internal_state, false + end + end + + class AnyFulfilledFuturePromise < AnyResolvedFuturePromise + + private + + def resolvable?(countdown, future, index) + future.fulfilled? || + # inlined super from BlockedPromise + countdown.zero? + end + end + + class DelayPromise < InnerPromise + + def initialize(default_executor) + event = Event.new(self, default_executor) + @Delayed = LockFreeStack.of1(self) + super event + event.add_callback_clear_delayed_node @Delayed.peek + end + + def touch + @Future.resolve_with RESOLVED + end + + def delayed_because + @Delayed + end + + end + + class ScheduledPromise < InnerPromise + def intended_time + @IntendedTime + end + + def inspect + "#{to_s[0..-2]} intended_time: #{@IntendedTime}>" + end + + private + + def initialize(default_executor, intended_time) + super Event.new(self, default_executor) + + @IntendedTime = intended_time + + in_seconds = begin + now = Time.now + schedule_time = if @IntendedTime.is_a? Time + @IntendedTime + else + now + @IntendedTime + end + [0, schedule_time.to_f - now.to_f].max + end + + Concurrent.global_timer_set.post(in_seconds) do + @Future.resolve_with RESOLVED + end + end + end + + extend FactoryMethods + + private_constant :AbstractPromise, + :ResolvableEventPromise, + :ResolvableFuturePromise, + :InnerPromise, + :BlockedPromise, + :BlockedTaskPromise, + :ThenPromise, + :RescuePromise, + :ChainPromise, + :ImmediateEventPromise, + :ImmediateFuturePromise, + :AbstractFlatPromise, + :FlatFuturePromise, + :FlatEventPromise, + :RunFuturePromise, + :ZipEventEventPromise, + :ZipFutureEventPromise, + :EventWrapperPromise, + :FutureWrapperPromise, + :ZipFuturesPromise, + :ZipEventsPromise, + :AbstractAnyPromise, + :AnyResolvedFuturePromise, + :AnyFulfilledFuturePromise, + :AnyResolvedEventPromise, + :DelayPromise, + :ScheduledPromise + + + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/re_include.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/re_include.rb new file mode 100644 index 0000000..516d58c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/re_include.rb @@ -0,0 +1,58 @@ +module Concurrent + + # Methods form module A included to a module B, which is already included into class C, + # will not be visible in the C class. If this module is extended to B then A's methods + # are correctly made visible to C. + # + # @example + # module A + # def a + # :a + # end + # end + # + # module B1 + # end + # + # class C1 + # include B1 + # end + # + # module B2 + # extend Concurrent::ReInclude + # end + # + # class C2 + # include B2 + # end + # + # B1.send :include, A + # B2.send :include, A + # + # C1.new.respond_to? :a # => false + # C2.new.respond_to? :a # => true + module ReInclude + # @!visibility private + def included(base) + (@re_include_to_bases ||= []) << [:include, base] + super(base) + end + + # @!visibility private + def extended(base) + (@re_include_to_bases ||= []) << [:extend, base] + super(base) + end + + # @!visibility private + def include(*modules) + result = super(*modules) + modules.reverse.each do |module_being_included| + (@re_include_to_bases ||= []).each do |method, mod| + mod.send method, module_being_included + end + end + result + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/scheduled_task.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/scheduled_task.rb new file mode 100644 index 0000000..96c8272 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/scheduled_task.rb @@ -0,0 +1,331 @@ +require 'concurrent/constants' +require 'concurrent/errors' +require 'concurrent/configuration' +require 'concurrent/ivar' +require 'concurrent/collection/copy_on_notify_observer_set' +require 'concurrent/utility/monotonic_time' + +require 'concurrent/options' + +module Concurrent + + # `ScheduledTask` is a close relative of `Concurrent::Future` but with one + # important difference: A `Future` is set to execute as soon as possible + # whereas a `ScheduledTask` is set to execute after a specified delay. This + # implementation is loosely based on Java's + # [ScheduledExecutorService](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledExecutorService.html). + # It is a more feature-rich variant of {Concurrent.timer}. + # + # The *intended* schedule time of task execution is set on object construction + # with the `delay` argument. The delay is a numeric (floating point or integer) + # representing a number of seconds in the future. Any other value or a numeric + # equal to or less than zero will result in an exception. The *actual* schedule + # time of task execution is set when the `execute` method is called. + # + # The constructor can also be given zero or more processing options. Currently + # the only supported options are those recognized by the + # [Dereferenceable](Dereferenceable) module. + # + # The final constructor argument is a block representing the task to be performed. + # If no block is given an `ArgumentError` will be raised. + # + # **States** + # + # `ScheduledTask` mixes in the [Obligation](Obligation) module thus giving it + # "future" behavior. This includes the expected lifecycle states. `ScheduledTask` + # has one additional state, however. While the task (block) is being executed the + # state of the object will be `:processing`. This additional state is necessary + # because it has implications for task cancellation. + # + # **Cancellation** + # + # A `:pending` task can be cancelled using the `#cancel` method. A task in any + # other state, including `:processing`, cannot be cancelled. The `#cancel` + # method returns a boolean indicating the success of the cancellation attempt. + # A cancelled `ScheduledTask` cannot be restarted. It is immutable. + # + # **Obligation and Observation** + # + # The result of a `ScheduledTask` can be obtained either synchronously or + # asynchronously. `ScheduledTask` mixes in both the [Obligation](Obligation) + # module and the + # [Observable](http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html) + # module from the Ruby standard library. With one exception `ScheduledTask` + # behaves identically to [Future](Observable) with regard to these modules. + # + # @!macro copy_options + # + # @example Basic usage + # + # require 'concurrent' + # require 'csv' + # require 'open-uri' + # + # class Ticker + # def get_year_end_closing(symbol, year, api_key) + # uri = "https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY&symbol=#{symbol}&apikey=#{api_key}&datatype=csv" + # data = [] + # csv = URI.parse(uri).read + # if csv.include?('call frequency') + # return :rate_limit_exceeded + # end + # CSV.parse(csv, headers: true) do |row| + # data << row['close'].to_f if row['timestamp'].include?(year.to_s) + # end + # year_end = data.first + # year_end + # rescue => e + # p e + # end + # end + # + # api_key = ENV['ALPHAVANTAGE_KEY'] + # abort(error_message) unless api_key + # + # # Future + # price = Concurrent::Future.execute{ Ticker.new.get_year_end_closing('TWTR', 2013, api_key) } + # price.state #=> :pending + # price.pending? #=> true + # price.value(0) #=> nil (does not block) + # + # sleep(1) # do other stuff + # + # price.value #=> 63.65 (after blocking if necessary) + # price.state #=> :fulfilled + # price.fulfilled? #=> true + # price.value #=> 63.65 + # + # @example Successful task execution + # + # task = Concurrent::ScheduledTask.new(2){ 'What does the fox say?' } + # task.state #=> :unscheduled + # task.execute + # task.state #=> pending + # + # # wait for it... + # sleep(3) + # + # task.unscheduled? #=> false + # task.pending? #=> false + # task.fulfilled? #=> true + # task.rejected? #=> false + # task.value #=> 'What does the fox say?' + # + # @example One line creation and execution + # + # task = Concurrent::ScheduledTask.new(2){ 'What does the fox say?' }.execute + # task.state #=> pending + # + # task = Concurrent::ScheduledTask.execute(2){ 'What do you get when you multiply 6 by 9?' } + # task.state #=> pending + # + # @example Failed task execution + # + # task = Concurrent::ScheduledTask.execute(2){ raise StandardError.new('Call me maybe?') } + # task.pending? #=> true + # + # # wait for it... + # sleep(3) + # + # task.unscheduled? #=> false + # task.pending? #=> false + # task.fulfilled? #=> false + # task.rejected? #=> true + # task.value #=> nil + # task.reason #=> # + # + # @example Task execution with observation + # + # observer = Class.new{ + # def update(time, value, reason) + # puts "The task completed at #{time} with value '#{value}'" + # end + # }.new + # + # task = Concurrent::ScheduledTask.new(2){ 'What does the fox say?' } + # task.add_observer(observer) + # task.execute + # task.pending? #=> true + # + # # wait for it... + # sleep(3) + # + # #>> The task completed at 2013-11-07 12:26:09 -0500 with value 'What does the fox say?' + # + # @!macro monotonic_clock_warning + # + # @see Concurrent.timer + class ScheduledTask < IVar + include Comparable + + # The executor on which to execute the task. + # @!visibility private + attr_reader :executor + + # Schedule a task for execution at a specified future time. + # + # @param [Float] delay the number of seconds to wait for before executing the task + # + # @yield the task to be performed + # + # @!macro executor_and_deref_options + # + # @option opts [object, Array] :args zero or more arguments to be passed the task + # block on execution + # + # @raise [ArgumentError] When no block is given + # @raise [ArgumentError] When given a time that is in the past + def initialize(delay, opts = {}, &task) + raise ArgumentError.new('no block given') unless block_given? + raise ArgumentError.new('seconds must be greater than zero') if delay.to_f < 0.0 + + super(NULL, opts, &nil) + + synchronize do + ns_set_state(:unscheduled) + @parent = opts.fetch(:timer_set, Concurrent.global_timer_set) + @args = get_arguments_from(opts) + @delay = delay.to_f + @task = task + @time = nil + @executor = Options.executor_from_options(opts) || Concurrent.global_io_executor + self.observers = Collection::CopyOnNotifyObserverSet.new + end + end + + # The `delay` value given at instanciation. + # + # @return [Float] the initial delay. + def initial_delay + synchronize { @delay } + end + + # The monotonic time at which the the task is scheduled to be executed. + # + # @return [Float] the schedule time or nil if `unscheduled` + def schedule_time + synchronize { @time } + end + + # Comparator which orders by schedule time. + # + # @!visibility private + def <=>(other) + schedule_time <=> other.schedule_time + end + + # Has the task been cancelled? + # + # @return [Boolean] true if the task is in the given state else false + def cancelled? + synchronize { ns_check_state?(:cancelled) } + end + + # In the task execution in progress? + # + # @return [Boolean] true if the task is in the given state else false + def processing? + synchronize { ns_check_state?(:processing) } + end + + # Cancel this task and prevent it from executing. A task can only be + # cancelled if it is pending or unscheduled. + # + # @return [Boolean] true if successfully cancelled else false + def cancel + if compare_and_set_state(:cancelled, :pending, :unscheduled) + complete(false, nil, CancelledOperationError.new) + # To avoid deadlocks this call must occur outside of #synchronize + # Changing the state above should prevent redundant calls + @parent.send(:remove_task, self) + else + false + end + end + + # Reschedule the task using the original delay and the current time. + # A task can only be reset while it is `:pending`. + # + # @return [Boolean] true if successfully rescheduled else false + def reset + synchronize{ ns_reschedule(@delay) } + end + + # Reschedule the task using the given delay and the current time. + # A task can only be reset while it is `:pending`. + # + # @param [Float] delay the number of seconds to wait for before executing the task + # + # @return [Boolean] true if successfully rescheduled else false + # + # @raise [ArgumentError] When given a time that is in the past + def reschedule(delay) + delay = delay.to_f + raise ArgumentError.new('seconds must be greater than zero') if delay < 0.0 + synchronize{ ns_reschedule(delay) } + end + + # Execute an `:unscheduled` `ScheduledTask`. Immediately sets the state to `:pending` + # and starts counting down toward execution. Does nothing if the `ScheduledTask` is + # in any state other than `:unscheduled`. + # + # @return [ScheduledTask] a reference to `self` + def execute + if compare_and_set_state(:pending, :unscheduled) + synchronize{ ns_schedule(@delay) } + end + self + end + + # Create a new `ScheduledTask` object with the given block, execute it, and return the + # `:pending` object. + # + # @param [Float] delay the number of seconds to wait for before executing the task + # + # @!macro executor_and_deref_options + # + # @return [ScheduledTask] the newly created `ScheduledTask` in the `:pending` state + # + # @raise [ArgumentError] if no block is given + def self.execute(delay, opts = {}, &task) + new(delay, opts, &task).execute + end + + # Execute the task. + # + # @!visibility private + def process_task + safe_execute(@task, @args) + end + + protected :set, :try_set, :fail, :complete + + protected + + # Schedule the task using the given delay and the current time. + # + # @param [Float] delay the number of seconds to wait for before executing the task + # + # @return [Boolean] true if successfully rescheduled else false + # + # @!visibility private + def ns_schedule(delay) + @delay = delay + @time = Concurrent.monotonic_time + @delay + @parent.send(:post_task, self) + end + + # Reschedule the task using the given delay and the current time. + # A task can only be reset while it is `:pending`. + # + # @param [Float] delay the number of seconds to wait for before executing the task + # + # @return [Boolean] true if successfully rescheduled else false + # + # @!visibility private + def ns_reschedule(delay) + return false unless ns_check_state?(:pending) + @parent.send(:remove_task, self) && ns_schedule(delay) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/set.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/set.rb new file mode 100644 index 0000000..3bf0c89 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/set.rb @@ -0,0 +1,74 @@ +require 'concurrent/utility/engine' +require 'concurrent/thread_safe/util' +require 'set' + +module Concurrent + + # @!macro concurrent_set + # + # A thread-safe subclass of Set. This version locks against the object + # itself for every method call, ensuring only one thread can be reading + # or writing at a time. This includes iteration methods like `#each`. + # + # @note `a += b` is **not** a **thread-safe** operation on + # `Concurrent::Set`. It reads Set `a`, then it creates new `Concurrent::Set` + # which is union of `a` and `b`, then it writes the union to `a`. + # The read and write are independent operations they do not form a single atomic + # operation therefore when two `+=` operations are executed concurrently updates + # may be lost. Use `#merge` instead. + # + # @see http://ruby-doc.org/stdlib-2.4.0/libdoc/set/rdoc/Set.html Ruby standard library `Set` + + # @!macro internal_implementation_note + SetImplementation = case + when Concurrent.on_cruby? + # The CRuby implementation of Set is written in Ruby itself and is + # not thread safe for certain methods. + require 'monitor' + require 'concurrent/thread_safe/util/data_structures' + + class CRubySet < ::Set + end + + ThreadSafe::Util.make_synchronized_on_cruby CRubySet + CRubySet + + when Concurrent.on_jruby? + require 'jruby/synchronized' + + class JRubySet < ::Set + include JRuby::Synchronized + end + + JRubySet + + when Concurrent.on_rbx? + require 'monitor' + require 'concurrent/thread_safe/util/data_structures' + + class RbxSet < ::Set + end + + ThreadSafe::Util.make_synchronized_on_rbx RbxSet + RbxSet + + when Concurrent.on_truffleruby? + require 'concurrent/thread_safe/util/data_structures' + + class TruffleRubySet < ::Set + end + + ThreadSafe::Util.make_synchronized_on_truffleruby TruffleRubySet + TruffleRubySet + + else + warn 'Possibly unsupported Ruby implementation' + ::Set + end + private_constant :SetImplementation + + # @!macro concurrent_set + class Set < SetImplementation + end +end + diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/settable_struct.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/settable_struct.rb new file mode 100644 index 0000000..0012352 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/settable_struct.rb @@ -0,0 +1,139 @@ +require 'concurrent/synchronization/abstract_struct' +require 'concurrent/errors' +require 'concurrent/synchronization' + +module Concurrent + + # An thread-safe, write-once variation of Ruby's standard `Struct`. + # Each member can have its value set at most once, either at construction + # or any time thereafter. Attempting to assign a value to a member + # that has already been set will result in a `Concurrent::ImmutabilityError`. + # + # @see http://ruby-doc.org/core/Struct.html Ruby standard library `Struct` + # @see http://en.wikipedia.org/wiki/Final_(Java) Java `final` keyword + module SettableStruct + include Synchronization::AbstractStruct + + # @!macro struct_values + def values + synchronize { ns_values } + end + alias_method :to_a, :values + + # @!macro struct_values_at + def values_at(*indexes) + synchronize { ns_values_at(indexes) } + end + + # @!macro struct_inspect + def inspect + synchronize { ns_inspect } + end + alias_method :to_s, :inspect + + # @!macro struct_merge + def merge(other, &block) + synchronize { ns_merge(other, &block) } + end + + # @!macro struct_to_h + def to_h + synchronize { ns_to_h } + end + + # @!macro struct_get + def [](member) + synchronize { ns_get(member) } + end + + # @!macro struct_equality + def ==(other) + synchronize { ns_equality(other) } + end + + # @!macro struct_each + def each(&block) + return enum_for(:each) unless block_given? + synchronize { ns_each(&block) } + end + + # @!macro struct_each_pair + def each_pair(&block) + return enum_for(:each_pair) unless block_given? + synchronize { ns_each_pair(&block) } + end + + # @!macro struct_select + def select(&block) + return enum_for(:select) unless block_given? + synchronize { ns_select(&block) } + end + + # @!macro struct_set + # + # @raise [Concurrent::ImmutabilityError] if the given member has already been set + def []=(member, value) + if member.is_a? Integer + length = synchronize { @values.length } + if member >= length + raise IndexError.new("offset #{member} too large for struct(size:#{length})") + end + synchronize do + unless @values[member].nil? + raise Concurrent::ImmutabilityError.new('struct member has already been set') + end + @values[member] = value + end + else + send("#{member}=", value) + end + rescue NoMethodError + raise NameError.new("no member '#{member}' in struct") + end + + private + + # @!visibility private + def initialize_copy(original) + synchronize do + super(original) + ns_initialize_copy + end + end + + # @!macro struct_new + def self.new(*args, &block) + clazz_name = nil + if args.length == 0 + raise ArgumentError.new('wrong number of arguments (0 for 1+)') + elsif args.length > 0 && args.first.is_a?(String) + clazz_name = args.shift + end + FACTORY.define_struct(clazz_name, args, &block) + end + + FACTORY = Class.new(Synchronization::LockableObject) do + def define_struct(name, members, &block) + synchronize do + clazz = Synchronization::AbstractStruct.define_struct_class(SettableStruct, Synchronization::LockableObject, name, members, &block) + members.each_with_index do |member, index| + clazz.send :remove_method, member if clazz.instance_methods.include? member + clazz.send(:define_method, member) do + synchronize { @values[index] } + end + clazz.send(:define_method, "#{member}=") do |value| + synchronize do + unless @values[index].nil? + raise Concurrent::ImmutabilityError.new('struct member has already been set') + end + @values[index] = value + end + end + end + clazz + end + end + end.new + private_constant :FACTORY + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization.rb new file mode 100644 index 0000000..49c68eb --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization.rb @@ -0,0 +1,30 @@ +require 'concurrent/utility/engine' + +require 'concurrent/synchronization/abstract_object' +require 'concurrent/utility/native_extension_loader' # load native parts first +Concurrent.load_native_extensions + +require 'concurrent/synchronization/mri_object' +require 'concurrent/synchronization/jruby_object' +require 'concurrent/synchronization/rbx_object' +require 'concurrent/synchronization/truffleruby_object' +require 'concurrent/synchronization/object' +require 'concurrent/synchronization/volatile' + +require 'concurrent/synchronization/abstract_lockable_object' +require 'concurrent/synchronization/mutex_lockable_object' +require 'concurrent/synchronization/jruby_lockable_object' +require 'concurrent/synchronization/rbx_lockable_object' + +require 'concurrent/synchronization/lockable_object' + +require 'concurrent/synchronization/condition' +require 'concurrent/synchronization/lock' + +module Concurrent + # {include:file:docs-source/synchronization.md} + # {include:file:docs-source/synchronization-notes.md} + module Synchronization + end +end + diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb new file mode 100644 index 0000000..bc12603 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb @@ -0,0 +1,98 @@ +module Concurrent + module Synchronization + + # @!visibility private + class AbstractLockableObject < Synchronization::Object + + protected + + # @!macro synchronization_object_method_synchronize + # + # @yield runs the block synchronized against this object, + # equivalent of java's `synchronize(this) {}` + # @note can by made public in descendants if required by `public :synchronize` + def synchronize + raise NotImplementedError + end + + # @!macro synchronization_object_method_ns_wait_until + # + # Wait until condition is met or timeout passes, + # protects against spurious wake-ups. + # @param [Numeric, nil] timeout in seconds, `nil` means no timeout + # @yield condition to be met + # @yieldreturn [true, false] + # @return [true, false] if condition met + # @note only to be used inside synchronized block + # @note to provide direct access to this method in a descendant add method + # ``` + # def wait_until(timeout = nil, &condition) + # synchronize { ns_wait_until(timeout, &condition) } + # end + # ``` + def ns_wait_until(timeout = nil, &condition) + if timeout + wait_until = Concurrent.monotonic_time + timeout + loop do + now = Concurrent.monotonic_time + condition_result = condition.call + return condition_result if now >= wait_until || condition_result + ns_wait wait_until - now + end + else + ns_wait timeout until condition.call + true + end + end + + # @!macro synchronization_object_method_ns_wait + # + # Wait until another thread calls #signal or #broadcast, + # spurious wake-ups can happen. + # + # @param [Numeric, nil] timeout in seconds, `nil` means no timeout + # @return [self] + # @note only to be used inside synchronized block + # @note to provide direct access to this method in a descendant add method + # ``` + # def wait(timeout = nil) + # synchronize { ns_wait(timeout) } + # end + # ``` + def ns_wait(timeout = nil) + raise NotImplementedError + end + + # @!macro synchronization_object_method_ns_signal + # + # Signal one waiting thread. + # @return [self] + # @note only to be used inside synchronized block + # @note to provide direct access to this method in a descendant add method + # ``` + # def signal + # synchronize { ns_signal } + # end + # ``` + def ns_signal + raise NotImplementedError + end + + # @!macro synchronization_object_method_ns_broadcast + # + # Broadcast to all waiting threads. + # @return [self] + # @note only to be used inside synchronized block + # @note to provide direct access to this method in a descendant add method + # ``` + # def broadcast + # synchronize { ns_broadcast } + # end + # ``` + def ns_broadcast + raise NotImplementedError + end + + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb new file mode 100644 index 0000000..532388b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb @@ -0,0 +1,24 @@ +module Concurrent + module Synchronization + + # @!visibility private + # @!macro internal_implementation_note + class AbstractObject + + # @abstract has to be implemented based on Ruby runtime + def initialize + raise NotImplementedError + end + + # @!visibility private + # @abstract + def full_memory_barrier + raise NotImplementedError + end + + def self.attr_volatile(*names) + raise NotImplementedError + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb new file mode 100644 index 0000000..1fe90c1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb @@ -0,0 +1,171 @@ +module Concurrent + module Synchronization + + # @!visibility private + # @!macro internal_implementation_note + module AbstractStruct + + # @!visibility private + def initialize(*values) + super() + ns_initialize(*values) + end + + # @!macro struct_length + # + # Returns the number of struct members. + # + # @return [Fixnum] the number of struct members + def length + self.class::MEMBERS.length + end + alias_method :size, :length + + # @!macro struct_members + # + # Returns the struct members as an array of symbols. + # + # @return [Array] the struct members as an array of symbols + def members + self.class::MEMBERS.dup + end + + protected + + # @!macro struct_values + # + # @!visibility private + def ns_values + @values.dup + end + + # @!macro struct_values_at + # + # @!visibility private + def ns_values_at(indexes) + @values.values_at(*indexes) + end + + # @!macro struct_to_h + # + # @!visibility private + def ns_to_h + length.times.reduce({}){|memo, i| memo[self.class::MEMBERS[i]] = @values[i]; memo} + end + + # @!macro struct_get + # + # @!visibility private + def ns_get(member) + if member.is_a? Integer + if member >= @values.length + raise IndexError.new("offset #{member} too large for struct(size:#{@values.length})") + end + @values[member] + else + send(member) + end + rescue NoMethodError + raise NameError.new("no member '#{member}' in struct") + end + + # @!macro struct_equality + # + # @!visibility private + def ns_equality(other) + self.class == other.class && self.values == other.values + end + + # @!macro struct_each + # + # @!visibility private + def ns_each + values.each{|value| yield value } + end + + # @!macro struct_each_pair + # + # @!visibility private + def ns_each_pair + @values.length.times do |index| + yield self.class::MEMBERS[index], @values[index] + end + end + + # @!macro struct_select + # + # @!visibility private + def ns_select + values.select{|value| yield value } + end + + # @!macro struct_inspect + # + # @!visibility private + def ns_inspect + struct = pr_underscore(self.class.ancestors[1]) + clazz = ((self.class.to_s =~ /^#" + end + + # @!macro struct_merge + # + # @!visibility private + def ns_merge(other, &block) + self.class.new(*self.to_h.merge(other, &block).values) + end + + # @!visibility private + def ns_initialize_copy + @values = @values.map do |val| + begin + val.clone + rescue TypeError + val + end + end + end + + # @!visibility private + def pr_underscore(clazz) + word = clazz.to_s.dup # dup string to workaround JRuby 9.2.0.0 bug https://github.com/jruby/jruby/issues/5229 + word.gsub!(/::/, '/') + word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2') + word.gsub!(/([a-z\d])([A-Z])/,'\1_\2') + word.tr!("-", "_") + word.downcase! + word + end + + # @!visibility private + def self.define_struct_class(parent, base, name, members, &block) + clazz = Class.new(base || Object) do + include parent + self.const_set(:MEMBERS, members.collect{|member| member.to_s.to_sym}.freeze) + def ns_initialize(*values) + raise ArgumentError.new('struct size differs') if values.length > length + @values = values.fill(nil, values.length..length-1) + end + end + unless name.nil? + begin + parent.send :remove_const, name if parent.const_defined?(name, false) + parent.const_set(name, clazz) + clazz + rescue NameError + raise NameError.new("identifier #{name} needs to be constant") + end + end + members.each_with_index do |member, index| + clazz.send :remove_method, member if clazz.instance_methods.include? member + clazz.send(:define_method, member) do + @values[index] + end + end + clazz.class_exec(&block) unless block.nil? + clazz.singleton_class.send :alias_method, :[], :new + clazz + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/condition.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/condition.rb new file mode 100644 index 0000000..f704b81 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/condition.rb @@ -0,0 +1,60 @@ +module Concurrent + module Synchronization + + # @!visibility private + # TODO (pitr-ch 04-Dec-2016): should be in edge + class Condition < LockableObject + safe_initialization! + + # TODO (pitr 12-Sep-2015): locks two objects, improve + # TODO (pitr 26-Sep-2015): study + # http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/java/util/concurrent/locks/AbstractQueuedSynchronizer.java#AbstractQueuedSynchronizer.Node + + singleton_class.send :alias_method, :private_new, :new + private_class_method :new + + def initialize(lock) + super() + @Lock = lock + end + + def wait(timeout = nil) + @Lock.synchronize { ns_wait(timeout) } + end + + def ns_wait(timeout = nil) + synchronize { super(timeout) } + end + + def wait_until(timeout = nil, &condition) + @Lock.synchronize { ns_wait_until(timeout, &condition) } + end + + def ns_wait_until(timeout = nil, &condition) + synchronize { super(timeout, &condition) } + end + + def signal + @Lock.synchronize { ns_signal } + end + + def ns_signal + synchronize { super } + end + + def broadcast + @Lock.synchronize { ns_broadcast } + end + + def ns_broadcast + synchronize { super } + end + end + + class LockableObject < LockableObjectImplementation + def new_condition + Condition.private_new(self) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb new file mode 100644 index 0000000..359a032 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb @@ -0,0 +1,13 @@ +module Concurrent + module Synchronization + + if Concurrent.on_jruby? && Concurrent.java_extensions_loaded? + + # @!visibility private + # @!macro internal_implementation_note + class JRubyLockableObject < AbstractLockableObject + + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb new file mode 100644 index 0000000..da91ac5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb @@ -0,0 +1,45 @@ +module Concurrent + module Synchronization + + if Concurrent.on_jruby? && Concurrent.java_extensions_loaded? + + # @!visibility private + module JRubyAttrVolatile + def self.included(base) + base.extend(ClassMethods) + end + + module ClassMethods + def attr_volatile(*names) + names.each do |name| + + ivar = :"@volatile_#{name}" + + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{name} + instance_variable_get_volatile(:#{ivar}) + end + + def #{name}=(value) + instance_variable_set_volatile(:#{ivar}, value) + end + RUBY + + end + names.map { |n| [n, :"#{n}="] }.flatten + end + end + end + + # @!visibility private + # @!macro internal_implementation_note + class JRubyObject < AbstractObject + include JRubyAttrVolatile + + def initialize + # nothing to do + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/lock.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/lock.rb new file mode 100644 index 0000000..0dbad2e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/lock.rb @@ -0,0 +1,36 @@ +module Concurrent + module Synchronization + + # @!visibility private + # TODO (pitr-ch 04-Dec-2016): should be in edge + class Lock < LockableObject + # TODO use JavaReentrantLock on JRuby + + public :synchronize + + def wait(timeout = nil) + synchronize { ns_wait(timeout) } + end + + public :ns_wait + + def wait_until(timeout = nil, &condition) + synchronize { ns_wait_until(timeout, &condition) } + end + + public :ns_wait_until + + def signal + synchronize { ns_signal } + end + + public :ns_signal + + def broadcast + synchronize { ns_broadcast } + end + + public :ns_broadcast + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb new file mode 100644 index 0000000..ae28e5b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb @@ -0,0 +1,72 @@ +module Concurrent + module Synchronization + + # @!visibility private + # @!macro internal_implementation_note + LockableObjectImplementation = case + when Concurrent.on_cruby? + MutexLockableObject + when Concurrent.on_jruby? + JRubyLockableObject + when Concurrent.on_rbx? + RbxLockableObject + when Concurrent.on_truffleruby? + MutexLockableObject + else + warn 'Possibly unsupported Ruby implementation' + MonitorLockableObject + end + private_constant :LockableObjectImplementation + + # Safe synchronization under any Ruby implementation. + # It provides methods like {#synchronize}, {#wait}, {#signal} and {#broadcast}. + # Provides a single layer which can improve its implementation over time without changes needed to + # the classes using it. Use {Synchronization::Object} not this abstract class. + # + # @note this object does not support usage together with + # [`Thread#wakeup`](http://ruby-doc.org/core/Thread.html#method-i-wakeup) + # and [`Thread#raise`](http://ruby-doc.org/core/Thread.html#method-i-raise). + # `Thread#sleep` and `Thread#wakeup` will work as expected but mixing `Synchronization::Object#wait` and + # `Thread#wakeup` will not work on all platforms. + # + # @see Event implementation as an example of this class use + # + # @example simple + # class AnClass < Synchronization::Object + # def initialize + # super + # synchronize { @value = 'asd' } + # end + # + # def value + # synchronize { @value } + # end + # end + # + # @!visibility private + class LockableObject < LockableObjectImplementation + + # TODO (pitr 12-Sep-2015): make private for c-r, prohibit subclassing + # TODO (pitr 12-Sep-2015): we inherit too much ourselves :/ + + # @!method initialize(*args, &block) + # @!macro synchronization_object_method_initialize + + # @!method synchronize + # @!macro synchronization_object_method_synchronize + + # @!method wait_until(timeout = nil, &condition) + # @!macro synchronization_object_method_ns_wait_until + + # @!method wait(timeout = nil) + # @!macro synchronization_object_method_ns_wait + + # @!method signal + # @!macro synchronization_object_method_ns_signal + + # @!method broadcast + # @!macro synchronization_object_method_ns_broadcast + + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb new file mode 100644 index 0000000..4b1d6c2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb @@ -0,0 +1,44 @@ +module Concurrent + module Synchronization + + # @!visibility private + module MriAttrVolatile + def self.included(base) + base.extend(ClassMethods) + end + + module ClassMethods + def attr_volatile(*names) + names.each do |name| + ivar = :"@volatile_#{name}" + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{name} + #{ivar} + end + + def #{name}=(value) + #{ivar} = value + end + RUBY + end + names.map { |n| [n, :"#{n}="] }.flatten + end + end + + def full_memory_barrier + # relying on undocumented behavior of CRuby, GVL acquire has lock which ensures visibility of ivars + # https://github.com/ruby/ruby/blob/ruby_2_2/thread_pthread.c#L204-L211 + end + end + + # @!visibility private + # @!macro internal_implementation_note + class MriObject < AbstractObject + include MriAttrVolatile + + def initialize + # nothing to do + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb new file mode 100644 index 0000000..f17ea50 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb @@ -0,0 +1,88 @@ +module Concurrent + # noinspection RubyInstanceVariableNamingConvention + module Synchronization + + # @!visibility private + # @!macro internal_implementation_note + module ConditionSignalling + protected + + def ns_signal + @__Condition__.signal + self + end + + def ns_broadcast + @__Condition__.broadcast + self + end + end + + + # @!visibility private + # @!macro internal_implementation_note + class MutexLockableObject < AbstractLockableObject + include ConditionSignalling + + safe_initialization! + + def initialize(*defaults) + super(*defaults) + @__Lock__ = ::Mutex.new + @__Condition__ = ::ConditionVariable.new + end + + def initialize_copy(other) + super + @__Lock__ = ::Mutex.new + @__Condition__ = ::ConditionVariable.new + end + + protected + + def synchronize + if @__Lock__.owned? + yield + else + @__Lock__.synchronize { yield } + end + end + + def ns_wait(timeout = nil) + @__Condition__.wait @__Lock__, timeout + self + end + end + + # @!visibility private + # @!macro internal_implementation_note + class MonitorLockableObject < AbstractLockableObject + include ConditionSignalling + + safe_initialization! + + def initialize(*defaults) + super(*defaults) + @__Lock__ = ::Monitor.new + @__Condition__ = @__Lock__.new_cond + end + + def initialize_copy(other) + super + @__Lock__ = ::Monitor.new + @__Condition__ = @__Lock__.new_cond + end + + protected + + def synchronize # TODO may be a problem with lock.synchronize { lock.wait } + @__Lock__.synchronize { yield } + end + + def ns_wait(timeout = nil) + @__Condition__.wait timeout + self + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/object.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/object.rb new file mode 100644 index 0000000..0e62112 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/object.rb @@ -0,0 +1,183 @@ +module Concurrent + module Synchronization + + # @!visibility private + # @!macro internal_implementation_note + ObjectImplementation = case + when Concurrent.on_cruby? + MriObject + when Concurrent.on_jruby? + JRubyObject + when Concurrent.on_rbx? + RbxObject + when Concurrent.on_truffleruby? + TruffleRubyObject + else + warn 'Possibly unsupported Ruby implementation' + MriObject + end + private_constant :ObjectImplementation + + # Abstract object providing final, volatile, ans CAS extensions to build other concurrent abstractions. + # - final instance variables see {Object.safe_initialization!} + # - volatile instance variables see {Object.attr_volatile} + # - volatile instance variables see {Object.attr_atomic} + class Object < ObjectImplementation + # TODO make it a module if possible + + # @!method self.attr_volatile(*names) + # Creates methods for reading and writing (as `attr_accessor` does) to a instance variable with + # volatile (Java) semantic. The instance variable should be accessed only through generated methods. + # + # @param [::Array] names of the instance variables to be volatile + # @return [::Array] names of defined method names + + # Has to be called by children. + def initialize + super + __initialize_atomic_fields__ + end + + # By calling this method on a class, it and all its children are marked to be constructed safely. Meaning that + # all writes (ivar initializations) are made visible to all readers of newly constructed object. It ensures + # same behaviour as Java's final fields. + # @example + # class AClass < Concurrent::Synchronization::Object + # safe_initialization! + # + # def initialize + # @AFinalValue = 'value' # published safely, does not have to be synchronized + # end + # end + # @return [true] + def self.safe_initialization! + # define only once, and not again in children + return if safe_initialization? + + # @!visibility private + def self.new(*args, &block) + object = super(*args, &block) + ensure + object.full_memory_barrier if object + end + + @safe_initialization = true + end + + # @return [true, false] if this class is safely initialized. + def self.safe_initialization? + @safe_initialization = false unless defined? @safe_initialization + @safe_initialization || (superclass.respond_to?(:safe_initialization?) && superclass.safe_initialization?) + end + + # For testing purposes, quite slow. Injects assert code to new method which will raise if class instance contains + # any instance variables with CamelCase names and isn't {.safe_initialization?}. + # @raise when offend found + # @return [true] + def self.ensure_safe_initialization_when_final_fields_are_present + Object.class_eval do + def self.new(*args, &block) + object = super(*args, &block) + ensure + has_final_field = object.instance_variables.any? { |v| v.to_s =~ /^@[A-Z]/ } + if has_final_field && !safe_initialization? + raise "there was an instance of #{object.class} with final field but not marked with safe_initialization!" + end + end + end + true + end + + # Creates methods for reading and writing to a instance variable with + # volatile (Java) semantic as {.attr_volatile} does. + # The instance variable should be accessed oly through generated methods. + # This method generates following methods: `value`, `value=(new_value) #=> new_value`, + # `swap_value(new_value) #=> old_value`, + # `compare_and_set_value(expected, value) #=> true || false`, `update_value(&block)`. + # @param [::Array] names of the instance variables to be volatile with CAS. + # @return [::Array] names of defined method names. + # @!macro attr_atomic + # @!method $1 + # @return [Object] The $1. + # @!method $1=(new_$1) + # Set the $1. + # @return [Object] new_$1. + # @!method swap_$1(new_$1) + # Set the $1 to new_$1 and return the old $1. + # @return [Object] old $1 + # @!method compare_and_set_$1(expected_$1, new_$1) + # Sets the $1 to new_$1 if the current $1 is expected_$1 + # @return [true, false] + # @!method update_$1(&block) + # Updates the $1 using the block. + # @yield [Object] Calculate a new $1 using given (old) $1 + # @yieldparam [Object] old $1 + # @return [Object] new $1 + def self.attr_atomic(*names) + @__atomic_fields__ ||= [] + @__atomic_fields__ += names + safe_initialization! + define_initialize_atomic_fields + + names.each do |name| + ivar = :"@Atomic#{name.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }}" + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{name} + #{ivar}.get + end + + def #{name}=(value) + #{ivar}.set value + end + + def swap_#{name}(value) + #{ivar}.swap value + end + + def compare_and_set_#{name}(expected, value) + #{ivar}.compare_and_set expected, value + end + + def update_#{name}(&block) + #{ivar}.update(&block) + end + RUBY + end + names.flat_map { |n| [n, :"#{n}=", :"swap_#{n}", :"compare_and_set_#{n}", :"update_#{n}"] } + end + + # @param [true, false] inherited should inherited volatile with CAS fields be returned? + # @return [::Array] Returns defined volatile with CAS fields on this class. + def self.atomic_attributes(inherited = true) + @__atomic_fields__ ||= [] + ((superclass.atomic_attributes if superclass.respond_to?(:atomic_attributes) && inherited) || []) + @__atomic_fields__ + end + + # @return [true, false] is the attribute with name atomic? + def self.atomic_attribute?(name) + atomic_attributes.include? name + end + + private + + def self.define_initialize_atomic_fields + assignments = @__atomic_fields__.map do |name| + "@Atomic#{name.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }} = Concurrent::AtomicReference.new(nil)" + end.join("\n") + + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def __initialize_atomic_fields__ + super + #{assignments} + end + RUBY + end + + private_class_method :define_initialize_atomic_fields + + def __initialize_atomic_fields__ + end + + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb new file mode 100644 index 0000000..1c4697c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb @@ -0,0 +1,71 @@ +module Concurrent + module Synchronization + + # @!visibility private + # @!macro internal_implementation_note + class RbxLockableObject < AbstractLockableObject + safe_initialization! + + def initialize(*defaults) + super(*defaults) + @__Waiters__ = [] + @__owner__ = nil + end + + def initialize_copy(other) + super + @__Waiters__ = [] + @__owner__ = nil + end + + protected + + def synchronize(&block) + if @__owner__ == Thread.current + yield + else + result = nil + Rubinius.synchronize(self) do + begin + @__owner__ = Thread.current + result = yield + ensure + @__owner__ = nil + end + end + result + end + end + + def ns_wait(timeout = nil) + wchan = Rubinius::Channel.new + + begin + @__Waiters__.push wchan + Rubinius.unlock(self) + signaled = wchan.receive_timeout timeout + ensure + Rubinius.lock(self) + + if !signaled && !@__Waiters__.delete(wchan) + # we timed out, but got signaled afterwards, + # so pass that signal on to the next waiter + @__Waiters__.shift << true unless @__Waiters__.empty? + end + end + + self + end + + def ns_signal + @__Waiters__.shift << true unless @__Waiters__.empty? + self + end + + def ns_broadcast + @__Waiters__.shift << true until @__Waiters__.empty? + self + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb new file mode 100644 index 0000000..4b23f2a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb @@ -0,0 +1,49 @@ +module Concurrent + module Synchronization + + # @!visibility private + module RbxAttrVolatile + def self.included(base) + base.extend(ClassMethods) + end + + module ClassMethods + + def attr_volatile(*names) + names.each do |name| + ivar = :"@volatile_#{name}" + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{name} + Rubinius.memory_barrier + #{ivar} + end + + def #{name}=(value) + #{ivar} = value + Rubinius.memory_barrier + end + RUBY + end + names.map { |n| [n, :"#{n}="] }.flatten + end + + end + + def full_memory_barrier + # Rubinius instance variables are not volatile so we need to insert barrier + # TODO (pitr 26-Nov-2015): check comments like ^ + Rubinius.memory_barrier + end + end + + # @!visibility private + # @!macro internal_implementation_note + class RbxObject < AbstractObject + include RbxAttrVolatile + + def initialize + # nothing to do + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb new file mode 100644 index 0000000..3919c76 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb @@ -0,0 +1,47 @@ +module Concurrent + module Synchronization + + # @!visibility private + module TruffleRubyAttrVolatile + def self.included(base) + base.extend(ClassMethods) + end + + module ClassMethods + def attr_volatile(*names) + names.each do |name| + ivar = :"@volatile_#{name}" + + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{name} + full_memory_barrier + #{ivar} + end + + def #{name}=(value) + #{ivar} = value + full_memory_barrier + end + RUBY + end + + names.map { |n| [n, :"#{n}="] }.flatten + end + end + + def full_memory_barrier + TruffleRuby.full_memory_barrier + end + end + + # @!visibility private + # @!macro internal_implementation_note + class TruffleRubyObject < AbstractObject + include TruffleRubyAttrVolatile + + def initialize + # nothing to do + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/volatile.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/volatile.rb new file mode 100644 index 0000000..9dffa91 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/synchronization/volatile.rb @@ -0,0 +1,36 @@ +module Concurrent + module Synchronization + + # Volatile adds the attr_volatile class method when included. + # + # @example + # class Foo + # include Concurrent::Synchronization::Volatile + # + # attr_volatile :bar + # + # def initialize + # self.bar = 1 + # end + # end + # + # foo = Foo.new + # foo.bar + # => 1 + # foo.bar = 2 + # => 2 + + Volatile = case + when Concurrent.on_cruby? + MriAttrVolatile + when Concurrent.on_jruby? + JRubyAttrVolatile + when Concurrent.on_rbx? + RbxAttrVolatile + when Concurrent.on_truffleruby? + TruffleRubyAttrVolatile + else + MriAttrVolatile + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb new file mode 100644 index 0000000..92e7c45 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb @@ -0,0 +1,50 @@ +require 'delegate' +require 'monitor' + +module Concurrent + unless defined?(SynchronizedDelegator) + + # This class provides a trivial way to synchronize all calls to a given object + # by wrapping it with a `Delegator` that performs `Monitor#enter/exit` calls + # around the delegated `#send`. Example: + # + # array = [] # not thread-safe on many impls + # array = SynchronizedDelegator.new([]) # thread-safe + # + # A simple `Monitor` provides a very coarse-grained way to synchronize a given + # object, in that it will cause synchronization for methods that have no need + # for it, but this is a trivial way to get thread-safety where none may exist + # currently on some implementations. + # + # This class is currently being considered for inclusion into stdlib, via + # https://bugs.ruby-lang.org/issues/8556 + # + # @!visibility private + class SynchronizedDelegator < SimpleDelegator + def setup + @old_abort = Thread.abort_on_exception + Thread.abort_on_exception = true + end + + def teardown + Thread.abort_on_exception = @old_abort + end + + def initialize(obj) + __setobj__(obj) + @monitor = Monitor.new + end + + def method_missing(method, *args, &block) + monitor = @monitor + begin + monitor.enter + super + ensure + monitor.exit + end + end + + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util.rb new file mode 100644 index 0000000..c67084a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util.rb @@ -0,0 +1,16 @@ +module Concurrent + + # @!visibility private + module ThreadSafe + + # @!visibility private + module Util + + # TODO (pitr-ch 15-Oct-2016): migrate to Utility::NativeInteger + FIXNUM_BIT_SIZE = (0.size * 8) - 2 + MAX_INT = (2 ** FIXNUM_BIT_SIZE) - 1 + # TODO (pitr-ch 15-Oct-2016): migrate to Utility::ProcessorCounter + CPU_COUNT = 16 # is there a way to determine this? + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util/adder.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util/adder.rb new file mode 100644 index 0000000..7a6e8d5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util/adder.rb @@ -0,0 +1,74 @@ +require 'concurrent/thread_safe/util' +require 'concurrent/thread_safe/util/striped64' + +module Concurrent + + # @!visibility private + module ThreadSafe + + # @!visibility private + module Util + + # A Ruby port of the Doug Lea's jsr166e.LondAdder class version 1.8 + # available in public domain. + # + # Original source code available here: + # http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/LongAdder.java?revision=1.8 + # + # One or more variables that together maintain an initially zero + # sum. When updates (method +add+) are contended across threads, + # the set of variables may grow dynamically to reduce contention. + # Method +sum+ returns the current total combined across the + # variables maintaining the sum. + # + # This class is usually preferable to single +Atomic+ reference when + # multiple threads update a common sum that is used for purposes such + # as collecting statistics, not for fine-grained synchronization + # control. Under low update contention, the two classes have similar + # characteristics. But under high contention, expected throughput of + # this class is significantly higher, at the expense of higher space + # consumption. + # + # @!visibility private + class Adder < Striped64 + # Adds the given value. + def add(x) + if (current_cells = cells) || !cas_base_computed {|current_base| current_base + x} + was_uncontended = true + hash = hash_code + unless current_cells && (cell = current_cells.volatile_get_by_hash(hash)) && (was_uncontended = cell.cas_computed {|current_value| current_value + x}) + retry_update(x, hash, was_uncontended) {|current_value| current_value + x} + end + end + end + + def increment + add(1) + end + + def decrement + add(-1) + end + + # Returns the current sum. The returned value is _NOT_ an + # atomic snapshot: Invocation in the absence of concurrent + # updates returns an accurate result, but concurrent updates that + # occur while the sum is being calculated might not be + # incorporated. + def sum + x = base + if current_cells = cells + current_cells.each do |cell| + x += cell.value if cell + end + end + x + end + + def reset + internal_reset(0) + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb new file mode 100644 index 0000000..d9b4c58 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb @@ -0,0 +1,118 @@ +require 'concurrent/thread_safe/util' +require 'concurrent/thread_safe/util/volatile' + +module Concurrent + + # @!visibility private + module ThreadSafe + + # @!visibility private + module Util + + # Provides a cheapest possible (mainly in terms of memory usage) +Mutex+ + # with the +ConditionVariable+ bundled in. + # + # Usage: + # class A + # include CheapLockable + # + # def do_exlusively + # cheap_synchronize { yield } + # end + # + # def wait_for_something + # cheap_synchronize do + # cheap_wait until resource_available? + # do_something + # cheap_broadcast # wake up others + # end + # end + # end + # + # @!visibility private + module CheapLockable + private + engine = defined?(RUBY_ENGINE) && RUBY_ENGINE + if engine == 'rbx' + # Making use of the Rubinius' ability to lock via object headers to avoid the overhead of the extra Mutex objects. + def cheap_synchronize + Rubinius.lock(self) + begin + yield + ensure + Rubinius.unlock(self) + end + end + + def cheap_wait + wchan = Rubinius::Channel.new + + begin + waiters = @waiters ||= [] + waiters.push wchan + Rubinius.unlock(self) + signaled = wchan.receive_timeout nil + ensure + Rubinius.lock(self) + + unless signaled or waiters.delete(wchan) + # we timed out, but got signaled afterwards (e.g. while waiting to + # acquire @lock), so pass that signal on to the next waiter + waiters.shift << true unless waiters.empty? + end + end + + self + end + + def cheap_broadcast + waiters = @waiters ||= [] + waiters.shift << true until waiters.empty? + self + end + elsif engine == 'jruby' + # Use Java's native synchronized (this) { wait(); notifyAll(); } to avoid the overhead of the extra Mutex objects + require 'jruby' + + def cheap_synchronize + JRuby.reference0(self).synchronized { yield } + end + + def cheap_wait + JRuby.reference0(self).wait + end + + def cheap_broadcast + JRuby.reference0(self).notify_all + end + else + require 'thread' + + extend Volatile + attr_volatile :mutex + + # Non-reentrant Mutex#syncrhonize + def cheap_synchronize + true until (my_mutex = mutex) || cas_mutex(nil, my_mutex = Mutex.new) + my_mutex.synchronize { yield } + end + + # Releases this object's +cheap_synchronize+ lock and goes to sleep waiting for other threads to +cheap_broadcast+, reacquires the lock on wakeup. + # Must only be called in +cheap_broadcast+'s block. + def cheap_wait + conditional_variable = @conditional_variable ||= ConditionVariable.new + conditional_variable.wait(mutex) + end + + # Wakes up all threads waiting for this object's +cheap_synchronize+ lock. + # Must only be called in +cheap_broadcast+'s block. + def cheap_broadcast + if conditional_variable = @conditional_variable + conditional_variable.broadcast + end + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb new file mode 100644 index 0000000..24d039b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb @@ -0,0 +1,88 @@ +require 'concurrent/thread_safe/util' + +# Shim for TruffleRuby.synchronized +if Concurrent.on_truffleruby? && !TruffleRuby.respond_to?(:synchronized) + module TruffleRuby + def self.synchronized(object, &block) + Truffle::System.synchronized(object, &block) + end + end +end + +module Concurrent + module ThreadSafe + module Util + def self.make_synchronized_on_cruby(klass) + klass.class_eval do + def initialize(*args, &block) + @_monitor = Monitor.new + super + end + + def initialize_copy(other) + # make sure a copy is not sharing a monitor with the original object! + @_monitor = Monitor.new + super + end + end + + klass.superclass.instance_methods(false).each do |method| + klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{method}(*args) + monitor = @_monitor + monitor or raise("BUG: Internal monitor was not properly initialized. Please report this to the concurrent-ruby developers.") + monitor.synchronize { super } + end + RUBY + end + end + + def self.make_synchronized_on_rbx(klass) + klass.class_eval do + private + + def _mon_initialize + @_monitor ||= Monitor.new # avoid double initialisation + end + + def self.new(*args) + obj = super(*args) + obj.send(:_mon_initialize) + obj + end + end + + klass.superclass.instance_methods(false).each do |method| + case method + when :new_range, :new_reserved + klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{method}(*args) + obj = super + obj.send(:_mon_initialize) + obj + end + RUBY + else + klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{method}(*args) + monitor = @_monitor + monitor or raise("BUG: Internal monitor was not properly initialized. Please report this to the concurrent-ruby developers.") + monitor.synchronize { super } + end + RUBY + end + end + end + + def self.make_synchronized_on_truffleruby(klass) + klass.superclass.instance_methods(false).each do |method| + klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{method}(*args, &block) + TruffleRuby.synchronized(self) { super(*args, &block) } + end + RUBY + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util/power_of_two_tuple.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util/power_of_two_tuple.rb new file mode 100644 index 0000000..b54be39 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util/power_of_two_tuple.rb @@ -0,0 +1,38 @@ +require 'concurrent/thread_safe/util' +require 'concurrent/tuple' + +module Concurrent + + # @!visibility private + module ThreadSafe + + # @!visibility private + module Util + + # @!visibility private + class PowerOfTwoTuple < Concurrent::Tuple + + def initialize(size) + raise ArgumentError, "size must be a power of 2 (#{size.inspect} provided)" unless size > 0 && size & (size - 1) == 0 + super(size) + end + + def hash_to_index(hash) + (size - 1) & hash + end + + def volatile_get_by_hash(hash) + volatile_get(hash_to_index(hash)) + end + + def volatile_set_by_hash(hash, value) + volatile_set(hash_to_index(hash), value) + end + + def next_in_size_table + self.class.new(size << 1) + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util/striped64.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util/striped64.rb new file mode 100644 index 0000000..4169c3d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util/striped64.rb @@ -0,0 +1,246 @@ +require 'concurrent/thread_safe/util' +require 'concurrent/thread_safe/util/power_of_two_tuple' +require 'concurrent/thread_safe/util/volatile' +require 'concurrent/thread_safe/util/xor_shift_random' + +module Concurrent + + # @!visibility private + module ThreadSafe + + # @!visibility private + module Util + + # A Ruby port of the Doug Lea's jsr166e.Striped64 class version 1.6 + # available in public domain. + # + # Original source code available here: + # http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/Striped64.java?revision=1.6 + # + # Class holding common representation and mechanics for classes supporting + # dynamic striping on 64bit values. + # + # This class maintains a lazily-initialized table of atomically updated + # variables, plus an extra +base+ field. The table size is a power of two. + # Indexing uses masked per-thread hash codes. Nearly all methods on this + # class are private, accessed directly by subclasses. + # + # Table entries are of class +Cell+; a variant of AtomicLong padded to + # reduce cache contention on most processors. Padding is overkill for most + # Atomics because they are usually irregularly scattered in memory and thus + # don't interfere much with each other. But Atomic objects residing in + # arrays will tend to be placed adjacent to each other, and so will most + # often share cache lines (with a huge negative performance impact) without + # this precaution. + # + # In part because +Cell+s are relatively large, we avoid creating them until + # they are needed. When there is no contention, all updates are made to the + # +base+ field. Upon first contention (a failed CAS on +base+ update), the + # table is initialized to size 2. The table size is doubled upon further + # contention until reaching the nearest power of two greater than or equal + # to the number of CPUS. Table slots remain empty (+nil+) until they are + # needed. + # + # A single spinlock (+busy+) is used for initializing and resizing the + # table, as well as populating slots with new +Cell+s. There is no need for + # a blocking lock: When the lock is not available, threads try other slots + # (or the base). During these retries, there is increased contention and + # reduced locality, which is still better than alternatives. + # + # Per-thread hash codes are initialized to random values. Contention and/or + # table collisions are indicated by failed CASes when performing an update + # operation (see method +retry_update+). Upon a collision, if the table size + # is less than the capacity, it is doubled in size unless some other thread + # holds the lock. If a hashed slot is empty, and lock is available, a new + # +Cell+ is created. Otherwise, if the slot exists, a CAS is tried. Retries + # proceed by "double hashing", using a secondary hash (XorShift) to try to + # find a free slot. + # + # The table size is capped because, when there are more threads than CPUs, + # supposing that each thread were bound to a CPU, there would exist a + # perfect hash function mapping threads to slots that eliminates collisions. + # When we reach capacity, we search for this mapping by randomly varying the + # hash codes of colliding threads. Because search is random, and collisions + # only become known via CAS failures, convergence can be slow, and because + # threads are typically not bound to CPUS forever, may not occur at all. + # However, despite these limitations, observed contention rates are + # typically low in these cases. + # + # It is possible for a +Cell+ to become unused when threads that once hashed + # to it terminate, as well as in the case where doubling the table causes no + # thread to hash to it under expanded mask. We do not try to detect or + # remove such cells, under the assumption that for long-running instances, + # observed contention levels will recur, so the cells will eventually be + # needed again; and for short-lived ones, it does not matter. + # + # @!visibility private + class Striped64 + + # Padded variant of AtomicLong supporting only raw accesses plus CAS. + # The +value+ field is placed between pads, hoping that the JVM doesn't + # reorder them. + # + # Optimisation note: It would be possible to use a release-only + # form of CAS here, if it were provided. + # + # @!visibility private + class Cell < Concurrent::AtomicReference + + alias_method :cas, :compare_and_set + + def cas_computed + cas(current_value = value, yield(current_value)) + end + + # @!visibility private + def self.padding + # TODO: this only adds padding after the :value slot, need to find a way to add padding before the slot + # TODO (pitr-ch 28-Jul-2018): the padding instance vars may not be created + # hide from yardoc in a method + attr_reader :padding_0, :padding_1, :padding_2, :padding_3, :padding_4, :padding_5, :padding_6, :padding_7, :padding_8, :padding_9, :padding_10, :padding_11 + end + padding + end + + extend Volatile + attr_volatile :cells, # Table of cells. When non-null, size is a power of 2. + :base, # Base value, used mainly when there is no contention, but also as a fallback during table initialization races. Updated via CAS. + :busy # Spinlock (locked via CAS) used when resizing and/or creating Cells. + + alias_method :busy?, :busy + + def initialize + super() + self.busy = false + self.base = 0 + end + + # Handles cases of updates involving initialization, resizing, + # creating new Cells, and/or contention. See above for + # explanation. This method suffers the usual non-modularity + # problems of optimistic retry code, relying on rechecked sets of + # reads. + # + # Arguments: + # [+x+] + # the value + # [+hash_code+] + # hash code used + # [+x+] + # false if CAS failed before call + def retry_update(x, hash_code, was_uncontended) # :yields: current_value + hash = hash_code + collided = false # True if last slot nonempty + while true + if current_cells = cells + if !(cell = current_cells.volatile_get_by_hash(hash)) + if busy? + collided = false + else # Try to attach new Cell + if try_to_install_new_cell(Cell.new(x), hash) # Optimistically create and try to insert new cell + break + else + redo # Slot is now non-empty + end + end + elsif !was_uncontended # CAS already known to fail + was_uncontended = true # Continue after rehash + elsif cell.cas_computed {|current_value| yield current_value} + break + elsif current_cells.size >= CPU_COUNT || cells != current_cells # At max size or stale + collided = false + elsif collided && expand_table_unless_stale(current_cells) + collided = false + redo # Retry with expanded table + else + collided = true + end + hash = XorShiftRandom.xorshift(hash) + + elsif try_initialize_cells(x, hash) || cas_base_computed {|current_base| yield current_base} + break + end + end + self.hash_code = hash + end + + private + # Static per-thread hash code key. Shared across all instances to + # reduce Thread locals pollution and because adjustments due to + # collisions in one table are likely to be appropriate for + # others. + THREAD_LOCAL_KEY = "#{name}.hash_code".to_sym + + # A thread-local hash code accessor. The code is initially + # random, but may be set to a different value upon collisions. + def hash_code + Thread.current[THREAD_LOCAL_KEY] ||= XorShiftRandom.get + end + + def hash_code=(hash) + Thread.current[THREAD_LOCAL_KEY] = hash + end + + # Sets base and all +cells+ to the given value. + def internal_reset(initial_value) + current_cells = cells + self.base = initial_value + if current_cells + current_cells.each do |cell| + cell.value = initial_value if cell + end + end + end + + def cas_base_computed + cas_base(current_base = base, yield(current_base)) + end + + def free? + !busy? + end + + def try_initialize_cells(x, hash) + if free? && !cells + try_in_busy do + unless cells # Recheck under lock + new_cells = PowerOfTwoTuple.new(2) + new_cells.volatile_set_by_hash(hash, Cell.new(x)) + self.cells = new_cells + end + end + end + end + + def expand_table_unless_stale(current_cells) + try_in_busy do + if current_cells == cells # Recheck under lock + new_cells = current_cells.next_in_size_table + current_cells.each_with_index {|x, i| new_cells.volatile_set(i, x)} + self.cells = new_cells + end + end + end + + def try_to_install_new_cell(new_cell, hash) + try_in_busy do + # Recheck under lock + if (current_cells = cells) && !current_cells.volatile_get(i = current_cells.hash_to_index(hash)) + current_cells.volatile_set(i, new_cell) + end + end + end + + def try_in_busy + if cas_busy(false, true) + begin + yield + ensure + self.busy = false + end + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util/volatile.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util/volatile.rb new file mode 100644 index 0000000..cdac2a3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util/volatile.rb @@ -0,0 +1,75 @@ +require 'concurrent/thread_safe/util' + +module Concurrent + + # @!visibility private + module ThreadSafe + + # @!visibility private + module Util + + # @!visibility private + module Volatile + + # Provides +volatile+ (in the JVM's sense) attribute accessors implemented + # atop of +Concurrent::AtomicReference+. + # + # Usage: + # class Foo + # extend Concurrent::ThreadSafe::Util::Volatile + # attr_volatile :foo, :bar + # + # def initialize(bar) + # super() # must super() into parent initializers before using the volatile attribute accessors + # self.bar = bar + # end + # + # def hello + # my_foo = foo # volatile read + # self.foo = 1 # volatile write + # cas_foo(1, 2) # => true | a strong CAS + # end + # end + def attr_volatile(*attr_names) + return if attr_names.empty? + include(Module.new do + atomic_ref_setup = attr_names.map {|attr_name| "@__#{attr_name} = Concurrent::AtomicReference.new"} + initialize_copy_setup = attr_names.zip(atomic_ref_setup).map do |attr_name, ref_setup| + "#{ref_setup}(other.instance_variable_get(:@__#{attr_name}).get)" + end + class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 + def initialize(*) + super + #{atomic_ref_setup.join('; ')} + end + + def initialize_copy(other) + super + #{initialize_copy_setup.join('; ')} + end + RUBY_EVAL + + attr_names.each do |attr_name| + class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 + def #{attr_name} + @__#{attr_name}.get + end + + def #{attr_name}=(value) + @__#{attr_name}.set(value) + end + + def compare_and_set_#{attr_name}(old_value, new_value) + @__#{attr_name}.compare_and_set(old_value, new_value) + end + RUBY_EVAL + + alias_method :"cas_#{attr_name}", :"compare_and_set_#{attr_name}" + alias_method :"lazy_set_#{attr_name}", :"#{attr_name}=" + end + end) + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb new file mode 100644 index 0000000..bdde2dd --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb @@ -0,0 +1,50 @@ +require 'concurrent/thread_safe/util' + +module Concurrent + + # @!visibility private + module ThreadSafe + + # @!visibility private + module Util + + # A xorshift random number (positive +Fixnum+s) generator, provides + # reasonably cheap way to generate thread local random numbers without + # contending for the global +Kernel.rand+. + # + # Usage: + # x = XorShiftRandom.get # uses Kernel.rand to generate an initial seed + # while true + # if (x = XorShiftRandom.xorshift).odd? # thread-localy generate a next random number + # do_something_at_random + # end + # end + module XorShiftRandom + extend self + MAX_XOR_SHIFTABLE_INT = MAX_INT - 1 + + # Generates an initial non-zero positive +Fixnum+ via +Kernel.rand+. + def get + Kernel.rand(MAX_XOR_SHIFTABLE_INT) + 1 # 0 can't be xorshifted + end + + # xorshift based on: http://www.jstatsoft.org/v08/i14/paper + if 0.size == 4 + # using the "yˆ=y>>a; yˆ=y<>c;" transform with the (a,b,c) tuple with values (3,1,14) to minimise Bignum overflows + def xorshift(x) + x ^= x >> 3 + x ^= (x << 1) & MAX_INT # cut-off Bignum overflow + x ^= x >> 14 + end + else + # using the "yˆ=y>>a; yˆ=y<>c;" transform with the (a,b,c) tuple with values (1,1,54) to minimise Bignum overflows + def xorshift(x) + x ^= x >> 1 + x ^= (x << 1) & MAX_INT # cut-off Bignum overflow + x ^= x >> 54 + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/timer_task.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/timer_task.rb new file mode 100644 index 0000000..b69cfc8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/timer_task.rb @@ -0,0 +1,311 @@ +require 'concurrent/collection/copy_on_notify_observer_set' +require 'concurrent/concern/dereferenceable' +require 'concurrent/concern/observable' +require 'concurrent/atomic/atomic_boolean' +require 'concurrent/executor/executor_service' +require 'concurrent/executor/ruby_executor_service' +require 'concurrent/executor/safe_task_executor' +require 'concurrent/scheduled_task' + +module Concurrent + + # A very common concurrency pattern is to run a thread that performs a task at + # regular intervals. The thread that performs the task sleeps for the given + # interval then wakes up and performs the task. Lather, rinse, repeat... This + # pattern causes two problems. First, it is difficult to test the business + # logic of the task because the task itself is tightly coupled with the + # concurrency logic. Second, an exception raised while performing the task can + # cause the entire thread to abend. In a long-running application where the + # task thread is intended to run for days/weeks/years a crashed task thread + # can pose a significant problem. `TimerTask` alleviates both problems. + # + # When a `TimerTask` is launched it starts a thread for monitoring the + # execution interval. The `TimerTask` thread does not perform the task, + # however. Instead, the TimerTask launches the task on a separate thread. + # Should the task experience an unrecoverable crash only the task thread will + # crash. This makes the `TimerTask` very fault tolerant. Additionally, the + # `TimerTask` thread can respond to the success or failure of the task, + # performing logging or ancillary operations. + # + # One other advantage of `TimerTask` is that it forces the business logic to + # be completely decoupled from the concurrency logic. The business logic can + # be tested separately then passed to the `TimerTask` for scheduling and + # running. + # + # In some cases it may be necessary for a `TimerTask` to affect its own + # execution cycle. To facilitate this, a reference to the TimerTask instance + # is passed as an argument to the provided block every time the task is + # executed. + # + # The `TimerTask` class includes the `Dereferenceable` mixin module so the + # result of the last execution is always available via the `#value` method. + # Dereferencing options can be passed to the `TimerTask` during construction or + # at any later time using the `#set_deref_options` method. + # + # `TimerTask` supports notification through the Ruby standard library + # {http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html + # Observable} module. On execution the `TimerTask` will notify the observers + # with three arguments: time of execution, the result of the block (or nil on + # failure), and any raised exceptions (or nil on success). + # + # @!macro copy_options + # + # @example Basic usage + # task = Concurrent::TimerTask.new{ puts 'Boom!' } + # task.execute + # + # task.execution_interval #=> 60 (default) + # + # # wait 60 seconds... + # #=> 'Boom!' + # + # task.shutdown #=> true + # + # @example Configuring `:execution_interval` + # task = Concurrent::TimerTask.new(execution_interval: 5) do + # puts 'Boom!' + # end + # + # task.execution_interval #=> 5 + # + # @example Immediate execution with `:run_now` + # task = Concurrent::TimerTask.new(run_now: true){ puts 'Boom!' } + # task.execute + # + # #=> 'Boom!' + # + # @example Last `#value` and `Dereferenceable` mixin + # task = Concurrent::TimerTask.new( + # dup_on_deref: true, + # execution_interval: 5 + # ){ Time.now } + # + # task.execute + # Time.now #=> 2013-11-07 18:06:50 -0500 + # sleep(10) + # task.value #=> 2013-11-07 18:06:55 -0500 + # + # @example Controlling execution from within the block + # timer_task = Concurrent::TimerTask.new(execution_interval: 1) do |task| + # task.execution_interval.times{ print 'Boom! ' } + # print "\n" + # task.execution_interval += 1 + # if task.execution_interval > 5 + # puts 'Stopping...' + # task.shutdown + # end + # end + # + # timer_task.execute # blocking call - this task will stop itself + # #=> Boom! + # #=> Boom! Boom! + # #=> Boom! Boom! Boom! + # #=> Boom! Boom! Boom! Boom! + # #=> Boom! Boom! Boom! Boom! Boom! + # #=> Stopping... + # + # @example Observation + # class TaskObserver + # def update(time, result, ex) + # if result + # print "(#{time}) Execution successfully returned #{result}\n" + # else + # print "(#{time}) Execution failed with error #{ex}\n" + # end + # end + # end + # + # task = Concurrent::TimerTask.new(execution_interval: 1){ 42 } + # task.add_observer(TaskObserver.new) + # task.execute + # sleep 4 + # + # #=> (2013-10-13 19:08:58 -0400) Execution successfully returned 42 + # #=> (2013-10-13 19:08:59 -0400) Execution successfully returned 42 + # #=> (2013-10-13 19:09:00 -0400) Execution successfully returned 42 + # task.shutdown + # + # task = Concurrent::TimerTask.new(execution_interval: 1){ sleep } + # task.add_observer(TaskObserver.new) + # task.execute + # + # #=> (2013-10-13 19:07:25 -0400) Execution timed out + # #=> (2013-10-13 19:07:27 -0400) Execution timed out + # #=> (2013-10-13 19:07:29 -0400) Execution timed out + # task.shutdown + # + # task = Concurrent::TimerTask.new(execution_interval: 1){ raise StandardError } + # task.add_observer(TaskObserver.new) + # task.execute + # + # #=> (2013-10-13 19:09:37 -0400) Execution failed with error StandardError + # #=> (2013-10-13 19:09:38 -0400) Execution failed with error StandardError + # #=> (2013-10-13 19:09:39 -0400) Execution failed with error StandardError + # task.shutdown + # + # @see http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html + # @see http://docs.oracle.com/javase/7/docs/api/java/util/TimerTask.html + class TimerTask < RubyExecutorService + include Concern::Dereferenceable + include Concern::Observable + + # Default `:execution_interval` in seconds. + EXECUTION_INTERVAL = 60 + + # Default `:timeout_interval` in seconds. + TIMEOUT_INTERVAL = 30 + + # Create a new TimerTask with the given task and configuration. + # + # @!macro timer_task_initialize + # @param [Hash] opts the options defining task execution. + # @option opts [Integer] :execution_interval number of seconds between + # task executions (default: EXECUTION_INTERVAL) + # @option opts [Boolean] :run_now Whether to run the task immediately + # upon instantiation or to wait until the first # execution_interval + # has passed (default: false) + # + # @!macro deref_options + # + # @raise ArgumentError when no block is given. + # + # @yield to the block after :execution_interval seconds have passed since + # the last yield + # @yieldparam task a reference to the `TimerTask` instance so that the + # block can control its own lifecycle. Necessary since `self` will + # refer to the execution context of the block rather than the running + # `TimerTask`. + # + # @return [TimerTask] the new `TimerTask` + def initialize(opts = {}, &task) + raise ArgumentError.new('no block given') unless block_given? + super + set_deref_options opts + end + + # Is the executor running? + # + # @return [Boolean] `true` when running, `false` when shutting down or shutdown + def running? + @running.true? + end + + # Execute a previously created `TimerTask`. + # + # @return [TimerTask] a reference to `self` + # + # @example Instance and execute in separate steps + # task = Concurrent::TimerTask.new(execution_interval: 10){ print "Hello World\n" } + # task.running? #=> false + # task.execute + # task.running? #=> true + # + # @example Instance and execute in one line + # task = Concurrent::TimerTask.new(execution_interval: 10){ print "Hello World\n" }.execute + # task.running? #=> true + def execute + synchronize do + if @running.false? + @running.make_true + schedule_next_task(@run_now ? 0 : @execution_interval) + end + end + self + end + + # Create and execute a new `TimerTask`. + # + # @!macro timer_task_initialize + # + # @example + # task = Concurrent::TimerTask.execute(execution_interval: 10){ print "Hello World\n" } + # task.running? #=> true + def self.execute(opts = {}, &task) + TimerTask.new(opts, &task).execute + end + + # @!attribute [rw] execution_interval + # @return [Fixnum] Number of seconds after the task completes before the + # task is performed again. + def execution_interval + synchronize { @execution_interval } + end + + # @!attribute [rw] execution_interval + # @return [Fixnum] Number of seconds after the task completes before the + # task is performed again. + def execution_interval=(value) + if (value = value.to_f) <= 0.0 + raise ArgumentError.new('must be greater than zero') + else + synchronize { @execution_interval = value } + end + end + + # @!attribute [rw] timeout_interval + # @return [Fixnum] Number of seconds the task can run before it is + # considered to have failed. + def timeout_interval + warn 'TimerTask timeouts are now ignored as these were not able to be implemented correctly' + end + + # @!attribute [rw] timeout_interval + # @return [Fixnum] Number of seconds the task can run before it is + # considered to have failed. + def timeout_interval=(value) + warn 'TimerTask timeouts are now ignored as these were not able to be implemented correctly' + end + + private :post, :<< + + private + + def ns_initialize(opts, &task) + set_deref_options(opts) + + self.execution_interval = opts[:execution] || opts[:execution_interval] || EXECUTION_INTERVAL + if opts[:timeout] || opts[:timeout_interval] + warn 'TimeTask timeouts are now ignored as these were not able to be implemented correctly' + end + @run_now = opts[:now] || opts[:run_now] + @executor = Concurrent::SafeTaskExecutor.new(task) + @running = Concurrent::AtomicBoolean.new(false) + @value = nil + + self.observers = Collection::CopyOnNotifyObserverSet.new + end + + # @!visibility private + def ns_shutdown_execution + @running.make_false + super + end + + # @!visibility private + def ns_kill_execution + @running.make_false + super + end + + # @!visibility private + def schedule_next_task(interval = execution_interval) + ScheduledTask.execute(interval, args: [Concurrent::Event.new], &method(:execute_task)) + nil + end + + # @!visibility private + def execute_task(completion) + return nil unless @running.true? + _success, value, reason = @executor.execute(self) + if completion.try? + self.value = value + schedule_next_task + time = Time.now + observers.notify_observers do + [time, self.value, reason] + end + end + nil + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/tuple.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/tuple.rb new file mode 100644 index 0000000..f8c4c25 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/tuple.rb @@ -0,0 +1,86 @@ +require 'concurrent/atomic/atomic_reference' + +module Concurrent + + # A fixed size array with volatile (synchronized, thread safe) getters/setters. + # Mixes in Ruby's `Enumerable` module for enhanced search, sort, and traversal. + # + # @example + # tuple = Concurrent::Tuple.new(16) + # + # tuple.set(0, :foo) #=> :foo | volatile write + # tuple.get(0) #=> :foo | volatile read + # tuple.compare_and_set(0, :foo, :bar) #=> true | strong CAS + # tuple.cas(0, :foo, :baz) #=> false | strong CAS + # tuple.get(0) #=> :bar | volatile read + # + # @see https://en.wikipedia.org/wiki/Tuple Tuple entry at Wikipedia + # @see http://www.erlang.org/doc/reference_manual/data_types.html#id70396 Erlang Tuple + # @see http://ruby-doc.org/core-2.2.2/Enumerable.html Enumerable + class Tuple + include Enumerable + + # The (fixed) size of the tuple. + attr_reader :size + + # @!visibility private + Tuple = defined?(Rubinius::Tuple) ? Rubinius::Tuple : ::Array + private_constant :Tuple + + # Create a new tuple of the given size. + # + # @param [Integer] size the number of elements in the tuple + def initialize(size) + @size = size + @tuple = tuple = Tuple.new(size) + i = 0 + while i < size + tuple[i] = Concurrent::AtomicReference.new + i += 1 + end + end + + # Get the value of the element at the given index. + # + # @param [Integer] i the index from which to retrieve the value + # @return [Object] the value at the given index or nil if the index is out of bounds + def get(i) + return nil if i >= @size || i < 0 + @tuple[i].get + end + alias_method :volatile_get, :get + + # Set the element at the given index to the given value + # + # @param [Integer] i the index for the element to set + # @param [Object] value the value to set at the given index + # + # @return [Object] the new value of the element at the given index or nil if the index is out of bounds + def set(i, value) + return nil if i >= @size || i < 0 + @tuple[i].set(value) + end + alias_method :volatile_set, :set + + # Set the value at the given index to the new value if and only if the current + # value matches the given old value. + # + # @param [Integer] i the index for the element to set + # @param [Object] old_value the value to compare against the current value + # @param [Object] new_value the value to set at the given index + # + # @return [Boolean] true if the value at the given element was set else false + def compare_and_set(i, old_value, new_value) + return false if i >= @size || i < 0 + @tuple[i].compare_and_set(old_value, new_value) + end + alias_method :cas, :compare_and_set + + # Calls the given block once for each element in self, passing that element as a parameter. + # + # @yieldparam [Object] ref the `Concurrent::AtomicReference` object at the current index + def each + @tuple.each {|ref| yield ref.get} + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/tvar.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/tvar.rb new file mode 100644 index 0000000..104761f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/tvar.rb @@ -0,0 +1,221 @@ +require 'set' +require 'concurrent/synchronization' + +module Concurrent + + # A `TVar` is a transactional variable - a single-element container that + # is used as part of a transaction - see `Concurrent::atomically`. + # + # @!macro thread_safe_variable_comparison + # + # {include:file:docs-source/tvar.md} + class TVar < Synchronization::Object + safe_initialization! + + # Create a new `TVar` with an initial value. + def initialize(value) + @value = value + @lock = Mutex.new + end + + # Get the value of a `TVar`. + def value + Concurrent::atomically do + Transaction::current.read(self) + end + end + + # Set the value of a `TVar`. + def value=(value) + Concurrent::atomically do + Transaction::current.write(self, value) + end + end + + # @!visibility private + def unsafe_value # :nodoc: + @value + end + + # @!visibility private + def unsafe_value=(value) # :nodoc: + @value = value + end + + # @!visibility private + def unsafe_lock # :nodoc: + @lock + end + + end + + # Run a block that reads and writes `TVar`s as a single atomic transaction. + # With respect to the value of `TVar` objects, the transaction is atomic, in + # that it either happens or it does not, consistent, in that the `TVar` + # objects involved will never enter an illegal state, and isolated, in that + # transactions never interfere with each other. You may recognise these + # properties from database transactions. + # + # There are some very important and unusual semantics that you must be aware of: + # + # * Most importantly, the block that you pass to atomically may be executed + # more than once. In most cases your code should be free of + # side-effects, except for via TVar. + # + # * If an exception escapes an atomically block it will abort the transaction. + # + # * It is undefined behaviour to use callcc or Fiber with atomically. + # + # * If you create a new thread within an atomically, it will not be part of + # the transaction. Creating a thread counts as a side-effect. + # + # Transactions within transactions are flattened to a single transaction. + # + # @example + # a = new TVar(100_000) + # b = new TVar(100) + # + # Concurrent::atomically do + # a.value -= 10 + # b.value += 10 + # end + def atomically + raise ArgumentError.new('no block given') unless block_given? + + # Get the current transaction + + transaction = Transaction::current + + # Are we not already in a transaction (not nested)? + + if transaction.nil? + # New transaction + + begin + # Retry loop + + loop do + + # Create a new transaction + + transaction = Transaction.new + Transaction::current = transaction + + # Run the block, aborting on exceptions + + begin + result = yield + rescue Transaction::AbortError => e + transaction.abort + result = Transaction::ABORTED + rescue Transaction::LeaveError => e + transaction.abort + break result + rescue => e + transaction.abort + raise e + end + # If we can commit, break out of the loop + + if result != Transaction::ABORTED + if transaction.commit + break result + end + end + end + ensure + # Clear the current transaction + + Transaction::current = nil + end + else + # Nested transaction - flatten it and just run the block + + yield + end + end + + # Abort a currently running transaction - see `Concurrent::atomically`. + def abort_transaction + raise Transaction::AbortError.new + end + + # Leave a transaction without committing or aborting - see `Concurrent::atomically`. + def leave_transaction + raise Transaction::LeaveError.new + end + + module_function :atomically, :abort_transaction, :leave_transaction + + private + + class Transaction + + ABORTED = ::Object.new + + OpenEntry = Struct.new(:value, :modified) + + AbortError = Class.new(StandardError) + LeaveError = Class.new(StandardError) + + def initialize + @open_tvars = {} + end + + def read(tvar) + entry = open(tvar) + entry.value + end + + def write(tvar, value) + entry = open(tvar) + entry.modified = true + entry.value = value + end + + def open(tvar) + entry = @open_tvars[tvar] + + unless entry + unless tvar.unsafe_lock.try_lock + Concurrent::abort_transaction + end + + entry = OpenEntry.new(tvar.unsafe_value, false) + @open_tvars[tvar] = entry + end + + entry + end + + def abort + unlock + end + + def commit + @open_tvars.each do |tvar, entry| + if entry.modified + tvar.unsafe_value = entry.value + end + end + + unlock + end + + def unlock + @open_tvars.each_key do |tvar| + tvar.unsafe_lock.unlock + end + end + + def self.current + Thread.current[:current_tvar_transaction] + end + + def self.current=(transaction) + Thread.current[:current_tvar_transaction] = transaction + end + + end + +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/utility/engine.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/utility/engine.rb new file mode 100644 index 0000000..bc4173e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/utility/engine.rb @@ -0,0 +1,56 @@ +module Concurrent + module Utility + + # @!visibility private + module EngineDetector + def on_jruby? + ruby_engine == 'jruby' + end + + def on_jruby_9000? + on_jruby? && ruby_version(JRUBY_VERSION, :>=, 9, 0, 0) + end + + def on_cruby? + ruby_engine == 'ruby' + end + + def on_rbx? + ruby_engine == 'rbx' + end + + def on_truffleruby? + ruby_engine == 'truffleruby' + end + + def on_windows? + !(RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/).nil? + end + + def on_osx? + !(RbConfig::CONFIG['host_os'] =~ /darwin|mac os/).nil? + end + + def on_linux? + !(RbConfig::CONFIG['host_os'] =~ /linux/).nil? + end + + def ruby_engine + defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby' + end + + def ruby_version(version = RUBY_VERSION, comparison, major, minor, patch) + result = (version.split('.').map(&:to_i) <=> [major, minor, patch]) + comparisons = { :== => [0], + :>= => [1, 0], + :<= => [-1, 0], + :> => [1], + :< => [-1] } + comparisons.fetch(comparison).include? result + end + end + end + + # @!visibility private + extend Utility::EngineDetector +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb new file mode 100644 index 0000000..0fa5c50 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb @@ -0,0 +1,90 @@ +require 'concurrent/synchronization' + +module Concurrent + + # @!macro monotonic_get_time + # + # Returns the current time a tracked by the application monotonic clock. + # + # @param [Symbol] unit the time unit to be returned, can be either + # :float_second, :float_millisecond, :float_microsecond, :second, + # :millisecond, :microsecond, or :nanosecond default to :float_second. + # + # @return [Float] The current monotonic time since some unspecified + # starting point + # + # @!macro monotonic_clock_warning + if defined?(Process::CLOCK_MONOTONIC) + + def monotonic_time(unit = :float_second) + Process.clock_gettime(Process::CLOCK_MONOTONIC, unit) + end + + elsif Concurrent.on_jruby? + + # @!visibility private + TIME_UNITS = Hash.new { |_hash, key| raise ArgumentError, "unexpected unit: #{key}" }.compare_by_identity + TIME_UNITS.merge!( + second: 1_000_000_000, + millisecond: 1_000_000, + microsecond: 1_000, + nanosecond: 1, + float_second: 1_000_000_000.0, + float_millisecond: 1_000_000.0, + float_microsecond: 1_000.0, + ) + TIME_UNITS.freeze + private_constant :TIME_UNITS + + def monotonic_time(unit = :float_second) + java.lang.System.nanoTime() / TIME_UNITS[unit] + end + + else + + class_definition = Class.new(Synchronization::LockableObject) do + def initialize + @last_time = Time.now.to_f + @time_units = Hash.new { |_hash, key| raise ArgumentError, "unexpected unit: #{key}" }.compare_by_identity + @time_units.merge!( + second: [nil, true], + millisecond: [1_000, true], + microsecond: [1_000_000, true], + nanosecond: [1_000_000_000, true], + float_second: [nil, false], + float_millisecond: [1_000.0, false], + float_microsecond: [1_000_000.0, false], + ) + super() + end + + # @!visibility private + def get_time(unit) + synchronize do + now = Time.now.to_f + if @last_time < now + @last_time = now + else # clock has moved back in time + @last_time += 0.000_001 + end + scale, to_int = @time_units[unit] + now *= scale if scale + now = now.to_i if to_int + now + end + end + end + + # Clock that cannot be set and represents monotonic time since + # some unspecified starting point. + # + # @!visibility private + GLOBAL_MONOTONIC_CLOCK = class_definition.new + private_constant :GLOBAL_MONOTONIC_CLOCK + + def monotonic_time(unit = :float_second) + GLOBAL_MONOTONIC_CLOCK.get_time(unit) + end + end + module_function :monotonic_time +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb new file mode 100644 index 0000000..a944bd7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb @@ -0,0 +1,79 @@ +require 'concurrent/utility/engine' + +module Concurrent + + module Utility + + # @!visibility private + module NativeExtensionLoader + + def allow_c_extensions? + Concurrent.on_cruby? + end + + def c_extensions_loaded? + defined?(@c_extensions_loaded) && @c_extensions_loaded + end + + def java_extensions_loaded? + defined?(@java_extensions_loaded) && @java_extensions_loaded + end + + def load_native_extensions + unless defined? Synchronization::AbstractObject + raise 'native_extension_loader loaded before Synchronization::AbstractObject' + end + + if Concurrent.on_cruby? && !c_extensions_loaded? + ['concurrent/concurrent_ruby_ext', + "concurrent/#{RUBY_VERSION[0..2]}/concurrent_ruby_ext" + ].each { |p| try_load_c_extension p } + end + + if Concurrent.on_jruby? && !java_extensions_loaded? + begin + require 'concurrent/concurrent_ruby.jar' + set_java_extensions_loaded + rescue LoadError => e + raise e, "Java extensions are required for JRuby.\n" + e.message, e.backtrace + end + end + end + + private + + def load_error_path(error) + if error.respond_to? :path + error.path + else + error.message.split(' -- ').last + end + end + + def set_c_extensions_loaded + @c_extensions_loaded = true + end + + def set_java_extensions_loaded + @java_extensions_loaded = true + end + + def try_load_c_extension(path) + require path + set_c_extensions_loaded + rescue LoadError => e + if load_error_path(e) == path + # move on with pure-Ruby implementations + # TODO (pitr-ch 12-Jul-2018): warning on verbose? + else + raise e + end + end + + end + end + + # @!visibility private + extend Utility::NativeExtensionLoader +end + diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/utility/native_integer.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/utility/native_integer.rb new file mode 100644 index 0000000..10719e7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/utility/native_integer.rb @@ -0,0 +1,53 @@ +module Concurrent + module Utility + # @private + module NativeInteger + # http://stackoverflow.com/questions/535721/ruby-max-integer + MIN_VALUE = -(2**(0.size * 8 - 2)) + MAX_VALUE = (2**(0.size * 8 - 2) - 1) + + def ensure_upper_bound(value) + if value > MAX_VALUE + raise RangeError.new("#{value} is greater than the maximum value of #{MAX_VALUE}") + end + value + end + + def ensure_lower_bound(value) + if value < MIN_VALUE + raise RangeError.new("#{value} is less than the maximum value of #{MIN_VALUE}") + end + value + end + + def ensure_integer(value) + unless value.is_a?(Integer) + raise ArgumentError.new("#{value} is not an Integer") + end + value + end + + def ensure_integer_and_bounds(value) + ensure_integer value + ensure_upper_bound value + ensure_lower_bound value + end + + def ensure_positive(value) + if value < 0 + raise ArgumentError.new("#{value} cannot be negative") + end + value + end + + def ensure_positive_and_no_zero(value) + if value < 1 + raise ArgumentError.new("#{value} cannot be negative or zero") + end + value + end + + extend self + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/utility/processor_counter.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/utility/processor_counter.rb new file mode 100644 index 0000000..c59f981 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/utility/processor_counter.rb @@ -0,0 +1,130 @@ +require 'etc' +require 'rbconfig' +require 'concurrent/delay' + +module Concurrent + module Utility + + # @!visibility private + class ProcessorCounter + def initialize + @processor_count = Delay.new { compute_processor_count } + @physical_processor_count = Delay.new { compute_physical_processor_count } + end + + # Number of processors seen by the OS and used for process scheduling. For + # performance reasons the calculated value will be memoized on the first + # call. + # + # When running under JRuby the Java runtime call + # `java.lang.Runtime.getRuntime.availableProcessors` will be used. According + # to the Java documentation this "value may change during a particular + # invocation of the virtual machine... [applications] should therefore + # occasionally poll this property." Subsequently the result will NOT be + # memoized under JRuby. + # + # Ruby's Etc.nprocessors will be used if available (MRI 2.2+). + # + # On Windows the Win32 API will be queried for the + # `NumberOfLogicalProcessors from Win32_Processor`. This will return the + # total number "logical processors for the current instance of the + # processor", which taked into account hyperthreading. + # + # * AIX: /usr/sbin/pmcycles (AIX 5+), /usr/sbin/lsdev + # * Alpha: /usr/bin/nproc (/proc/cpuinfo exists but cannot be used) + # * BSD: /sbin/sysctl + # * Cygwin: /proc/cpuinfo + # * Darwin: /usr/bin/hwprefs, /usr/sbin/sysctl + # * HP-UX: /usr/sbin/ioscan + # * IRIX: /usr/sbin/sysconf + # * Linux: /proc/cpuinfo + # * Minix 3+: /proc/cpuinfo + # * Solaris: /usr/sbin/psrinfo + # * Tru64 UNIX: /usr/sbin/psrinfo + # * UnixWare: /usr/sbin/psrinfo + # + # @return [Integer] number of processors seen by the OS or Java runtime + # + # @see https://github.com/grosser/parallel/blob/4fc8b89d08c7091fe0419ca8fba1ec3ce5a8d185/lib/parallel.rb + # + # @see http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html#availableProcessors() + # @see http://msdn.microsoft.com/en-us/library/aa394373(v=vs.85).aspx + def processor_count + @processor_count.value + end + + # Number of physical processor cores on the current system. For performance + # reasons the calculated value will be memoized on the first call. + # + # On Windows the Win32 API will be queried for the `NumberOfCores from + # Win32_Processor`. This will return the total number "of cores for the + # current instance of the processor." On Unix-like operating systems either + # the `hwprefs` or `sysctl` utility will be called in a subshell and the + # returned value will be used. In the rare case where none of these methods + # work or an exception is raised the function will simply return 1. + # + # @return [Integer] number physical processor cores on the current system + # + # @see https://github.com/grosser/parallel/blob/4fc8b89d08c7091fe0419ca8fba1ec3ce5a8d185/lib/parallel.rb + # + # @see http://msdn.microsoft.com/en-us/library/aa394373(v=vs.85).aspx + # @see http://www.unix.com/man-page/osx/1/HWPREFS/ + # @see http://linux.die.net/man/8/sysctl + def physical_processor_count + @physical_processor_count.value + end + + private + + def compute_processor_count + if Concurrent.on_jruby? + java.lang.Runtime.getRuntime.availableProcessors + else + Etc.nprocessors + end + end + + def compute_physical_processor_count + ppc = case RbConfig::CONFIG["target_os"] + when /darwin\d\d/ + IO.popen("/usr/sbin/sysctl -n hw.physicalcpu", &:read).to_i + when /linux/ + cores = {} # unique physical ID / core ID combinations + phy = 0 + IO.read("/proc/cpuinfo").scan(/^physical id.*|^core id.*/) do |ln| + if ln.start_with?("physical") + phy = ln[/\d+/] + elsif ln.start_with?("core") + cid = phy + ":" + ln[/\d+/] + cores[cid] = true if not cores[cid] + end + end + cores.count + when /mswin|mingw/ + require 'win32ole' + result_set = WIN32OLE.connect("winmgmts://").ExecQuery( + "select NumberOfCores from Win32_Processor") + result_set.to_enum.collect(&:NumberOfCores).reduce(:+) + else + processor_count + end + # fall back to logical count if physical info is invalid + ppc > 0 ? ppc : processor_count + rescue + return 1 + end + end + end + + # create the default ProcessorCounter on load + @processor_counter = Utility::ProcessorCounter.new + singleton_class.send :attr_reader, :processor_counter + + def self.processor_count + processor_counter.processor_count + end + + def self.physical_processor_count + processor_counter.physical_processor_count + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/version.rb b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/version.rb new file mode 100644 index 0000000..4372d0d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/version.rb @@ -0,0 +1,3 @@ +module Concurrent + VERSION = '1.1.10' +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/bake/console.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/bake/console.rb new file mode 100644 index 0000000..d4669cb --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/bake/console.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2020-2022, by Samuel Williams. + +# Increase the verbosity of the logger to info. +def info + require_relative '../lib/console' + + Console.logger.info! +end + +# Increase the verbosity of the logger to debug. +def debug + require_relative '../lib/console' + + Console.logger.debug! +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console.rb new file mode 100644 index 0000000..6d47dfe --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. +# Copyright, 2019, by Bryan Powell. +# Copyright, 2020, by Michael Adams. +# Copyright, 2021, by Cédric Boutillier. + +require_relative 'console/version' +require_relative 'console/logger' + +module Console + def self.logger + Logger.instance + end + + def self.logger= instance + Logger.instance= instance + end + + def logger= logger + @logger = logger + end + + def logger + @logger || Logger.instance + end + + def self.extended(klass) + klass.instance_variable_set(:@logger, nil) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/buffer.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/buffer.rb new file mode 100644 index 0000000..b68975a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/buffer.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. + +require 'stringio' + +module Console + class Buffer < StringIO + def initialize(prefix = nil) + @prefix = prefix + + super() + end + + def puts(*args, prefix: @prefix) + args.each do |arg| + self.write(prefix) if prefix + super(arg) + end + end + + alias << puts + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/capture.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/capture.rb new file mode 100644 index 0000000..fd3e354 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/capture.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. + +require_relative 'filter' + +module Console + # A general sink which captures all events into a buffer. + class Capture + def initialize + @buffer = [] + @verbose = false + end + + attr :buffer + attr :verbose + + def last + @buffer.last + end + + def include?(pattern) + JSON.dump(@buffer).include?(pattern) + end + + def clear + @buffer.clear + end + + def empty? + @buffer.empty? + end + + def verbose!(value = true) + @verbose = value + end + + def verbose? + @verbose + end + + def call(subject = nil, *arguments, severity: UNKNOWN, **options, &block) + message = { + time: ::Time.now.iso8601, + severity: severity, + **options, + } + + if subject + message[:subject] = subject + end + + if arguments.any? + message[:arguments] = arguments + end + + if block_given? + if block.arity.zero? + message[:message] = yield + else + buffer = StringIO.new + yield buffer + message[:message] = buffer.string + end + end + + @buffer << message + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/clock.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/clock.rb new file mode 100644 index 0000000..608ac50 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/clock.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2021-2022, by Samuel Williams. + +module Console + module Clock + def self.formatted_duration(duration) + if duration < 60.0 + return "#{duration.round(2)}s" + end + + duration /= 60.0 + + if duration < 60.0 + return "#{duration.floor}m" + end + + duration /= 60.0 + + if duration < 24.0 + return "#{duration.floor}h" + end + + duration /= 24.0 + + return "#{duration.floor}d" + end + + # Get the current elapsed monotonic time. + def self.now + ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/compatible/logger.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/compatible/logger.rb new file mode 100644 index 0000000..8e85a71 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/compatible/logger.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2022, by Samuel Williams. + +require 'logger' + +module Console + module Compatible + class Logger < ::Logger + class LogDevice + def initialize(subject, output) + @subject = subject + @output = output + end + + def write(message) + @output.call(@subject, message) + end + + def call(*arguments, **options) + @output.call(*arguments, **options) + end + + def reopen + end + + def close + end + end + + def initialize(subject, output) + super(nil) + + @progname = subject + @logdev = LogDevice.new(subject, output) + end + + def add(severity, message = nil, progname = nil) + severity ||= UNKNOWN + + if @logdev.nil? or severity < level + return true + end + + if progname.nil? + progname = @progname + end + + if message.nil? + if block_given? + message = yield + else + message = progname + progname = @progname + end + end + + @logdev.call( + progname, message, + severity: format_severity(severity) + ) + + return true + end + + def format_severity(value) + super.downcase.to_sym + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/event.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/event.rb new file mode 100644 index 0000000..8673e67 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/event.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. + +require_relative 'event/spawn' +require_relative 'event/failure' +require_relative 'event/progress' diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/event/failure.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/event/failure.rb new file mode 100644 index 0000000..81f4acf --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/event/failure.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. +# Copyright, 2021, by Robert Schulze. + +require_relative 'generic' + +module Console + module Event + class Failure < Generic + def self.current_working_directory + Dir.getwd + rescue # e.g. Errno::EMFILE + nil + end + + def self.for(exception) + self.new(exception, self.current_working_directory) + end + + def initialize(exception, root = nil) + @exception = exception + @root = root + end + + attr :exception + attr :root + + def self.register(terminal) + terminal[:exception_title] ||= terminal.style(:red, nil, :bold) + terminal[:exception_detail] ||= terminal.style(:yellow) + terminal[:exception_backtrace] ||= terminal.style(:red) + terminal[:exception_backtrace_other] ||= terminal.style(:red, nil, :faint) + terminal[:exception_message] ||= terminal.style(:default) + end + + def to_h + {exception: @exception, root: @root} + end + + def format(output, terminal, verbose) + format_exception(@exception, nil, output, terminal, verbose) + end + + def format_exception(exception, prefix, output, terminal, verbose) + lines = exception.message.lines.map(&:chomp) + + output.puts " #{prefix}#{terminal[:exception_title]}#{exception.class}#{terminal.reset}: #{lines.shift}" + + lines.each do |line| + output.puts " #{terminal[:exception_detail]}#{line}#{terminal.reset}" + end + + root_pattern = /^#{@root}\// if @root + + exception.backtrace&.each_with_index do |line, index| + path, offset, message = line.split(":") + style = :exception_backtrace + + # Make the path a bit more readable + if root_pattern and path.sub!(root_pattern, "").nil? + style = :exception_backtrace_other + end + + output.puts " #{index == 0 ? "→" : " "} #{terminal[style]}#{path}:#{offset}#{terminal[:exception_message]} #{message}#{terminal.reset}" + end + + if exception.cause + format_exception(exception.cause, "Caused by ", output, terminal, verbose) + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/event/generic.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/event/generic.rb new file mode 100644 index 0000000..dbb151f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/event/generic.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. + +module Console + module Event + class Generic + def self.register(terminal) + end + + def to_h + end + + def to_json(*arguments) + JSON.generate([self.class, to_h], *arguments) + end + + def format(buffer, terminal) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/event/progress.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/event/progress.rb new file mode 100644 index 0000000..45b6990 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/event/progress.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2020-2022, by Samuel Williams. + +require_relative 'generic' + +module Console + module Event + class Progress < Generic + BLOCK = [ + " ", + "▏", + "▎", + "▍", + "▌", + "▋", + "▊", + "▉", + "█", + ] + + def initialize(current, total) + @current = current + @total = total + end + + attr :current + attr :total + + def value + @current.to_f / @total.to_f + end + + def bar(value = self.value, width = 70) + blocks = width * value + full_blocks = blocks.floor + partial_block = ((blocks - full_blocks) * BLOCK.size).floor + + if partial_block.zero? + BLOCK.last * full_blocks + else + "#{BLOCK.last * full_blocks}#{BLOCK[partial_block]}" + end.ljust(width) + end + + def self.register(terminal) + terminal[:progress_bar] ||= terminal.style(:blue, :white) + end + + def to_h + {current: @current, total: @total} + end + + def format(output, terminal, verbose) + output.puts "#{terminal[:progress_bar]}#{self.bar}#{terminal.reset} #{sprintf('%6.2f', self.value * 100)}%" + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/event/spawn.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/event/spawn.rb new file mode 100644 index 0000000..a4972aa --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/event/spawn.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. + +require_relative 'generic' + +module Console + module Event + class Spawn < Generic + def self.for(*arguments, **options) + # Extract out the command environment: + if arguments.first.is_a?(Hash) + environment = arguments.shift + self.new(environment, arguments, options) + else + self.new(nil, arguments, options) + end + end + + def initialize(environment, arguments, options) + @environment = environment + @arguments = arguments + @options = options + end + + attr :environment + attr :arguments + attr :options + + def chdir_string(options) + if options and chdir = options[:chdir] + " in #{chdir}" + end + end + + def self.register(terminal) + terminal[:shell_command] ||= terminal.style(:blue, nil, :bold) + end + + def to_h + Hash.new.tap do |hash| + hash[:environment] = @environment if @environment&.any? + hash[:arguments] = @arguments if @arguments&.any? + hash[:options] = @options if @options&.any? + end + end + + def format(output, terminal, verbose) + arguments = @arguments.flatten.collect(&:to_s) + + output.puts " #{terminal[:shell_command]}#{arguments.join(' ')}#{terminal.reset}#{chdir_string(options)}" + + if verbose and @environment + @environment.each do |key, value| + output.puts " export #{key}=#{value}" + end + end + end + end + end + + # Deprecated. + Shell = Event::Spawn +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/filter.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/filter.rb new file mode 100644 index 0000000..563c77d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/filter.rb @@ -0,0 +1,133 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. +# Copyright, 2019, by Bryan Powell. +# Copyright, 2020, by Michael Adams. +# Copyright, 2021, by Robert Schulze. + +require_relative 'buffer' + +module Console + UNKNOWN = 'unknown' + + class Filter + def self.[] **levels + klass = Class.new(self) + min_level, max_level = levels.values.minmax + + klass.instance_exec do + const_set(:LEVELS, levels) + const_set(:MINIMUM_LEVEL, min_level) + const_set(:MAXIMUM_LEVEL, max_level) + + levels.each do |name, level| + const_set(name.to_s.upcase, level) + + define_method(name) do |subject = nil, *arguments, **options, &block| + if self.enabled?(subject, level) + self.call(subject, *arguments, severity: name, **options, **@options, &block) + end + end + + define_method("#{name}!") do + @level = level + end + + define_method("#{name}?") do + @level <= level + end + end + end + + return klass + end + + def initialize(output, verbose: true, level: self.class::DEFAULT_LEVEL, enabled: nil, **options) + @output = output + @verbose = verbose + @level = level + + @subjects = {} + + @options = options + + if enabled + enabled.each{|name| enable(name)} + end + end + + def with(level: @level, verbose: @verbose, **options) + dup.tap do |logger| + logger.level = level + logger.verbose! if verbose + logger.options = @options.merge(options) + end + end + + attr_accessor :output + attr :verbose + attr :level + + attr :subjects + + attr_accessor :options + + def level= level + if level.is_a? Symbol + @level = self.class::LEVELS[level] + else + @level = level + end + end + + def verbose!(value = true) + @verbose = value + @output.verbose!(value) + end + + def off! + @level = self.class::MAXIMUM_LEVEL + 1 + end + + def all! + @level = self.class::MINIMUM_LEVEL - 1 + end + + # You can enable and disable logging for classes. This function checks if logging for a given subject is enabled. + # @param subject [Object] the subject to check. + def enabled?(subject, level = self.class::MINIMUM_LEVEL) + if specific_level = @subjects[subject.class] + return level >= specific_level + end + + if level >= @level + return true + end + end + + # Enable specific log level for the given class. + # @parameter name [Class] The class to enable. + def enable(subject, level = self.class::MINIMUM_LEVEL) + unless subject.is_a?(Class) + raise ArgumentError, "Expected a class, got #{subject.inspect}" + end + + @subjects[subject] = level + end + + # Disable specific logging for the specific class. + # @parameter name [Class] The class to disable. + def disable(subject) + unless subject.is_a?(Class) + raise ArgumentError, "Expected a class, got #{subject.inspect}" + end + + @subjects.delete(subject) + end + + def call(*arguments, **options, &block) + @output.call(*arguments, **options, &block) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/logger.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/logger.rb new file mode 100644 index 0000000..d60d157 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/logger.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. +# Copyright, 2021, by Bryan Powell. +# Copyright, 2021, by Robert Schulze. + +require_relative 'output' +require_relative 'filter' +require_relative 'progress' + +require_relative 'resolver' +require_relative 'terminal/logger' +require_relative 'serialized/logger' + +require 'fiber/local' + +module Console + class Logger < Filter[debug: 0, info: 1, warn: 2, error: 3, fatal: 4] + extend Fiber::Local + + # Set the default log level based on `$DEBUG` and `$VERBOSE`. + # You can also specify CONSOLE_LEVEL=debug or CONSOLE_LEVEL=info in environment. + # https://mislav.net/2011/06/ruby-verbose-mode/ has more details about how it all fits together. + def self.default_log_level(env = ENV) + if level = env['CONSOLE_LEVEL'] + LEVELS[level.to_sym] || level.to_i + elsif $DEBUG + DEBUG + elsif $VERBOSE.nil? + WARN + else + INFO + end + end + + # Controls verbose output using `$VERBOSE`. + def self.verbose?(env = ENV) + !$VERBOSE.nil? || env['CONSOLE_VERBOSE'] + end + + def self.default_logger(output = $stderr, env = ENV, **options) + if options[:verbose].nil? + options[:verbose] = self.verbose?(env) + end + + if options[:level].nil? + options[:level] = self.default_log_level(env) + end + + output = Output.new(output, env, **options) + logger = self.new(output, **options) + + Resolver.default_resolver(logger) + + return logger + end + + def self.local + self.default_logger + end + + DEFAULT_LEVEL = 1 + + def initialize(output, **options) + super(output, **options) + end + + def progress(subject, total, **options) + Progress.new(self, subject, total, **options) + end + + # @deprecated Use `fatal` instead. + def failure(subject, exception, *arguments, &block) + self.fatal(subject, exception, *arguments, &block) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/output.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/output.rb new file mode 100644 index 0000000..706adbd --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/output.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2021-2022, by Samuel Williams. + +require_relative 'output/default' +require_relative 'output/json' +require_relative 'output/text' +require_relative 'output/xterm' + +module Console + module Output + def self.new(output = nil, env = ENV, **options) + if names = env['CONSOLE_OUTPUT'] + names = names.split(',').reverse + + names.inject(output) do |output, name| + Output.const_get(name).new(output, **options) + end + else + return Output::Default.new(output, **options) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/output/default.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/output/default.rb new file mode 100644 index 0000000..9396af1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/output/default.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2021-2022, by Samuel Williams. + +require_relative 'xterm' +require_relative 'json' + +module Console + module Output + module Default + def self.new(output, **options) + output ||= $stderr + + if output.tty? + XTerm.new(output, **options) + else + JSON.new(output, **options) + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/output/json.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/output/json.rb new file mode 100644 index 0000000..da2e84f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/output/json.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2021-2022, by Samuel Williams. + +require_relative '../serialized/logger' + +module Console + module Output + module JSON + def self.new(output, **options) + Serialized::Logger.new(output, format: ::JSON, **options) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/output/sensitive.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/output/sensitive.rb new file mode 100644 index 0000000..9b2d484 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/output/sensitive.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2021-2022, by Samuel Williams. + +require_relative '../serialized/logger' + +module Console + module Output + class Sensitive + def initialize(output, **options) + @output = output + end + + REDACT = / + phone + | email + | full_?name + | first_?name + | last_?name + + | device_name + | user_agent + + | zip + | address + | location + | latitude + | longitude + + | ip + | gps + + | sex + | gender + + | token + | password + /xi + + def redact?(text) + text.match?(REDACT) + end + + def redact_hash(arguments, filter) + arguments.transform_values do |value| + redact(value, filter) + end + end + + def redact_array(array, filter) + array.map do |value| + redact(value, filter) + end + end + + def redact(argument, filter) + case argument + when String + if filter + filter.call(argument) + elsif redact?(argument) + "[REDACTED]" + else + argument + end + when Array + redact_array(argument, filter) + when Hash + redact_hash(argument, filter) + else + redact(argument.to_s, filter) + end + end + + class Filter + def initialize(substitutions) + @substitutions = substitutions + @pattern = Regexp.union(substitutions.keys) + end + + def call(text) + text.gsub(@pattern, @substitutions) + end + end + + def call(subject = nil, *arguments, sensitive: true, **options, &block) + if sensitive + if sensitive.respond_to?(:call) + filter = sensitive + elsif sensitive.is_a?(Hash) + filter = Filter.new(sensitive) + end + + subject = redact(subject, filter) + arguments = redact_array(arguments, filter) + end + + @output.call(subject, *arguments, **options) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/output/split.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/output/split.rb new file mode 100644 index 0000000..440e6e3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/output/split.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. + +module Console + module Output + class Split + def self.[](*outputs) + self.new(outputs) + end + + def initialize(outputs) + @outputs = outputs + end + + def verbose!(value = true) + @outputs.each{|output| output.verbose!(value)} + end + + def call(level, subject = nil, *arguments, **options, &block) + @outputs.each do |output| + output.call(level, subject, *arguments, **options, &block) + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/output/text.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/output/text.rb new file mode 100644 index 0000000..1867a85 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/output/text.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2021-2022, by Samuel Williams. + +require_relative '../terminal/logger' + +module Console + module Output + module Text + def self.new(output, **options) + Terminal::Logger.new(output, format: Terminal::Text, **options) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/output/xterm.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/output/xterm.rb new file mode 100644 index 0000000..293254f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/output/xterm.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2021-2022, by Samuel Williams. + +require_relative '../terminal/logger' + +module Console + module Output + module XTerm + def self.new(output, **options) + Terminal::Logger.new(output, format: Terminal::XTerm, **options) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/progress.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/progress.rb new file mode 100644 index 0000000..4880ec9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/progress.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2020-2022, by Samuel Williams. + +require_relative 'event/progress' +require_relative 'clock' + +module Console + class Progress + def self.now + Process.clock_gettime(Process::CLOCK_MONOTONIC) + end + + def initialize(output, subject, total = 0, minimum_output_duration: 0.1) + @output = output + @subject = subject + + @start_time = Progress.now + + @last_output_time = nil + @minimum_output_duration = minimum_output_duration + + @current = 0 + @total = total + end + + attr :subject + attr :current + attr :total + + def duration + Progress.now - @start_time + end + + def ratio + Rational(@current.to_f, @total.to_f) + end + + def remaining + @total - @current + end + + def average_duration + if @current > 0 + duration / @current + end + end + + def estimated_remaining_time + if average_duration = self.average_duration + average_duration * remaining + end + end + + def increment(amount = 1) + @current += amount + + if output? + @output.info(@subject, self) {Event::Progress.new(@current, @total)} + @last_output_time = Progress.now + end + + return self + end + + def resize(total) + @total = total + + @output.info(@subject, self) {Event::Progress.new(@current, @total)} + @last_output_time = Progress.now + + return self + end + + def mark(...) + @output.info(@subject, ...) + end + + def to_s + if estimated_remaining_time = self.estimated_remaining_time + "#{@current}/#{@total} completed in #{Clock.formatted_duration(self.duration)}, #{Clock.formatted_duration(estimated_remaining_time)} remaining." + else + "#{@current}/#{@total} completed, waiting for estimate..." + end + end + + private + + def duration_since_last_output + if @last_output_time + Progress.now - @last_output_time + end + end + + def output? + if remaining.zero? + return true + elsif duration = duration_since_last_output + return duration > @minimum_output_duration + else + return true + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/resolver.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/resolver.rb new file mode 100644 index 0000000..5ac2cc6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/resolver.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. +# Copyright, 2021, by Robert Schulze. + +require_relative 'filter' + +module Console + class Resolver + # You can change the log level for different classes using CONSOLE_$LEVEL env vars. + # + # e.g. `CONSOLE_WARN=Acorn,Banana CONSOLE_DEBUG=Cat` will set the log level for the classes Acorn and Banana to `warn` and Cat to `debug`. This overrides the default log level. + # + # You can enable all log levels for a given class by using `CONSOLE_ON=MyClass`. Similarly you can disable all logging using `CONSOLE_OFF=MyClass`. + # + # @parameter logger [Logger] A logger instance to set the logging levels on. + # @parameter env [Hash] The environment to read levels from. + # + # @returns [Nil] If there were no custom logging levels specified in the environment. + # @returns [Resolver] If there were custom logging levels, then the created resolver is returned. + def self.default_resolver(logger, env = ENV) + # Find all CONSOLE_$LEVEL variables from environment: + levels = logger.class::LEVELS + .map{|label, level| [level, env["CONSOLE_#{label.upcase}"]&.split(',')]} + .to_h + .compact + + off_klasses = env['CONSOLE_OFF']&.split(',') + on_klasses = env['CONSOLE_ON']&.split(',') + + resolver = nil + + # If we have any levels, then create a class resolver, and each time a class is resolved, set the log level for that class to the specified level: + if on_klasses&.any? + resolver ||= Resolver.new + + resolver.bind(on_klasses) do |klass| + logger.enable(klass, logger.class::MINIMUM_LEVEL - 1) + end + end + + if off_klasses&.any? + resolver ||= Resolver.new + + resolver.bind(off_klasses) do |klass| + logger.disable(klass) + end + end + + levels.each do |level, names| + resolver ||= Resolver.new + + resolver.bind(names) do |klass| + logger.enable(klass, level) + end + end + + return resolver + end + + def initialize + @names = {} + + @trace_point = TracePoint.new(:class, &self.method(:resolve)) + end + + def bind(names, &block) + names.each do |name| + if klass = Object.const_get(name) rescue nil + yield klass + else + @names[name] = block + end + end + + if @names.any? + @trace_point.enable + else + @trace_point.disable + end + end + + def waiting? + @trace_point.enabled? + end + + def resolve(trace_point) + if block = @names.delete(trace_point.self.to_s) + block.call(trace_point.self) + end + + if @names.empty? + @trace_point.disable + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/serialized/logger.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/serialized/logger.rb new file mode 100644 index 0000000..c6fca94 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/serialized/logger.rb @@ -0,0 +1,102 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. + +require_relative '../buffer' +require_relative '../filter' + +require 'time' +require 'json' + +module Console + module Serialized + class Logger + def initialize(io = $stderr, format: JSON, verbose: false, **options) + @io = io + @start = Time.now + @format = format + @verbose = verbose + end + + attr :io + attr :start + attr :format + + def verbose!(value = true) + @verbose = true + end + + def dump(record) + @format.dump(record) + end + + def call(subject = nil, *arguments, severity: UNKNOWN, **options, &block) + record = { + time: Time.now.iso8601, + severity: severity, + class: subject.class, + oid: subject.object_id, + pid: Process.pid, + } + + if subject + record[:subject] = subject + end + + message = arguments + + if block_given? + if block.arity.zero? + message << yield + else + buffer = StringIO.new + yield buffer + message << buffer.string + end + end + + if message.size == 1 + record[:message] = message.first + elsif message.any? + record[:message] = message + end + + if exception = find_exception(message) + record[:error] = { + kind: exception.class, + message: exception.message, + stack: format_stack(exception) + } + end + + record.update(options) + + @io.puts(self.dump(record)) + end + + private + + def find_exception(message) + message.find{|part| part.is_a?(Exception)} + end + + def format_stack(exception) + buffer = StringIO.new + format_backtrace(exception, buffer) + return buffer.string + end + + def format_backtrace(exception, buffer) + buffer.puts exception.backtrace + + if exception = exception.cause + buffer.puts + buffer.puts "Caused by: #{exception.class} #{exception.message}" + + format_backtrace(exception, buffer) + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/split.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/split.rb new file mode 100644 index 0000000..61e5224 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/split.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. + +require_relative 'output/split' + +module Console + Split = Output::Split +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/terminal.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/terminal.rb new file mode 100644 index 0000000..ed3af95 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/terminal.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. + +require_relative 'terminal/logger' diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/terminal/logger.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/terminal/logger.rb new file mode 100644 index 0000000..e3e88aa --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/terminal/logger.rb @@ -0,0 +1,198 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. +# Copyright, 2021, by Robert Schulze. + +require_relative '../buffer' +require_relative '../event' + +require_relative 'text' +require_relative 'xterm' + +require 'json' +require 'fiber' + +module Console + module Terminal + # This, and all related methods, is considered private. + CONSOLE_START_AT = 'CONSOLE_START_AT' + + # Exports CONSOLE_START which can be used to synchronize the start times of all child processes when they log using delta time. + def self.start_at!(environment = ENV) + if time_string = environment[CONSOLE_START_AT] + start_at = Time.parse(time_string) rescue nil + end + + unless start_at + start_at = Time.now + environment[CONSOLE_START_AT] = start_at.to_s + end + + return start_at + end + + def self.for(io) + if io.isatty + XTerm.new(io) + else + Text.new(io) + end + end + + class Logger + def initialize(io = $stderr, verbose: nil, start_at: Terminal.start_at!, format: nil, **options) + @io = io + @start_at = start_at + + @terminal = format.nil? ? Terminal.for(io) : format.new(io) + + if verbose.nil? + @verbose = !@terminal.colors? + else + @verbose = verbose + end + + @terminal[:logger_suffix] ||= @terminal.style(:white, nil, :faint) + @terminal[:subject] ||= @terminal.style(nil, nil, :bold) + @terminal[:debug] = @terminal.style(:cyan) + @terminal[:info] = @terminal.style(:green) + @terminal[:warn] = @terminal.style(:yellow) + @terminal[:error] = @terminal.style(:red) + @terminal[:fatal] = @terminal[:error] + + self.register_defaults(@terminal) + end + + attr :io + + attr_accessor :verbose + + attr :start + attr :terminal + + def verbose!(value = true) + @verbose = value + end + + def register_defaults(terminal) + Event.constants.each do |constant| + klass = Event.const_get(constant) + klass.register(terminal) + end + end + + UNKNOWN = :unknown + + def call(subject = nil, *arguments, name: nil, severity: UNKNOWN, **options, &block) + prefix = build_prefix(name || severity.to_s) + indent = " " * prefix.size + + buffer = Buffer.new("#{indent}| ") + + if subject + format_subject(severity, prefix, subject, buffer) + end + + if options&.any? + format_options(options, buffer) + end + + arguments.each do |argument| + format_argument(argument, buffer) + end + + if block_given? + if block.arity.zero? + format_argument(yield, buffer) + else + yield(buffer, @terminal) + end + end + + @io.write buffer.string + end + + protected + + def format_options(options, output) + format_value(options.to_json, output) + end + + def format_argument(argument, output) + case argument + when Exception + Event::Failure.for(argument).format(output, @terminal, @verbose) + when Event::Generic + argument.format(output, @terminal, @verbose) + else + format_value(argument, output) + end + end + + def format_subject(severity, prefix, subject, buffer) + if subject.is_a?(String) + format_string_subject(severity, prefix, subject, buffer) + elsif subject.is_a?(Module) + format_string_subject(severity, prefix, subject.to_s, buffer) + else + format_object_subject(severity, prefix, subject, buffer) + end + end + + def default_suffix(object = nil) + buffer = +" #{@terminal[:logger_suffix]}" + + if object + buffer << "[oid=0x#{object.object_id.to_s(16)}] " + end + + buffer << "[ec=0x#{Fiber.current.object_id.to_s(16)}] [pid=#{Process.pid}] [#{::Time.now}]#{@terminal.reset}" + end + + def format_object_subject(severity, prefix, subject, output) + prefix_style = @terminal[severity] + + if @verbose + suffix = default_suffix(subject) + end + + prefix = "#{prefix_style}#{prefix}:#{@terminal.reset} " + + output.puts "#{@terminal[:subject]}#{subject.class}#{@terminal.reset}#{suffix}", prefix: prefix + end + + def format_string_subject(severity, prefix, subject, output) + prefix_style = @terminal[severity] + + if @verbose + suffix = default_suffix + end + + prefix = "#{prefix_style}#{prefix}:#{@terminal.reset} " + + output.puts "#{@terminal[:subject]}#{subject}#{@terminal.reset}#{suffix}", prefix: prefix + end + + def format_value(value, output) + string = value.to_s + + string.each_line do |line| + output.puts "#{line}" + end + end + + def time_offset_prefix + Clock.formatted_duration(Time.now - @start_at).rjust(6) + end + + def build_prefix(name) + if @verbose + "#{time_offset_prefix} #{name.rjust(8)}" + else + time_offset_prefix + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/terminal/text.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/terminal/text.rb new file mode 100644 index 0000000..7499cad --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/terminal/text.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. + +require 'io/console' + +module Console + # Styled terminal output. + module Terminal + class Text + def initialize(output) + @output = output + @styles = {reset: self.reset} + end + + def [] key + @styles[key] + end + + def []= key, value + @styles[key] = value + end + + def colors? + false + end + + def style(foreground, background = nil, *attributes) + end + + def reset + end + + def write(*arguments, style: nil) + if style and prefix = self[style] + @output.write(prefix) + @output.write(*arguments) + @output.write(self.reset) + else + @output.write(*arguments) + end + end + + def puts(*arguments, style: nil) + if style and prefix = self[style] + @output.write(prefix) + @output.puts(*arguments) + @output.write(self.reset) + else + @output.puts(*arguments) + end + end + + # Print out the given arguments. + # When the argument is a symbol, look up the style and inject it into the output stream. + # When the argument is a proc/lambda, call it with self as the argument. + # When the argument is anything else, write it directly to the output. + def print(*arguments) + arguments.each do |argument| + case argument + when Symbol + @output.write(self[argument]) + when Proc + argument.call(self) + else + @output.write(argument) + end + end + end + + # Print out the arguments as per {#print}, followed by the reset sequence and a newline. + def print_line(*arguments) + print(*arguments) + @output.puts(self.reset) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/terminal/xterm.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/terminal/xterm.rb new file mode 100644 index 0000000..e06c964 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/terminal/xterm.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. + +require 'io/console' + +require_relative 'text' + +module Console + # Styled terminal output. + module Terminal + class XTerm < Text + COLORS = { + black: 0, + red: 1, + green: 2, + yellow: 3, + blue: 4, + magenta: 5, + cyan: 6, + white: 7, + default: 9, + } + + ATTRIBUTES = { + normal: 0, + bold: 1, + bright: 1, + faint: 2, + italic: 3, + underline: 4, + blink: 5, + reverse: 7, + hidden: 8, + } + + def colors? + true + end + + def size + @output.winsize + end + + def style(foreground, background = nil, *attributes) + tokens = [] + + if foreground + tokens << 30 + COLORS.fetch(foreground) + end + + if background + tokens << 40 + COLORS.fetch(background) + end + + attributes.each do |attribute| + tokens << ATTRIBUTES.fetch(attribute){attribute.to_i} + end + + return "\e[#{tokens.join(';')}m" + end + + def reset + "\e[0m" + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/version.rb b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/version.rb new file mode 100644 index 0000000..fb13c24 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/lib/console/version.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2022, by Samuel Williams. + +module Console + VERSION = "1.16.2" +end diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/license.md b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/license.md new file mode 100644 index 0000000..8c6cf81 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/license.md @@ -0,0 +1,27 @@ +# MIT License + +Copyright, 2019-2022, by Samuel Williams. +Copyright, 2019-2021, by Bryan Powell. +Copyright, 2019, by Cyril Roelandt. +Copyright, 2020, by Olle Jonsson. +Copyright, 2020, by Michael Adams. +Copyright, 2021, by Cédric Boutillier. +Copyright, 2021, by Robert Schulze. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/readme.md b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/readme.md new file mode 100644 index 0000000..6ef6af4 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/console-1.16.2/readme.md @@ -0,0 +1,31 @@ +# Console + +Provides beautiful console logging for Ruby applications. Implements fast, buffered log output. + +[![Development Status](https://github.com/socketry/console/workflows/Test/badge.svg)](https://github.com/socketry/console/actions?workflow=Test) + +## Motivation + +When Ruby decided to reverse the order of exception backtraces, I finally gave up using the built in logging and decided restore sanity to the output of my programs once and for all! + +## Features + + - Thread safe global logger with per-fiber context. + - Carry along context with nested loggers. + - Enable/disable log levels per-class. + - Detailed logging of exceptions. + - Beautiful logging to the terminal or structured logging using JSON. + +## Usage + +Please see the [project documentation](https://socketry.github.io/console). + +## Contributing + +We welcome contributions to this project. + +1. Fork it. +2. Create your feature branch (`git checkout -b my-new-feature`). +3. Commit your changes (`git commit -am 'Add some feature'`). +4. Push to the branch (`git push origin my-new-feature`). +5. Create new Pull Request. diff --git a/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/.github/workflows/test.yml b/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/.github/workflows/test.yml new file mode 100644 index 0000000..e9cf7ac --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/.github/workflows/test.yml @@ -0,0 +1,112 @@ +name: Test + +on: + push: + branches: [ master ] + schedule: + - cron: '0 0 11,25 * *' # roughly every two weeks to run on new Ruby versions + pull_request: + branches: [ master ] + workflow_dispatch: + +jobs: + test: + name: "Unit" + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - macos-latest + - ubuntu-latest + - windows-latest + ruby: + - "2.1" + - "2.2" + - "2.3" + - "2.4" + - "2.5" + - "2.6" + - "2.7" + - "3.0" + + steps: + + - uses: actions/checkout@v2 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + + - name: Test + run: bundle exec rake + + system: + name: "System" + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - macos-latest + - ubuntu-latest + - windows-latest + ruby: + # This includes rubies that are not actually extended by this gem. + # We want to make sure the gem silently fails to load on those platforms. + - "2" + - "3.0" + - "jruby" + - "truffleruby" + exclude: + # Windows releases of jruby and truffleruby have issues. Skip them for now. + - { ruby: "jruby", os: "windows-latest" } + - { ruby: "truffleruby", os: "windows-latest" } + + steps: + + - uses: actions/checkout@v2 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + + - name: Build gem + run: bundle exec gem build --verbose debug_inspector.gemspec + + - name: Install gem + run: gem install --verbose debug_inspector*.gem + + - name: Create directory for gem test + run: mkdir -p tmp/gem-test + + - name: Create test Gemfile + run: echo "gem 'debug_inspector'" > Gemfile + working-directory: ./tmp/gem-test + + - name: Get gem installation path + id: gem_path + run: | + gem_path=$(bundle show debug_inspector) + echo "gem_path is ${gem_path}" + echo "::set-output name=path::${gem_path}" + shell: bash + working-directory: ./tmp/gem-test + + - name: List installed gem contents + run: find . + shell: bash + working-directory: ${{ steps.gem_path.outputs.path }} + + - name: Test gem load + run: bundle exec ruby -e "require 'debug_inspector'" + working-directory: ./tmp/gem-test + + - name: Test gem functionality + if: ${{ matrix.ruby != 'jruby' && matrix.ruby != 'truffleruby' }} + run: bundle exec ruby -e "require 'debug_inspector'; RubyVM::DebugInspector.open { |dc| dc.frame_binding(1) }" + working-directory: ./tmp/gem-test diff --git a/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/.gitignore b/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/.gitignore new file mode 100644 index 0000000..70e63b1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/.gitignore @@ -0,0 +1,16 @@ +Gemfile.lock + +/.bundle/ +/.yardoc +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +/tmp/ +*.bundle +*.so +*.o +*.a +mkmf.log +Makefile diff --git a/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/Gemfile b/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/Gemfile new file mode 100644 index 0000000..d5ebb79 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/Gemfile @@ -0,0 +1,8 @@ +source "https://rubygems.org" + +# Specify your gem's dependencies in debug_inspector.gemspec +gemspec + +gem "rake" +gem "rake-compiler" +gem "minitest" diff --git a/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/README.md b/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/README.md new file mode 100644 index 0000000..df72677 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/README.md @@ -0,0 +1,80 @@ +[![Build Status](https://github.com/banister/debug_inspector/workflows/Test/badge.svg?branch=master&event=push)](https://github.com/banister/debug_inspector/actions?query=branch%3Amaster) +[![Gem Version](https://img.shields.io/gem/v/debug_inspector.svg)](https://rubygems.org/gems/debug_inspector) + +debug_inspector +=============== + +_A Ruby wrapper for the MRI 2.0+ debug\_inspector API_ + +The `debug_inspector` C extension and API were designed and built by [Koichi Sasada](https://github.com/ko1), this project +is just a gemification of his work. + +**NOTES:** + +* **Do not use this library outside of debugging situations**. +* This library makes use of the debug inspector API which was new in MRI 2.0.0. +* Only works on MRI 2 and 3. Requiring it on unsupported Rubies will result in a no-op + +Usage +----- + +```ruby +require 'debug_inspector' + +# Open debug context +# Passed `dc' is only active in a block +RubyVM::DebugInspector.open { |dc| + # backtrace locations (returns an array of Thread::Backtrace::Location objects) + locs = dc.backtrace_locations + + # you can get depth of stack frame with `locs.size' + locs.size.times do |i| + # binding of i-th caller frame (returns a Binding object or nil) + p dc.frame_binding(i) + + # iseq of i-th caller frame (returns a RubyVM::InstructionSequence object or nil) + p dc.frame_iseq(i) + + # class of i-th caller frame + p dc.frame_class(i) + end +} +``` + +Development +----------- + +After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. + +To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). + +Contact +------- + +Problems or questions contact me at [github](http://github.com/banister) + +License +------- + +(The MIT License) + +Copyright (c) 2012-2013 (John Mair) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/Rakefile b/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/Rakefile new file mode 100755 index 0000000..e1323f1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/Rakefile @@ -0,0 +1,27 @@ +require "bundler/gem_tasks" +require "rake/testtask" + +def can_compile_extensions? + RUBY_ENGINE == "ruby" +end + +Rake::TestTask.new(:test) do |t| + t.libs << "test" + t.libs << "lib" + t.test_files = FileList["test/**/*_test.rb"] + t.warning = true + t.verbose = true +end + +require "rake/extensiontask" + +if can_compile_extensions? + task :build => :compile + task :default => [:clobber, :compile, :test] +else + task :default => [:test] +end + +Rake::ExtensionTask.new("debug_inspector") do |ext| + ext.lib_dir = "lib" +end diff --git a/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/debug_inspector.gemspec b/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/debug_inspector.gemspec new file mode 100644 index 0000000..a4d88ba --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/debug_inspector.gemspec @@ -0,0 +1,37 @@ +# We don't want to define any constants if the gem extension isn't loaded, so not requiring the version file. + +Gem::Specification.new do |spec| + spec.name = "debug_inspector" + spec.version = "1.1.0" + spec.authors = ["John Mair (banisterfiend)"] + spec.email = ["jrmair@gmail.com"] + + spec.summary = %q{A Ruby wrapper for the MRI 2.0 debug_inspector API} + spec.description = <<-TXT +Adds methods to RubyVM::DebugInspector to allow for inspection of backtrace frames. + +The debug_inspector C extension and API were designed and built by Koichi Sasada, this project is just a gemification of his work. + +This library makes use of the debug inspector API which was added to MRI 2.0.0. +Only works on MRI 2 and 3. Requiring it on unsupported Rubies will result in a no-op. + +Recommended for use only in debugging situations. Do not use this in production apps. +TXT + spec.homepage = "https://github.com/banister/debug_inspector" + spec.license = "MIT" + spec.required_ruby_version = Gem::Requirement.new(">= 2.0") + + spec.metadata["homepage_uri"] = spec.homepage + spec.metadata["source_code_uri"] = "https://github.com/banister/debug_inspector" + spec.metadata["changelog_uri"] = "https://github.com/banister/debug_inspector/releases" + + spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do + `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|bin)/}) } + end + + spec.require_paths = ["lib"] + + if RUBY_ENGINE == "ruby" + spec.extensions = ["ext/debug_inspector/extconf.rb"] + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/ext/debug_inspector/.sitearchdir.time b/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/ext/debug_inspector/.sitearchdir.time new file mode 100644 index 0000000..e69de29 diff --git a/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/ext/debug_inspector/debug_inspector.c b/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/ext/debug_inspector/debug_inspector.c new file mode 100644 index 0000000..704d86e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/ext/debug_inspector/debug_inspector.c @@ -0,0 +1,117 @@ +/********************************************************************** + + debug_inspector.c + + $Author: ko1 $ + created at: Thu Nov 15 17:34:36 2012 + + Copyright (C) 1993-2012 Yukihiro Matsumoto + +**********************************************************************/ + +#include "ruby/ruby.h" +#include "ruby/debug.h" + +static size_t +di_size(const void *dummy) +{ + return sizeof(void *); +} + +static const rb_data_type_t di_data_type = { + "simple_debugger", + {0, 0, di_size,}, +}; + +static const rb_debug_inspector_t * +di_get_dc(VALUE self) +{ + const rb_debug_inspector_t *dc; + TypedData_Get_Struct(self, const rb_debug_inspector_t, &di_data_type, dc); + if (dc == 0) { + rb_raise(rb_eArgError, "invalid debug context"); + } + return dc; +} + +static VALUE +di_backtrace_locations(VALUE self) +{ + const rb_debug_inspector_t *dc = di_get_dc(self); + return rb_debug_inspector_backtrace_locations(dc); +} + +static VALUE +di_binding(VALUE self, VALUE index) +{ + const rb_debug_inspector_t *dc = di_get_dc(self); + return rb_debug_inspector_frame_binding_get(dc, NUM2INT(index)); +} + +static VALUE +di_frame_class(VALUE self, VALUE index) +{ + const rb_debug_inspector_t *dc = di_get_dc(self); + return rb_debug_inspector_frame_class_get(dc, NUM2INT(index)); +} + +static VALUE +di_frame_iseq(VALUE self, VALUE index) +{ + const rb_debug_inspector_t *dc = di_get_dc(self); + return rb_debug_inspector_frame_iseq_get(dc, NUM2INT(index)); +} + +static VALUE +di_frame_self(VALUE self, VALUE index) +{ + const rb_debug_inspector_t *dc = di_get_dc(self); + return rb_debug_inspector_frame_self_get(dc, NUM2INT(index)); +} + +static VALUE +breakpoint_i(const rb_debug_inspector_t *dc, void *ptr) +{ + VALUE self = (VALUE)ptr; + VALUE result; + + /* should protect */ + DATA_PTR(self) = (void *)dc; + result = rb_yield(self); + return result; +} + +static VALUE +di_open_body(VALUE self) +{ + return rb_debug_inspector_open(breakpoint_i, (void *)self); +} + +static VALUE +di_open_ensure(VALUE self) +{ + DATA_PTR(self) = 0; + return self; +} + +static VALUE +di_open_s(VALUE klass) +{ + VALUE self = TypedData_Wrap_Struct(klass, &di_data_type, 0); + return rb_ensure(di_open_body, self, di_open_ensure, self); +} + +void +Init_debug_inspector(void) +{ + VALUE rb_cRubyVM = rb_const_get(rb_cObject, rb_intern("RubyVM")); + VALUE cDebugInspector = rb_define_class_under(rb_cRubyVM, "DebugInspector", rb_cObject); + + rb_undef_alloc_func(cDebugInspector); + rb_define_singleton_method(cDebugInspector, "open", di_open_s, 0); + rb_define_method(cDebugInspector, "backtrace_locations", di_backtrace_locations, 0); + rb_define_method(cDebugInspector, "frame_binding", di_binding, 1); + rb_define_method(cDebugInspector, "frame_class", di_frame_class, 1); + rb_define_method(cDebugInspector, "frame_iseq", di_frame_iseq, 1); + rb_define_method(cDebugInspector, "frame_self", di_frame_self, 1); +} diff --git a/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/ext/debug_inspector/extconf.rb b/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/ext/debug_inspector/extconf.rb new file mode 100755 index 0000000..fd199ee --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/ext/debug_inspector/extconf.rb @@ -0,0 +1,19 @@ +def fake_makefile + File.open("Makefile", "w") { |f| + f.puts '.PHONY: install' + f.puts 'install:' + f.puts "\t" + '@echo "This Ruby not supported by/does not require debug_inspector."' + } +end + +def mri_2_or_3? + defined?(RUBY_ENGINE) && RUBY_ENGINE == "ruby" && + RUBY_VERSION =~ /^[23]/ +end + +if mri_2_or_3? + require 'mkmf' + create_makefile('debug_inspector') +else + fake_makefile +end diff --git a/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/lib/debug_inspector.rb b/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/lib/debug_inspector.rb new file mode 100644 index 0000000..9d617ef --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/lib/debug_inspector.rb @@ -0,0 +1,17 @@ +require 'rbconfig' +dlext = RbConfig::CONFIG['DLEXT'] +begin + # If the installation task did its job, the extension is in lib/ next to this file. + require "debug_inspector.#{dlext}" + # We only want to define constants if the extension has loaded. + require_relative "rubyvm/debug_inspector/version" +rescue LoadError + begin + # If not, maybe the extension is in ext/ + require_relative "../ext/debug_inspector/debug_inspector.#{dlext}" + # We only want to define constants if the extension has loaded. + require_relative "rubyvm/debug_inspector/version" + rescue LoadError => e + puts "debug_inspector extension was not loaded" + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/lib/rubyvm/debug_inspector/version.rb b/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/lib/rubyvm/debug_inspector/version.rb new file mode 100644 index 0000000..a8cd308 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/debug_inspector-1.1.0/lib/rubyvm/debug_inspector/version.rb @@ -0,0 +1,4 @@ +class RubyVM::DebugInspector + # Don't forget to update the version string in the gemspec file. + VERSION = "1.1.0" +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/.rspec b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/.rspec new file mode 100644 index 0000000..53607ea --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/.rspec @@ -0,0 +1 @@ +--colour diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/Code-of-Conduct.md b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/Code-of-Conduct.md new file mode 100644 index 0000000..d05f4bc --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/Code-of-Conduct.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at [INSERT EMAIL ADDRESS]. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/Contributing.md b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/Contributing.md new file mode 100644 index 0000000..425f49f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/Contributing.md @@ -0,0 +1,119 @@ +## Contributing + +I value any contribution to Diff::LCS you can provide: a bug report, a +feature request, or code contributions. Code contributions to Diff::LCS are +especially welcomeencouraged. Because Diff::LCS is a complex +codebase, there are a few guidelines: + +- Code changes _will not_ be accepted without tests. The test suite is + written with [RSpec][]. +- Match my coding style. +- Use a thoughtfully-named topic branch that contains your change. Rebase + your commits into logical chunks as necessary. +- Use [quality commit messages][]. +- Do not change the version number; when your patch is accepted and a release + is made, the version will be updated at that point. +- Submit a GitHub pull request with your changes. +- New or changed behaviours require appropriate documentation. + +### Test Dependencies + +Diff::LCS uses Ryan Davis’s [Hoe][] to manage the release process, and it +adds a number of rake tasks. You will mostly be interested in: + +```sh +$ rake +``` + +which runs the tests the same way that: + +```sh +$ rake spec +``` + +will do. + +To assist with the installation of the development dependencies, I have +provided a Gemfile pointing to the (generated) `diff-lcs.gemspec` file. This +will permit you to do: + +```sh +$ bundle install +``` + +to get the development dependencies. If you aleady have `hoe` installed, you +can accomplish the same thing with: + +```sh +$ rake newb +``` + +This task will install any missing dependencies, run the tests/specs, and +generate the RDoc. + +You can run tests with code coverage analysis by running: + +```sh +$ rake spec:coverage +``` + +### Workflow + +Here's the most direct way to get your work merged into the project: + +- Fork the project. +- Clone down your fork (`git clone git://github.com//diff-lcs.git`). +- Create a topic branch to contain your change (`git checkout -b my_awesome_feature`). +- Hack away, add tests. Not necessarily in that order. +- Make sure everything still passes by running `rake`. +- If necessary, rebase your commits into logical chunks, without errors. +- Push the branch up (`git push origin my_awesome_feature`). +- Create a pull request against halostatue/diff-lcs and describe what your + change does and the why you think it should be merged. + +### Contributors + +- Austin Ziegler created Diff::LCS. + +Thanks to everyone else who has contributed code or bug reports to Diff::LCS: + +- @ginriki +- @joshbronson +- @kevinmook +- @mckaz +- Akinori Musha +- Artem Ignatyev +- Brandon Fish +- Camille Drapier +- Cédric Boutillier +- Gregg Kellogg +- Jagdeep Singh +- Jason Gladish +- Jon Rowe +- Josef Strzibny +- Josep (@apuratepp) +- Josh Bronson +- Jun Aruga +- Kenichi Kamiya +- Kensuke Nagae +- Kevin Ansfield +- Koichi Ito +- Mark Friedgan +- Michael Granger +- Myron Marston +- Nicolas Leger +- Oleg Orlov +- Paul Kunysch +- Pete Higgins +- Peter Wagenet +- Philippe Lafoucrière +- Ryan Lovelett +- Scott Steele +- Simon Courtois +- Tien (@tiendo1011) +- Tomas Jura +- Vít Ondruch + +[rspec]: http://rspec.info/documentation/ +[quality commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html +[hoe]: https://github.com/seattlerb/hoe diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/History.md b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/History.md new file mode 100644 index 0000000..d426e95 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/History.md @@ -0,0 +1,400 @@ +# History + +## 1.5.0 / 2021-12-23 + +- Updated the CI configuration and monkey-patch Hoe. + +- Kenichi Kamiya fixed a test configuration deprecation in SimpleCov. [#69] + +- Tien introduced several corrections and code improvements: + + - Removed an off-by-one error when calculating an index value by embracing + Ruby iteration properly. This had a side-effect of fixing a long-standing + bug in `#traverse_sequences` where the traversal would not be transitive. + That is, `LCS(s2, s1)` should produce a sequence that is transitive with + `LCS(s1, s2)` on traversal, and applying the diff computed from those + results would result in equivalent changes that could be played forward or + backward as appropriate. [#71], [#75] + + - The above fix resulted in a changed order of the longest common subsequence + when callbacks were applied. After analysis, it was determined that the + computed subsequence was _equivalent_ to the prior version, so the test was + updated. This also resulted in the clarification of documentation when + traversing the subsequences. [#79] + + - An infinite loop case in the case where Diff::LCS would be included into an + enumerable class has been fixed. [#73] + + - Clarified the purpose of a threshold test in calculation of LCS. [#72], + [#80] + +- Removed autotest directory + +## 1.4.4 / 2020-07-01 + +- Fixed an issue reported by Jun Aruga in the Diff::LCS::Ldiff binary text + detection. [#44] + +- Fixed a theoretical issue reported by Jun Aruga in Diff::LCS::Hunk to raise + a more useful exception. [#43] + +- Added documentation that should address custom object issues as reported in + [#35]. + +- Fixed more diff errors, in part reported in [#65]. + + - The use of `Numeric#abs` is incorrect in `Diff::LCS::Block#diff_size`. The + diff size _must_ be accurate for correct change placement. + + - When selecting @max_diff_size in Diff::LCS::Hunk, choose it based on + `block.diff_size.abs`. + + - Made a number of changes that will, unfortunately, increase allocations at + the cost of being safe with frozen strings. + + - Add some knowledge that when `Diff::LCS::Hunk#diff` is called, that we are + processing the _last_ hunk, so some changes will be made to how the output + is generated. + + - `old`, `ed`, and `reverse_ed` formats have no differences. + + - `unified` format will report `\ No newline at end of file` given the + correct conditions, at most once. Unified range reporting also differs for + the last hunk such that the `length` of the range is reduced by one. + + - `context` format will report `\No newline at end of file` given the + correct conditions, up to once per "file". Context range reporting also + differs for the last hunk such that the `end` part of the range is reduced + by one to a minimum of one. + +- Added a bunch more tests for the cases above, and fixed `hunk_spec.rb` so that + the phrase being compared isn't nonsense French. + +- Updated formatting. + +- Added a Rake task to assist with manual testing on Ruby 1.8. + +## 1.4.3 / 2020-06-29 + +- Fixed several issues with the 1.4 on Rubies older than 2.0. Some of this was + providing useful shim functions to Hoe 3.x (which dropped these older Rubies + a while ago). Specifically: + + - Removed Array#lazy from a method in Diff::LCS::Hunk. + + - Changed some unit tests to use old-style Symbol-keyed hashes. + + - Changed some unit test helper functions to no longer use keyword parameters, + but only a trailing options hash. + + - Made the use of `psych` dependent on `RUBY_VERSION >= 1.9`. + + Resolves [#63]. + +## 1.4.2 / 2020-06-23 + +- Camille Drapier fixed a small issue with RuboCop configuration. [#59] + +- Applied another fix (and unit test) to fix an issue for the Chef team. + [#60], [#61] + +## 1.4.1 / 2020-06-23 + +- Fix an issue where diff sizes could be negative, and they should be. [#57], + [#58] + +## 1.4 / 2020-06-23 + +- Ruby versions lower than 2.4 are soft-deprecated and will not be run as part + of the CI process any longer. + +- Akinora MUSHA (knu) added the ability for Diff::LCS::Change objects to be + implicitly treated arrays. Originally provided as pull request [#47], but it + introduced a number of test failures as documented in [#48], and remediation + of Diff::LCS itself was introduced in [#49]. + +- Resolved [#5] with some tests comparing output from `system` calls to + `bin/ldiff` with some pre-generated output. Resolved [#6] with these tests. + +- Resolved a previously undetected `bin/ldiff` issue with `--context` output not + matching `diff --context` output. + +- Resolved an issue with later versions of Ruby not working with an `OptParse` + specification of `Numeric`; this has been changed to `Integer`. + +- Brandon Fish added truffleruby in [#52]. + +- Fixed two missing classes as reported in [#53]. + +## 1.3 / 2017-01-18 + +- Bugs fixed: + + - Fixed an error for bin/ldiff --version. Fixes issue [#21]. + + - Force Diff::LCS::Change and Diff::LCS::ContextChange to only perform + equality comparisons against themselves. Provided by Kevin Mook in pull + request [#29]. + + - Fix tab expansion in htmldiff, provided by Mark Friedgan in pull request + [#25]. + + - Silence Ruby 2.4 Fixnum deprecation warnings. Fixes issue [#38] and pull + request [#36]. + + - Ensure that test dependencies are loaded properly. Fixes issue [#33] and + pull request [#34]. + + - Fix issue [#1] with incorrect intuition of patch direction. Tentative fix, + but the previous failure cases pass now. + +- Tooling changes: + + - Added SimpleCov and Coveralls support. + + - Change the homepage (temporarily) to the GitHub repo. + + - Updated testing and gem infrastructure. + + - Modernized the specs. + +- Cleaned up documentation. + +- Added a Code of Conduct. + +## 1.2.5 / 2013-11-08 + +- Bugs fixed: + + - Comparing arrays flattened them too far, especially with Diff::LCS.sdiff. + Fixed by Josh Bronson in pull request [#23]. + +## 1.2.4 / 2013-04-20 + +- Bugs fixed: + + - A bug was introduced after 1.1.3 when pruning common sequences at the start + of comparison. Paul Kunysch (@pck) fixed this in pull request [#18]. Thanks! + + - The Rubinius (1.9 mode) bug in [rubinius/rubinius#2268] has been fixed by + the Rubinius team two days after it was filed. Thanks for fixing this so + quickly! + +- Switching to Raggi's hoe-gemspec2 for gemspec generation. + +## 1.2.3 / 2013-04-11 + +- Bugs Fixed: + + - The new encoding detection for diff output generation (added in 1.2.2) + introduced a bug if the left side of the comparison was the empty set. + Originally found in [rspec/rspec-expectations#238] and + [rspec/rspec-expectations#239]. Jon Rowe developed a reasonable heuristic + (left side, right side, empty string literal) to avoid this bug. + + - There is a known issue with Rubinius in 1.9 mode reported in + [rubinius/rubinius#2268] and demonstrated in the Travis CI builds. For all + other tested platforms, diff-lcs is considered stable. As soon as a suitably + small test-case can be created for the Rubinius team to examine, this will + be added to the Rubinius issue around this. + +## 1.2.2 / 2013-03-30 + +- Bugs Fixed: + + - Diff::LCS::Hunk could not properly generate a difference for comparison sets + that are not US-ASCII-compatible because of the use of literal regular + expressions and strings. Jon Rowe found this in + [rspec/rspec-expectations#219] and provided a first pass implementation in + pull request [#15]. I've reworked it because of test failures in Rubinius + when running in Ruby 1.9 mode. This coerces the added values to the encoding + of the old dataset (as determined by the first piece of the old dataset). + + - Adding Travis CI testing for Ruby 2.0. + +## 1.2.1 / 2013-02-09 + +- Bugs Fixed: + + - As seen in [rspec/rspec-expectations#200], the release of Diff::LCS 1.2 + introduced an unnecessary public API change to Diff::LCS::Hunk (see the + change at [rspec/rspec-expectations@3d6fc82c] for details). The new method + name (and behaviour) is more correct, but I should not have renamed the + function or should have at least provided an alias. This release restores + Diff::LCS::Hunk#unshift as an alias to #merge. Note that the old #unshift + behaviour was incorrect and will not be restored. + +## 1.2.0 / 2013-01-21 + +- Minor Enhancements: + + - Added special case handling for Diff::LCS.patch so that it handles patches + that are empty or contain no changes. + + - Added two new methods (#patch_me and #unpatch_me) to the includable module. + +- Bugs Fixed: + + - Fixed issue [#1] patch direction detection. + + - Resolved issue [#2] by handling `string[string.size, 1]` properly (it + returns `""` not `nil`). + + - Michael Granger (ged) fixed an implementation error in Diff::LCS::Change and + added specs in pull request [#8]. Thanks! + + - Made the code auto-testable. + + - Vít Ondruch (voxik) provided the latest version of the GPL2 license file in + pull request [#10]. Thanks! + + - Fixed a documentation issue with the includable versions of #patch! and + #unpatch! where they implied that they would replace the original value. + Given that Diff::LCS.patch always returns a copy, the documentation was + incorrect and has been corrected. To provide the behaviour that was + originally documented, two new methods were added to provide this behaviour. + Found by scooter-dangle in issue [#12]. Thanks! + +- Code Style Changes: + + - Removed trailing spaces. + + - Calling class methods using `.` instead of `::`. + + - Vít Ondruch (voxik) removed unnecessary shebangs in pull request [#9]. + Thanks! + + - Kenichi Kamiya (kachick) removed some warnings of an unused variable in + lucky pull request [#13]. Thanks! + + - Embarked on a major refactoring to make the files a little more manageable + and understand the code on a deeper level. + + - Adding to http://travis-ci.org. + +## 1.1.3 / 2011-08-27 + +- Converted to 'hoe' for release. + +- Converted tests to RSpec 2. + +- Extracted the body of htmldiff into a class available from diff/lcs/htmldiff. + +- Migrated development and issue tracking to GitHub. + +- Bugs fixed: + + - Eliminated the explicit use of RubyGems in both bin/htmldiff and bin/ldiff. + Resolves issue [#4]. + + - Eliminated Ruby warnings. Resolves issue [#3]. + +## 1.1.2 / 2004-10-20 + +- Fixed a problem reported by Mauricio Fernandez in htmldiff. + +## 1.1.1 / 2004-09-25 + +- Fixed bug #891 (Set returned from patch command does not contain last equal + part). + +- Fixed a problem with callback initialisation code (it assumed that all + callbacks passed as classes can be initialised; now, it rescues NoMethodError + in the event of private :new being called). + +- Modified the non-initialisable callbacks to have a private #new method. + +- Moved ldiff core code to Diff::LCS::Ldiff (diff/lcs/ldiff.rb). + +## 1.1.0 + +- Eliminated the need for Diff::LCS::Event and removed it. + +- Added a contextual diff callback, Diff::LCS::ContextDiffCallback. + +- Implemented patching/unpatching for standard Diff callback output formats with + both #diff and #sdiff. + +- Extensive documentation changes. + +## 1.0.4 + +- Fixed a problem with bin/ldiff output, especially for unified format. Newlines + that should have been present weren't. + +- Changed the .tar.gz installer to generate Windows batch files if ones do not + exist already. Removed the existing batch files as they didn't work. + +## 1.0.3 + +- Fixed a problem with #traverse_sequences where the first difference from the + left sequence might not be appropriately captured. + +## 1.0.2 + +- Fixed an issue with ldiff not working because actions were changed from + symbols to strings. + +## 1.0.1 + +- Minor modifications to the gemspec, the README. + +- Renamed the diff program to ldiff (as well as the companion batch file) so as + to not collide with the standard diff program. + +- Fixed issues with RubyGems. Requires RubyGems > 0.6.1 or >= 0.6.1 with the + latest CVS version. + +## 1.0 + +- Initial release based mostly on Perl's Algorithm::Diff. + +[rubinius/rubinius#2268]: https://github.com/rubinius/rubinius/issues/2268 +[rspec/rspec-expectations#239]: https://github.com/rspec/rspec-expectations/issues/239 +[rspec/rspec-expectations#238]: https://github.com/rspec/rspec-expectations/issues/238 +[rspec/rspec-expectations#219]: https://github.com/rspec/rspec-expectations/issues/219 +[rspec/rspec-expectations@3d6fc82c]: https://github.com/rspec/rspec-expectations/commit/3d6fc82c +[rspec/rspec-expectations#200]: https://github.com/rspec/rspec-expectations/pull/200 +[#1]: https://github.com/halostatue/diff-lcs/issues/1 +[#2]: https://github.com/halostatue/diff-lcs/issues/2 +[#3]: https://github.com/halostatue/diff-lcs/issues/3 +[#4]: https://github.com/halostatue/diff-lcs/issues/4 +[#5]: https://github.com/halostatue/diff-lcs/issues/5 +[#6]: https://github.com/halostatue/diff-lcs/issues/6 +[#8]: https://github.com/halostatue/diff-lcs/pull/8 +[#9]: https://github.com/halostatue/diff-lcs/pull/9 +[#10]: https://github.com/halostatue/diff-lcs/pull/10 +[#12]: https://github.com/halostatue/diff-lcs/issues/12 +[#13]: https://github.com/halostatue/diff-lcs/pull/13 +[#15]: https://github.com/halostatue/diff-lcs/pull/15 +[#18]: https://github.com/halostatue/diff-lcs/pull/18 +[#21]: https://github.com/halostatue/diff-lcs/issues/21 +[#23]: https://github.com/halostatue/diff-lcs/pull/23 +[#25]: https://github.com/halostatue/diff-lcs/pull/25 +[#29]: https://github.com/halostatue/diff-lcs/pull/29 +[#33]: https://github.com/halostatue/diff-lcs/issues/33 +[#34]: https://github.com/halostatue/diff-lcs/pull/34 +[#35]: https://github.com/halostatue/diff-lcs/issues/35 +[#36]: https://github.com/halostatue/diff-lcs/pull/36 +[#38]: https://github.com/halostatue/diff-lcs/issues/38 +[#43]: https://github.com/halostatue/diff-lcs/issues/43 +[#44]: https://github.com/halostatue/diff-lcs/issues/44 +[#47]: https://github.com/halostatue/diff-lcs/pull/47 +[#48]: https://github.com/halostatue/diff-lcs/issues/48 +[#49]: https://github.com/halostatue/diff-lcs/pull/49 +[#52]: https://github.com/halostatue/diff-lcs/pull/52 +[#53]: https://github.com/halostatue/diff-lcs/issues/53 +[#57]: https://github.com/halostatue/diff-lcs/issues/57 +[#58]: https://github.com/halostatue/diff-lcs/pull/58 +[#59]: https://github.com/halostatue/diff-lcs/pull/59 +[#60]: https://github.com/halostatue/diff-lcs/issues/60 +[#61]: https://github.com/halostatue/diff-lcs/pull/61 +[#63]: https://github.com/halostatue/diff-lcs/issues/63 +[#65]: https://github.com/halostatue/diff-lcs/issues/65 +[#69]: https://github.com/halostatue/diff-lcs/issues/69 +[#71]: https://github.com/halostatue/diff-lcs/issues/71 +[#72]: https://github.com/halostatue/diff-lcs/issues/72 +[#73]: https://github.com/halostatue/diff-lcs/issues/73 +[#75]: https://github.com/halostatue/diff-lcs/issues/75 +[#79]: https://github.com/halostatue/diff-lcs/issues/79 +[#80]: https://github.com/halostatue/diff-lcs/issues/80 diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/License.md b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/License.md new file mode 100644 index 0000000..63b763d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/License.md @@ -0,0 +1,39 @@ +== License + +This software is available under three licenses: the GNU GPL version 2 (or at +your option, a later version), the Perl Artistic license, or the MIT license. +Note that my preference for licensing is the MIT license, but Algorithm::Diff +was dually originally licensed with the Perl Artistic and the GNU GPL ("the +same terms as Perl itself") and given that the Ruby implementation originally +hewed pretty closely to the Perl version, I must maintain the additional +licensing terms. + +* Copyright 2004–2013 Austin Ziegler. +* Adapted from Algorithm::Diff (Perl) by Ned Konz and a Smalltalk version by + Mario I. Wolczko. + +=== MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +=== Perl Artistic License (version 2) +See the file docs/artistic.txt in the main distribution. + +=== GNU GPL version 2 +See the file docs/COPYING.txt in the main distribution. diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/Manifest.txt b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/Manifest.txt new file mode 100644 index 0000000..0d7dcb3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/Manifest.txt @@ -0,0 +1,59 @@ +.rspec +Code-of-Conduct.md +Contributing.md +History.md +License.md +Manifest.txt +README.rdoc +Rakefile +bin/htmldiff +bin/ldiff +docs/COPYING.txt +docs/artistic.txt +lib/diff-lcs.rb +lib/diff/lcs.rb +lib/diff/lcs/array.rb +lib/diff/lcs/backports.rb +lib/diff/lcs/block.rb +lib/diff/lcs/callbacks.rb +lib/diff/lcs/change.rb +lib/diff/lcs/htmldiff.rb +lib/diff/lcs/hunk.rb +lib/diff/lcs/internals.rb +lib/diff/lcs/ldiff.rb +lib/diff/lcs/string.rb +spec/change_spec.rb +spec/diff_spec.rb +spec/fixtures/aX +spec/fixtures/bXaX +spec/fixtures/ds1.csv +spec/fixtures/ds2.csv +spec/fixtures/ldiff/output.diff +spec/fixtures/ldiff/output.diff-c +spec/fixtures/ldiff/output.diff-e +spec/fixtures/ldiff/output.diff-f +spec/fixtures/ldiff/output.diff-u +spec/fixtures/ldiff/output.diff.chef +spec/fixtures/ldiff/output.diff.chef-c +spec/fixtures/ldiff/output.diff.chef-e +spec/fixtures/ldiff/output.diff.chef-f +spec/fixtures/ldiff/output.diff.chef-u +spec/fixtures/ldiff/output.diff.chef2 +spec/fixtures/ldiff/output.diff.chef2-c +spec/fixtures/ldiff/output.diff.chef2-d +spec/fixtures/ldiff/output.diff.chef2-e +spec/fixtures/ldiff/output.diff.chef2-f +spec/fixtures/ldiff/output.diff.chef2-u +spec/fixtures/new-chef +spec/fixtures/new-chef2 +spec/fixtures/old-chef +spec/fixtures/old-chef2 +spec/hunk_spec.rb +spec/issues_spec.rb +spec/lcs_spec.rb +spec/ldiff_spec.rb +spec/patch_spec.rb +spec/sdiff_spec.rb +spec/spec_helper.rb +spec/traverse_balanced_spec.rb +spec/traverse_sequences_spec.rb diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/README.rdoc b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/README.rdoc new file mode 100644 index 0000000..6092424 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/README.rdoc @@ -0,0 +1,84 @@ += Diff::LCS + +home :: https://github.com/halostatue/diff-lcs +code :: https://github.com/halostatue/diff-lcs +bugs :: https://github.com/halostatue/diff-lcs/issues +rdoc :: http://rubydoc.info/github/halostatue/diff-lcs +continuous integration :: {}[https://github.com/halostatue/diff-lcs/actions] + +== Description + +Diff::LCS computes the difference between two Enumerable sequences using the +McIlroy-Hunt longest common subsequence (LCS) algorithm. It includes utilities +to create a simple HTML diff output format and a standard diff-like tool. + +This is release 1.4.3, providing a simple extension that allows for +Diff::LCS::Change objects to be treated implicitly as arrays and fixes a +number of formatting issues. + +Ruby versions below 2.5 are soft-deprecated, which means that older versions +are no longer part of the CI test suite. If any changes have been introduced +that break those versions, bug reports and patches will be accepted, but it +will be up to the reporter to verify any fixes prior to release. The next +major release will completely break compatibility. + +== Synopsis + +Using this module is quite simple. By default, Diff::LCS does not extend +objects with the Diff::LCS interface, but will be called as if it were a +function: + + require 'diff/lcs' + + seq1 = %w(a b c e h j l m n p) + seq2 = %w(b c d e f j k l m r s t) + + lcs = Diff::LCS.LCS(seq1, seq2) + diffs = Diff::LCS.diff(seq1, seq2) + sdiff = Diff::LCS.sdiff(seq1, seq2) + seq = Diff::LCS.traverse_sequences(seq1, seq2, callback_obj) + bal = Diff::LCS.traverse_balanced(seq1, seq2, callback_obj) + seq2 == Diff::LCS.patch!(seq1, diffs) + seq1 == Diff::LCS.unpatch!(seq2, diffs) + seq2 == Diff::LCS.patch!(seq1, sdiff) + seq1 == Diff::LCS.unpatch!(seq2, sdiff) + +Objects can be extended with Diff::LCS: + + seq1.extend(Diff::LCS) + lcs = seq1.lcs(seq2) + diffs = seq1.diff(seq2) + sdiff = seq1.sdiff(seq2) + seq = seq1.traverse_sequences(seq2, callback_obj) + bal = seq1.traverse_balanced(seq2, callback_obj) + seq2 == seq1.patch!(diffs) + seq1 == seq2.unpatch!(diffs) + seq2 == seq1.patch!(sdiff) + seq1 == seq2.unpatch!(sdiff) + +By requiring 'diff/lcs/array' or 'diff/lcs/string', Array or String will be +extended for use this way. + +Note that Diff::LCS requires a sequenced enumerable container, which means that +the order of enumeration is both predictable and consistent for the same set of +data. While it is theoretically possible to generate a diff for an unordered +hash, it will only be meaningful if the enumeration of the hashes is +consistent. In general, this will mean that containers that behave like String +or Array will perform best. + +== History + +Diff::LCS is a port of Perl's Algorithm::Diff that uses the McIlroy-Hunt +longest common subsequence (LCS) algorithm to compute intelligent differences +between two sequenced enumerable containers. The implementation is based on +Mario I. Wolczko's +{Smalltalk version 1.2}[ftp://st.cs.uiuc.edu/pub/Smalltalk/MANCHESTER/manchester/4.0/diff.st] +(1993) and Ned Konz's Perl version +{Algorithm::Diff 1.15}[http://search.cpan.org/~nedkonz/Algorithm-Diff-1.15/]. +Diff::LCS#sdiff and Diff::LCS#traverse_balanced were originally written for the +Perl version by Mike Schilli. + +The algorithm is described in A Fast Algorithm for Computing Longest Common +Subsequences, CACM, vol.20, no.5, pp.350-353, May 1977, with a few minor +improvements to improve the speed. A simplified description of the algorithm, +originally written for the Perl version, was written by Mark-Jason Dominus. diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/Rakefile b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/Rakefile new file mode 100644 index 0000000..fd7ee31 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/Rakefile @@ -0,0 +1,121 @@ +# frozen_string_literal: true + +require 'rubygems' +require 'rspec' +require 'rspec/core/rake_task' +require 'hoe' + +# This is required until https://github.com/seattlerb/hoe/issues/112 is fixed +class Hoe + def with_config + config = Hoe::DEFAULT_CONFIG + + rc = File.expand_path("~/.hoerc") + homeconfig = load_config(rc) + config = config.merge(homeconfig) + + localconfig = load_config(File.expand_path(File.join(Dir.pwd, ".hoerc"))) + config = config.merge(localconfig) + + yield config, rc + end + + def load_config(name) + File.exist?(name) ? safe_load_yaml(name) : {} + end + + def safe_load_yaml(name) + return safe_load_yaml_file(name) if YAML.respond_to?(:safe_load_file) + + data = IO.binread(name) + YAML.safe_load(data, permitted_classes: [Regexp]) + rescue + YAML.safe_load(data, [Regexp]) + end + + def safe_load_yaml_file(name) + YAML.safe_load_file(name, permitted_classes: [Regexp]) + rescue + YAML.safe_load_file(name, [Regexp]) + end +end + +Hoe.plugin :bundler +Hoe.plugin :doofus +Hoe.plugin :gemspec2 +Hoe.plugin :git + +if RUBY_VERSION < '1.9' + class Array #:nodoc: + def to_h + Hash[*flatten(1)] + end + end + + class Gem::Specification #:nodoc: + def metadata=(*); end + + def default_value(*); end + end + + class Object #:nodoc: + def caller_locations(*) + [] + end + end +end + +_spec = Hoe.spec 'diff-lcs' do + developer('Austin Ziegler', 'halostatue@gmail.com') + + require_ruby_version '>= 1.8' + + self.history_file = 'History.md' + self.readme_file = 'README.rdoc' + self.licenses = ['MIT', 'Artistic-2.0', 'GPL-2.0+'] + + extra_dev_deps << ['hoe-doofus', '~> 1.0'] + extra_dev_deps << ['hoe-gemspec2', '~> 1.1'] + extra_dev_deps << ['hoe-git', '~> 1.6'] + extra_dev_deps << ['hoe-rubygems', '~> 1.0'] + extra_dev_deps << ['rspec', '>= 2.0', '< 4'] + extra_dev_deps << ['rake', '>= 10.0', '< 14'] + extra_dev_deps << ['rdoc', '>= 6.3.1', '< 7'] +end + +desc "Run all specifications" +RSpec::Core::RakeTask.new(:spec) do |t| + rspec_dirs = %w(spec lib).join(":") + t.rspec_opts = ["-I#{rspec_dirs}"] +end + +Rake::Task["spec"].actions.uniq! { |a| a.source_location } + +task :default => :spec unless Rake::Task["default"].prereqs.include?("spec") +task :test => :spec unless Rake::Task["test"].prereqs.include?("spec") + +if RUBY_VERSION >= '2.0' && RUBY_ENGINE == 'ruby' + namespace :spec do + desc "Runs test coverage. Only works Ruby 2.0+ and assumes 'simplecov' is installed." + task :coverage do + ENV['COVERAGE'] = 'yes' + Rake::Task['spec'].execute + end + end +end + +task :ruby18 do + puts <<-MESSAGE +You are starting a barebones Ruby 1.8 docker environment. You will need to +do the following: + +- mv Gemfile.lock{,.v2} +- gem install bundler --version 1.17.2 --no-ri --no-rdoc +- ruby -S bundle +- rake + +Don't forget to restore your Gemfile.lock after testing. + + MESSAGE + sh "docker run -it --rm -v #{Dir.pwd}:/root/diff-lcs bellbind/docker-ruby18-rails2 bash -l" +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/bin/htmldiff b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/bin/htmldiff new file mode 100755 index 0000000..14114a7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/bin/htmldiff @@ -0,0 +1,35 @@ +#! /usr/bin/env ruby -w +# frozen_string_literal: true + +require 'diff/lcs' +require 'diff/lcs/htmldiff' + +begin + require 'text/format' +rescue LoadError + Diff::LCS::HTMLDiff.can_expand_tabs = false +end + +if ARGV.size < 2 or ARGV.size > 3 + warn "usage: #{File.basename($0)} old new [output.html]" + warn " #{File.basename($0)} old new > output.html" + exit 127 +end + +left = IO.read(ARGV[0]).split($/) +right = IO.read(ARGV[1]).split($/) + +options = { :title => "diff #{ARGV[0]} #{ARGV[1]}" } + +htmldiff = Diff::LCS::HTMLDiff.new(left, right, options) + +if ARGV[2] + File.open(ARGV[2], 'w') do |f| + htmldiff.options[:output] = f + htmldiff.run + end +else + htmldiff.run +end + +# vim: ft=ruby diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/bin/ldiff b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/bin/ldiff new file mode 100755 index 0000000..f4734f5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/bin/ldiff @@ -0,0 +1,9 @@ +#! /usr/bin/env ruby -w +# frozen_string_literal: true + +require 'diff/lcs' +require 'diff/lcs/ldiff' + +exit Diff::LCS::Ldiff.run(ARGV) + +# vim: ft=ruby diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/docs/COPYING.txt b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/docs/COPYING.txt new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/docs/COPYING.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/docs/artistic.txt b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/docs/artistic.txt new file mode 100644 index 0000000..c04639a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/docs/artistic.txt @@ -0,0 +1,127 @@ +The "Artistic License" + + Preamble + +The intent of this document is to state the conditions under which a +Package may be copied, such that the Copyright Holder maintains some +semblance of artistic control over the development of the package, +while giving the users of the package the right to use and distribute +the Package in a more-or-less customary fashion, plus the right to make +reasonable modifications. + +Definitions: + + "Package" refers to the collection of files distributed by the + Copyright Holder, and derivatives of that collection of files + created through textual modification. + + "Standard Version" refers to such a Package if it has not been + modified, or has been modified in accordance with the wishes + of the Copyright Holder as specified below. + + "Copyright Holder" is whoever is named in the copyright or + copyrights for the package. + + "You" is you, if you're thinking about copying or distributing + this Package. + + "Reasonable copying fee" is whatever you can justify on the + basis of media cost, duplication charges, time of people involved, + and so on. (You will not be required to justify it to the + Copyright Holder, but only to the computing community at large + as a market that must bear the fee.) + + "Freely Available" means that no fee is charged for the item + itself, though there may be fees involved in handling the item. + It also means that recipients of the item may redistribute it + under the same conditions they received it. + +1. You may make and give away verbatim copies of the source form of the +Standard Version of this Package without restriction, provided that you +duplicate all of the original copyright notices and associated disclaimers. + +2. You may apply bug fixes, portability fixes and other modifications +derived from the Public Domain or from the Copyright Holder. A Package +modified in such a way shall still be considered the Standard Version. + +3. You may otherwise modify your copy of this Package in any way, provided +that you insert a prominent notice in each changed file stating how and +when you changed that file, and provided that you do at least ONE of the +following: + + a) place your modifications in the Public Domain or otherwise make them + Freely Available, such as by posting said modifications to Usenet or + an equivalent medium, or placing the modifications on a major archive + site such as uunet.uu.net, or by allowing the Copyright Holder to include + your modifications in the Standard Version of the Package. + + b) use the modified Package only within your corporation or organization. + + c) rename any non-standard executables so the names do not conflict + with standard executables, which must also be provided, and provide + a separate manual page for each non-standard executable that clearly + documents how it differs from the Standard Version. + + d) make other distribution arrangements with the Copyright Holder. + +4. You may distribute the programs of this Package in object code or +executable form, provided that you do at least ONE of the following: + + a) distribute a Standard Version of the executables and library files, + together with instructions (in the manual page or equivalent) on where + to get the Standard Version. + + b) accompany the distribution with the machine-readable source of + the Package with your modifications. + + c) give non-standard executables non-standard names, and clearly + document the differences in manual pages (or equivalent), together + with instructions on where to get the Standard Version. + + d) make other distribution arrangements with the Copyright Holder. + +5. You may charge a reasonable copying fee for any distribution of this +Package. You may charge any fee you choose for support of this +Package. You may not charge a fee for this Package itself. However, +you may distribute this Package in aggregate with other (possibly +commercial) programs as part of a larger (possibly commercial) software +distribution provided that you do not advertise this Package as a +product of your own. You may embed this Package's interpreter within +an executable of yours (by linking); this shall be construed as a mere +form of aggregation, provided that the complete Standard Version of the +interpreter is so embedded. + +6. The scripts and library files supplied as input to or produced as +output from the programs of this Package do not automatically fall +under the copyright of this Package, but belong to whoever generated +them, and may be sold commercially, and may be aggregated with this +Package. If such scripts or library files are aggregated with this +Package via the so-called "undump" or "unexec" methods of producing a +binary executable image, then distribution of such an image shall +neither be construed as a distribution of this Package nor shall it +fall under the restrictions of Paragraphs 3 and 4, provided that you do +not represent such an executable image as a Standard Version of this +Package. + +7. C subroutines (or comparably compiled subroutines in other +languages) supplied by you and linked into this Package in order to +emulate subroutines and variables of the language defined by this +Package shall not be considered part of this Package, but are the +equivalent of input as in Paragraph 6, provided these subroutines do +not change the language in any way that would cause it to fail the +regression tests for the language. + +8. Aggregation of this Package with a commercial distribution is always +permitted provided that the use of this Package is embedded; that is, +when no overt attempt is made to make this Package's interfaces visible +to the end user of the commercial distribution. Such use shall not be +construed as a distribution of this Package. + +9. The name of the Copyright Holder may not be used to endorse or promote +products derived from this software without specific prior written permission. + +10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + The End diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff-lcs.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff-lcs.rb new file mode 100644 index 0000000..250392f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff-lcs.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require 'diff/lcs' diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs.rb new file mode 100644 index 0000000..288cfc2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs.rb @@ -0,0 +1,739 @@ +# frozen_string_literal: true + +module Diff; end unless defined? Diff # rubocop:disable Style/Documentation + +# == How Diff Works (by Mark-Jason Dominus) +# +# I once read an article written by the authors of +diff+; they said that they +# hard worked very hard on the algorithm until they found the right one. +# +# I think what they ended up using (and I hope someone will correct me, because +# I am not very confident about this) was the `longest common subsequence' +# method. In the LCS problem, you have two sequences of items: +# +# a b c d f g h j q z +# a b c d e f g i j k r x y z +# +# and you want to find the longest sequence of items that is present in both +# original sequences in the same order. That is, you want to find a new +# sequence *S* which can be obtained from the first sequence by deleting some +# items, and from the second sequence by deleting other items. You also want +# *S* to be as long as possible. In this case *S* is: +# +# a b c d f g j z +# +# From there it's only a small step to get diff-like output: +# +# e h i k q r x y +# + - + + - + + + +# +# This module solves the LCS problem. It also includes a canned function to +# generate +diff+-like output. +# +# It might seem from the example above that the LCS of two sequences is always +# pretty obvious, but that's not always the case, especially when the two +# sequences have many repeated elements. For example, consider +# +# a x b y c z p d q +# a b c a x b y c z +# +# A naive approach might start by matching up the +a+ and +b+ that appear at +# the beginning of each sequence, like this: +# +# a x b y c z p d q +# a b c a b y c z +# +# This finds the common subsequence +a b c z+. But actually, the LCS is +a x b +# y c z+: +# +# a x b y c z p d q +# a b c a x b y c z +module Diff::LCS + VERSION = '1.5.0' +end + +require 'diff/lcs/callbacks' +require 'diff/lcs/internals' + +module Diff::LCS # rubocop:disable Style/Documentation + # Returns an Array containing the longest common subsequence(s) between + # +self+ and +other+. See Diff::LCS#lcs. + # + # lcs = seq1.lcs(seq2) + # + # A note when using objects: Diff::LCS only works properly when each object + # can be used as a key in a Hash, which typically means that the objects must + # implement Object#eql? in a way that two identical values compare + # identically for key purposes. That is: + # + # O.new('a').eql?(O.new('a')) == true + def lcs(other, &block) #:yields self[i] if there are matched subsequences: + Diff::LCS.lcs(self, other, &block) + end + + # Returns the difference set between +self+ and +other+. See Diff::LCS#diff. + def diff(other, callbacks = nil, &block) + Diff::LCS.diff(self, other, callbacks, &block) + end + + # Returns the balanced ("side-by-side") difference set between +self+ and + # +other+. See Diff::LCS#sdiff. + def sdiff(other, callbacks = nil, &block) + Diff::LCS.sdiff(self, other, callbacks, &block) + end + + # Traverses the discovered longest common subsequences between +self+ and + # +other+. See Diff::LCS#traverse_sequences. + def traverse_sequences(other, callbacks = nil, &block) + Diff::LCS.traverse_sequences(self, other, callbacks || Diff::LCS::SequenceCallbacks, &block) + end + + # Traverses the discovered longest common subsequences between +self+ and + # +other+ using the alternate, balanced algorithm. See + # Diff::LCS#traverse_balanced. + def traverse_balanced(other, callbacks = nil, &block) + Diff::LCS.traverse_balanced(self, other, callbacks || Diff::LCS::BalancedCallbacks, &block) + end + + # Attempts to patch +self+ with the provided +patchset+. A new sequence based + # on +self+ and the +patchset+ will be created. See Diff::LCS#patch. Attempts + # to autodiscover the direction of the patch. + def patch(patchset) + Diff::LCS.patch(self, patchset) + end + alias unpatch patch + + # Attempts to patch +self+ with the provided +patchset+. A new sequence based + # on +self+ and the +patchset+ will be created. See Diff::LCS#patch. Does no + # patch direction autodiscovery. + def patch!(patchset) + Diff::LCS.patch!(self, patchset) + end + + # Attempts to unpatch +self+ with the provided +patchset+. A new sequence + # based on +self+ and the +patchset+ will be created. See Diff::LCS#unpatch. + # Does no patch direction autodiscovery. + def unpatch!(patchset) + Diff::LCS.unpatch!(self, patchset) + end + + # Attempts to patch +self+ with the provided +patchset+, using #patch!. If + # the sequence this is used on supports #replace, the value of +self+ will be + # replaced. See Diff::LCS#patch. Does no patch direction autodiscovery. + def patch_me(patchset) + if respond_to? :replace + replace(patch!(patchset)) + else + patch!(patchset) + end + end + + # Attempts to unpatch +self+ with the provided +patchset+, using #unpatch!. + # If the sequence this is used on supports #replace, the value of +self+ will + # be replaced. See Diff::LCS#unpatch. Does no patch direction autodiscovery. + def unpatch_me(patchset) + if respond_to? :replace + replace(unpatch!(patchset)) + else + unpatch!(patchset) + end + end +end + +class << Diff::LCS + def lcs(seq1, seq2, &block) #:yields seq1[i] for each matched: + matches = Diff::LCS::Internals.lcs(seq1, seq2) + ret = [] + string = seq1.kind_of? String + matches.each_with_index do |_e, i| + next if matches[i].nil? + + v = string ? seq1[i, 1] : seq1[i] + v = block[v] if block + ret << v + end + ret + end + alias LCS lcs + + # #diff computes the smallest set of additions and deletions necessary to + # turn the first sequence into the second, and returns a description of these + # changes. + # + # See Diff::LCS::DiffCallbacks for the default behaviour. An alternate + # behaviour may be implemented with Diff::LCS::ContextDiffCallbacks. If a + # Class argument is provided for +callbacks+, #diff will attempt to + # initialise it. If the +callbacks+ object (possibly initialised) responds to + # #finish, it will be called. + def diff(seq1, seq2, callbacks = nil, &block) # :yields diff changes: + diff_traversal(:diff, seq1, seq2, callbacks || Diff::LCS::DiffCallbacks, &block) + end + + # #sdiff computes all necessary components to show two sequences and their + # minimized differences side by side, just like the Unix utility + # sdiff does: + # + # old < - + # same same + # before | after + # - > new + # + # See Diff::LCS::SDiffCallbacks for the default behaviour. An alternate + # behaviour may be implemented with Diff::LCS::ContextDiffCallbacks. If a + # Class argument is provided for +callbacks+, #diff will attempt to + # initialise it. If the +callbacks+ object (possibly initialised) responds to + # #finish, it will be called. + # + # Each element of a returned array is a Diff::LCS::ContextChange object, + # which can be implicitly converted to an array. + # + # Diff::LCS.sdiff(a, b).each do |action, (old_pos, old_element), (new_pos, new_element)| + # case action + # when '!' + # # replace + # when '-' + # # delete + # when '+' + # # insert + # end + # end + def sdiff(seq1, seq2, callbacks = nil, &block) #:yields diff changes: + diff_traversal(:sdiff, seq1, seq2, callbacks || Diff::LCS::SDiffCallbacks, &block) + end + + # #traverse_sequences is the most general facility provided by this module; + # #diff and #lcs are implemented as calls to it. + # + # The arguments to #traverse_sequences are the two sequences to traverse, and + # a callback object, like this: + # + # traverse_sequences(seq1, seq2, Diff::LCS::ContextDiffCallbacks.new) + # + # == Callback Methods + # + # Optional callback methods are emphasized. + # + # callbacks#match:: Called when +a+ and +b+ are pointing to + # common elements in +A+ and +B+. + # callbacks#discard_a:: Called when +a+ is pointing to an + # element not in +B+. + # callbacks#discard_b:: Called when +b+ is pointing to an + # element not in +A+. + # callbacks#finished_a:: Called when +a+ has reached the end of + # sequence +A+. + # callbacks#finished_b:: Called when +b+ has reached the end of + # sequence +B+. + # + # == Algorithm + # + # a---+ + # v + # A = a b c e h j l m n p + # B = b c d e f j k l m r s t + # ^ + # b---+ + # + # If there are two arrows (+a+ and +b+) pointing to elements of sequences +A+ + # and +B+, the arrows will initially point to the first elements of their + # respective sequences. #traverse_sequences will advance the arrows through + # the sequences one element at a time, calling a method on the user-specified + # callback object before each advance. It will advance the arrows in such a + # way that if there are elements A[i] and B[j] which are + # both equal and part of the longest common subsequence, there will be some + # moment during the execution of #traverse_sequences when arrow +a+ is + # pointing to A[i] and arrow +b+ is pointing to B[j]. When + # this happens, #traverse_sequences will call callbacks#match and + # then it will advance both arrows. + # + # Otherwise, one of the arrows is pointing to an element of its sequence that + # is not part of the longest common subsequence. #traverse_sequences will + # advance that arrow and will call callbacks#discard_a or + # callbacks#discard_b, depending on which arrow it advanced. If both + # arrows point to elements that are not part of the longest common + # subsequence, then #traverse_sequences will advance arrow +a+ and call the + # appropriate callback, then it will advance arrow +b+ and call the appropriate + # callback. + # + # The methods for callbacks#match, callbacks#discard_a, and + # callbacks#discard_b are invoked with an event comprising the + # action ("=", "+", or "-", respectively), the indicies +i+ and +j+, and the + # elements A[i] and B[j]. Return values are discarded by + # #traverse_sequences. + # + # === End of Sequences + # + # If arrow +a+ reaches the end of its sequence before arrow +b+ does, + # #traverse_sequence will try to call callbacks#finished_a with the + # last index and element of +A+ (A[-1]) and the current index and + # element of +B+ (B[j]). If callbacks#finished_a does not + # exist, then callbacks#discard_b will be called on each element of + # +B+ until the end of the sequence is reached (the call will be done with + # A[-1] and B[j] for each element). + # + # If +b+ reaches the end of +B+ before +a+ reaches the end of +A+, + # callbacks#finished_b will be called with the current index and + # element of +A+ (A[i]) and the last index and element of +B+ + # (A[-1]). Again, if callbacks#finished_b does not exist on + # the callback object, then callbacks#discard_a will be called on + # each element of +A+ until the end of the sequence is reached (A[i] + # and B[-1]). + # + # There is a chance that one additional callbacks#discard_a or + # callbacks#discard_b will be called after the end of the sequence + # is reached, if +a+ has not yet reached the end of +A+ or +b+ has not yet + # reached the end of +B+. + def traverse_sequences(seq1, seq2, callbacks = Diff::LCS::SequenceCallbacks) #:yields change events: + callbacks ||= Diff::LCS::SequenceCallbacks + matches = Diff::LCS::Internals.lcs(seq1, seq2) + + run_finished_a = run_finished_b = false + string = seq1.kind_of?(String) + + a_size = seq1.size + b_size = seq2.size + ai = bj = 0 + + matches.each do |b_line| + if b_line.nil? + unless seq1[ai].nil? + ax = string ? seq1[ai, 1] : seq1[ai] + bx = string ? seq2[bj, 1] : seq2[bj] + + event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx) + event = yield event if block_given? + callbacks.discard_a(event) + end + else + ax = string ? seq1[ai, 1] : seq1[ai] + + loop do + break unless bj < b_line + + bx = string ? seq2[bj, 1] : seq2[bj] + event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx) + event = yield event if block_given? + callbacks.discard_b(event) + bj += 1 + end + bx = string ? seq2[bj, 1] : seq2[bj] + event = Diff::LCS::ContextChange.new('=', ai, ax, bj, bx) + event = yield event if block_given? + callbacks.match(event) + bj += 1 + end + ai += 1 + end + + # The last entry (if any) processed was a match. +ai+ and +bj+ point just + # past the last matching lines in their sequences. + while (ai < a_size) or (bj < b_size) + # last A? + if ai == a_size and bj < b_size + if callbacks.respond_to?(:finished_a) and !run_finished_a + ax = string ? seq1[-1, 1] : seq1[-1] + bx = string ? seq2[bj, 1] : seq2[bj] + event = Diff::LCS::ContextChange.new('>', (a_size - 1), ax, bj, bx) + event = yield event if block_given? + callbacks.finished_a(event) + run_finished_a = true + else + ax = string ? seq1[ai, 1] : seq1[ai] + loop do + bx = string ? seq2[bj, 1] : seq2[bj] + event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx) + event = yield event if block_given? + callbacks.discard_b(event) + bj += 1 + break unless bj < b_size + end + end + end + + # last B? + if bj == b_size and ai < a_size + if callbacks.respond_to?(:finished_b) and !run_finished_b + ax = string ? seq1[ai, 1] : seq1[ai] + bx = string ? seq2[-1, 1] : seq2[-1] + event = Diff::LCS::ContextChange.new('<', ai, ax, (b_size - 1), bx) + event = yield event if block_given? + callbacks.finished_b(event) + run_finished_b = true + else + bx = string ? seq2[bj, 1] : seq2[bj] + loop do + ax = string ? seq1[ai, 1] : seq1[ai] + event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx) + event = yield event if block_given? + callbacks.discard_a(event) + ai += 1 + break unless bj < b_size + end + end + end + + if ai < a_size + ax = string ? seq1[ai, 1] : seq1[ai] + bx = string ? seq2[bj, 1] : seq2[bj] + event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx) + event = yield event if block_given? + callbacks.discard_a(event) + ai += 1 + end + + if bj < b_size + ax = string ? seq1[ai, 1] : seq1[ai] + bx = string ? seq2[bj, 1] : seq2[bj] + event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx) + event = yield event if block_given? + callbacks.discard_b(event) + bj += 1 + end + end + end + + # #traverse_balanced is an alternative to #traverse_sequences. It uses a + # different algorithm to iterate through the entries in the computed longest + # common subsequence. Instead of viewing the changes as insertions or + # deletions from one of the sequences, #traverse_balanced will report + # changes between the sequences. + # + # The arguments to #traverse_balanced are the two sequences to traverse and a + # callback object, like this: + # + # traverse_balanced(seq1, seq2, Diff::LCS::ContextDiffCallbacks.new) + # + # #sdiff is implemented with #traverse_balanced. + # + # == Callback Methods + # + # Optional callback methods are emphasized. + # + # callbacks#match:: Called when +a+ and +b+ are pointing to + # common elements in +A+ and +B+. + # callbacks#discard_a:: Called when +a+ is pointing to an + # element not in +B+. + # callbacks#discard_b:: Called when +b+ is pointing to an + # element not in +A+. + # callbacks#change:: Called when +a+ and +b+ are pointing to + # the same relative position, but + # A[a] and B[b] are not + # the same; a change has + # occurred. + # + # #traverse_balanced might be a bit slower than #traverse_sequences, + # noticable only while processing huge amounts of data. + # + # == Algorithm + # + # a---+ + # v + # A = a b c e h j l m n p + # B = b c d e f j k l m r s t + # ^ + # b---+ + # + # === Matches + # + # If there are two arrows (+a+ and +b+) pointing to elements of sequences +A+ + # and +B+, the arrows will initially point to the first elements of their + # respective sequences. #traverse_sequences will advance the arrows through + # the sequences one element at a time, calling a method on the user-specified + # callback object before each advance. It will advance the arrows in such a + # way that if there are elements A[i] and B[j] which are + # both equal and part of the longest common subsequence, there will be some + # moment during the execution of #traverse_sequences when arrow +a+ is + # pointing to A[i] and arrow +b+ is pointing to B[j]. When + # this happens, #traverse_sequences will call callbacks#match and + # then it will advance both arrows. + # + # === Discards + # + # Otherwise, one of the arrows is pointing to an element of its sequence that + # is not part of the longest common subsequence. #traverse_sequences will + # advance that arrow and will call callbacks#discard_a or + # callbacks#discard_b, depending on which arrow it advanced. + # + # === Changes + # + # If both +a+ and +b+ point to elements that are not part of the longest + # common subsequence, then #traverse_sequences will try to call + # callbacks#change and advance both arrows. If + # callbacks#change is not implemented, then + # callbacks#discard_a and callbacks#discard_b will be + # called in turn. + # + # The methods for callbacks#match, callbacks#discard_a, + # callbacks#discard_b, and callbacks#change are invoked + # with an event comprising the action ("=", "+", "-", or "!", respectively), + # the indicies +i+ and +j+, and the elements A[i] and B[j]. + # Return values are discarded by #traverse_balanced. + # + # === Context + # + # Note that +i+ and +j+ may not be the same index position, even if +a+ and + # +b+ are considered to be pointing to matching or changed elements. + def traverse_balanced(seq1, seq2, callbacks = Diff::LCS::BalancedCallbacks) + matches = Diff::LCS::Internals.lcs(seq1, seq2) + a_size = seq1.size + b_size = seq2.size + ai = bj = mb = 0 + ma = -1 + string = seq1.kind_of?(String) + + # Process all the lines in the match vector. + loop do + # Find next match indices +ma+ and +mb+ + loop do + ma += 1 + break unless ma < matches.size and matches[ma].nil? + end + + break if ma >= matches.size # end of matches? + + mb = matches[ma] + + # Change(seq2) + while (ai < ma) or (bj < mb) + ax = string ? seq1[ai, 1] : seq1[ai] + bx = string ? seq2[bj, 1] : seq2[bj] + + case [(ai < ma), (bj < mb)] + when [true, true] + if callbacks.respond_to?(:change) + event = Diff::LCS::ContextChange.new('!', ai, ax, bj, bx) + event = yield event if block_given? + callbacks.change(event) + ai += 1 + else + event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx) + event = yield event if block_given? + callbacks.discard_a(event) + ai += 1 + ax = string ? seq1[ai, 1] : seq1[ai] + event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx) + event = yield event if block_given? + callbacks.discard_b(event) + end + + bj += 1 + when [true, false] + event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx) + event = yield event if block_given? + callbacks.discard_a(event) + ai += 1 + when [false, true] + event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx) + event = yield event if block_given? + callbacks.discard_b(event) + bj += 1 + end + end + + # Match + ax = string ? seq1[ai, 1] : seq1[ai] + bx = string ? seq2[bj, 1] : seq2[bj] + event = Diff::LCS::ContextChange.new('=', ai, ax, bj, bx) + event = yield event if block_given? + callbacks.match(event) + ai += 1 + bj += 1 + end + + while (ai < a_size) or (bj < b_size) + ax = string ? seq1[ai, 1] : seq1[ai] + bx = string ? seq2[bj, 1] : seq2[bj] + + case [(ai < a_size), (bj < b_size)] + when [true, true] + if callbacks.respond_to?(:change) + event = Diff::LCS::ContextChange.new('!', ai, ax, bj, bx) + event = yield event if block_given? + callbacks.change(event) + ai += 1 + else + event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx) + event = yield event if block_given? + callbacks.discard_a(event) + ai += 1 + ax = string ? seq1[ai, 1] : seq1[ai] + event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx) + event = yield event if block_given? + callbacks.discard_b(event) + end + + bj += 1 + when [true, false] + event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx) + event = yield event if block_given? + callbacks.discard_a(event) + ai += 1 + when [false, true] + event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx) + event = yield event if block_given? + callbacks.discard_b(event) + bj += 1 + end + end + end + + PATCH_MAP = { #:nodoc: + :patch => { '+' => '+', '-' => '-', '!' => '!', '=' => '=' }.freeze, + :unpatch => { '+' => '-', '-' => '+', '!' => '!', '=' => '=' }.freeze + }.freeze + + # Applies a +patchset+ to the sequence +src+ according to the +direction+ + # (:patch or :unpatch), producing a new sequence. + # + # If the +direction+ is not specified, Diff::LCS::patch will attempt to + # discover the direction of the +patchset+. + # + # A +patchset+ can be considered to apply forward (:patch) if the + # following expression is true: + # + # patch(s1, diff(s1, s2)) -> s2 + # + # A +patchset+ can be considered to apply backward (:unpatch) if the + # following expression is true: + # + # patch(s2, diff(s1, s2)) -> s1 + # + # If the +patchset+ contains no changes, the +src+ value will be returned as + # either src.dup or +src+. A +patchset+ can be deemed as having no + # changes if the following predicate returns true: + # + # patchset.empty? or + # patchset.flatten(1).all? { |change| change.unchanged? } + # + # === Patchsets + # + # A +patchset+ is always an enumerable sequence of changes, hunks of changes, + # or a mix of the two. A hunk of changes is an enumerable sequence of + # changes: + # + # [ # patchset + # # change + # [ # hunk + # # change + # ] + # ] + # + # The +patch+ method accepts patchsets that are enumerable sequences + # containing either Diff::LCS::Change objects (or a subclass) or the array + # representations of those objects. Prior to application, array + # representations of Diff::LCS::Change objects will be reified. + def patch(src, patchset, direction = nil) + # Normalize the patchset. + has_changes, patchset = Diff::LCS::Internals.analyze_patchset(patchset) + + return src.respond_to?(:dup) ? src.dup : src unless has_changes + + string = src.kind_of?(String) + # Start with a new empty type of the source's class + res = src.class.new + + direction ||= Diff::LCS::Internals.intuit_diff_direction(src, patchset) + + ai = bj = 0 + + patch_map = PATCH_MAP[direction] + + patchset.each do |change| + # Both Change and ContextChange support #action + action = patch_map[change.action] + + case change + when Diff::LCS::ContextChange + case direction + when :patch + el = change.new_element + op = change.old_position + np = change.new_position + when :unpatch + el = change.old_element + op = change.new_position + np = change.old_position + end + + case action + when '-' # Remove details from the old string + while ai < op + res << (string ? src[ai, 1] : src[ai]) + ai += 1 + bj += 1 + end + ai += 1 + when '+' + while bj < np + res << (string ? src[ai, 1] : src[ai]) + ai += 1 + bj += 1 + end + + res << el + bj += 1 + when '=' + # This only appears in sdiff output with the SDiff callback. + # Therefore, we only need to worry about dealing with a single + # element. + res << el + + ai += 1 + bj += 1 + when '!' + while ai < op + res << (string ? src[ai, 1] : src[ai]) + ai += 1 + bj += 1 + end + + bj += 1 + ai += 1 + + res << el + end + when Diff::LCS::Change + case action + when '-' + while ai < change.position + res << (string ? src[ai, 1] : src[ai]) + ai += 1 + bj += 1 + end + ai += 1 + when '+' + while bj < change.position + res << (string ? src[ai, 1] : src[ai]) + ai += 1 + bj += 1 + end + + bj += 1 + + res << change.element + end + end + end + + while ai < src.size + res << (string ? src[ai, 1] : src[ai]) + ai += 1 + bj += 1 + end + + res + end + + # Given a set of patchset, convert the current version to the prior version. + # Does no auto-discovery. + def unpatch!(src, patchset) + patch(src, patchset, :unpatch) + end + + # Given a set of patchset, convert the current version to the next version. + # Does no auto-discovery. + def patch!(src, patchset) + patch(src, patchset, :patch) + end +end + +require 'diff/lcs/backports' diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/array.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/array.rb new file mode 100644 index 0000000..5c250f6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/array.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require 'diff/lcs' + +class Array + include Diff::LCS +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/backports.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/backports.rb new file mode 100644 index 0000000..642fc9c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/backports.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +unless 0.respond_to?(:positive?) + class Fixnum # rubocop:disable Lint/UnifiedInteger, Style/Documentation + def positive? + self > 0 # rubocop:disable Style/NumericPredicate + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/block.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/block.rb new file mode 100644 index 0000000..430702d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/block.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +# A block is an operation removing, adding, or changing a group of items. +# Basically, this is just a list of changes, where each change adds or +# deletes a single item. Used by bin/ldiff. +class Diff::LCS::Block + attr_reader :changes, :insert, :remove + + def initialize(chunk) + @changes = [] + @insert = [] + @remove = [] + + chunk.each do |item| + @changes << item + @remove << item if item.deleting? + @insert << item if item.adding? + end + end + + def diff_size + @insert.size - @remove.size + end + + def op + case [@remove.empty?, @insert.empty?] + when [false, false] + '!' + when [false, true] + '-' + when [true, false] + '+' + else # [true, true] + '^' + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/callbacks.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/callbacks.rb new file mode 100644 index 0000000..2a7665b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/callbacks.rb @@ -0,0 +1,325 @@ +# frozen_string_literal: true + +require 'diff/lcs/change' + +module Diff::LCS # rubocop:disable Style/Documentation + # This callback object implements the default set of callback events, + # which only returns the event itself. Note that #finished_a and + # #finished_b are not implemented -- I haven't yet figured out where they + # would be useful. + # + # Note that this is intended to be called as is, e.g., + # + # Diff::LCS.LCS(seq1, seq2, Diff::LCS::DefaultCallbacks) + class DefaultCallbacks + class << self + # Called when two items match. + def match(event) + event + end + + # Called when the old value is discarded in favour of the new value. + def discard_a(event) + event + end + + # Called when the new value is discarded in favour of the old value. + def discard_b(event) + event + end + + # Called when both the old and new values have changed. + def change(event) + event + end + + private :new + end + end + + # An alias for DefaultCallbacks that is used in + # Diff::LCS#traverse_sequences. + # + # Diff::LCS.LCS(seq1, seq2, Diff::LCS::SequenceCallbacks) + SequenceCallbacks = DefaultCallbacks + + # An alias for DefaultCallbacks that is used in + # Diff::LCS#traverse_balanced. + # + # Diff::LCS.LCS(seq1, seq2, Diff::LCS::BalancedCallbacks) + BalancedCallbacks = DefaultCallbacks + + def self.callbacks_for(callbacks) + callbacks.new rescue callbacks + end +end + +# This will produce a compound array of simple diff change objects. Each +# element in the #diffs array is a +hunk+ or +hunk+ array, where each +# element in each +hunk+ array is a single Change object representing the +# addition or removal of a single element from one of the two tested +# sequences. The +hunk+ provides the full context for the changes. +# +# diffs = Diff::LCS.diff(seq1, seq2) +# # This example shows a simplified array format. +# # [ [ [ '-', 0, 'a' ] ], # 1 +# # [ [ '+', 2, 'd' ] ], # 2 +# # [ [ '-', 4, 'h' ], # 3 +# # [ '+', 4, 'f' ] ], +# # [ [ '+', 6, 'k' ] ], # 4 +# # [ [ '-', 8, 'n' ], # 5 +# # [ '-', 9, 'p' ], +# # [ '+', 9, 'r' ], +# # [ '+', 10, 's' ], +# # [ '+', 11, 't' ] ] ] +# +# There are five hunks here. The first hunk says that the +a+ at position 0 +# of the first sequence should be deleted ('-'). The second hunk +# says that the +d+ at position 2 of the second sequence should be inserted +# ('+'). The third hunk says that the +h+ at position 4 of the +# first sequence should be removed and replaced with the +f+ from position 4 +# of the second sequence. The other two hunks are described similarly. +# +# === Use +# +# This callback object must be initialised and is used by the Diff::LCS#diff +# method. +# +# cbo = Diff::LCS::DiffCallbacks.new +# Diff::LCS.LCS(seq1, seq2, cbo) +# cbo.finish +# +# Note that the call to #finish is absolutely necessary, or the last set of +# changes will not be visible. Alternatively, can be used as: +# +# cbo = Diff::LCS::DiffCallbacks.new { |tcbo| Diff::LCS.LCS(seq1, seq2, tcbo) } +# +# The necessary #finish call will be made. +# +# === Simplified Array Format +# +# The simplified array format used in the example above can be obtained +# with: +# +# require 'pp' +# pp diffs.map { |e| e.map { |f| f.to_a } } +class Diff::LCS::DiffCallbacks + # Returns the difference set collected during the diff process. + attr_reader :diffs + + def initialize # :yields self: + @hunk = [] + @diffs = [] + + return unless block_given? + + begin + yield self + ensure + finish + end + end + + # Finalizes the diff process. If an unprocessed hunk still exists, then it + # is appended to the diff list. + def finish + finish_hunk + end + + def match(_event) + finish_hunk + end + + def discard_a(event) + @hunk << Diff::LCS::Change.new('-', event.old_position, event.old_element) + end + + def discard_b(event) + @hunk << Diff::LCS::Change.new('+', event.new_position, event.new_element) + end + + def finish_hunk + @diffs << @hunk unless @hunk.empty? + @hunk = [] + end + private :finish_hunk +end + +# This will produce a compound array of contextual diff change objects. Each +# element in the #diffs array is a "hunk" array, where each element in each +# "hunk" array is a single change. Each change is a Diff::LCS::ContextChange +# that contains both the old index and new index values for the change. The +# "hunk" provides the full context for the changes. Both old and new objects +# will be presented for changed objects. +nil+ will be substituted for a +# discarded object. +# +# seq1 = %w(a b c e h j l m n p) +# seq2 = %w(b c d e f j k l m r s t) +# +# diffs = Diff::LCS.diff(seq1, seq2, Diff::LCS::ContextDiffCallbacks) +# # This example shows a simplified array format. +# # [ [ [ '-', [ 0, 'a' ], [ 0, nil ] ] ], # 1 +# # [ [ '+', [ 3, nil ], [ 2, 'd' ] ] ], # 2 +# # [ [ '-', [ 4, 'h' ], [ 4, nil ] ], # 3 +# # [ '+', [ 5, nil ], [ 4, 'f' ] ] ], +# # [ [ '+', [ 6, nil ], [ 6, 'k' ] ] ], # 4 +# # [ [ '-', [ 8, 'n' ], [ 9, nil ] ], # 5 +# # [ '+', [ 9, nil ], [ 9, 'r' ] ], +# # [ '-', [ 9, 'p' ], [ 10, nil ] ], +# # [ '+', [ 10, nil ], [ 10, 's' ] ], +# # [ '+', [ 10, nil ], [ 11, 't' ] ] ] ] +# +# The five hunks shown are comprised of individual changes; if there is a +# related set of changes, they are still shown individually. +# +# This callback can also be used with Diff::LCS#sdiff, which will produce +# results like: +# +# diffs = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextCallbacks) +# # This example shows a simplified array format. +# # [ [ [ "-", [ 0, "a" ], [ 0, nil ] ] ], # 1 +# # [ [ "+", [ 3, nil ], [ 2, "d" ] ] ], # 2 +# # [ [ "!", [ 4, "h" ], [ 4, "f" ] ] ], # 3 +# # [ [ "+", [ 6, nil ], [ 6, "k" ] ] ], # 4 +# # [ [ "!", [ 8, "n" ], [ 9, "r" ] ], # 5 +# # [ "!", [ 9, "p" ], [ 10, "s" ] ], +# # [ "+", [ 10, nil ], [ 11, "t" ] ] ] ] +# +# The five hunks are still present, but are significantly shorter in total +# presentation, because changed items are shown as changes ("!") instead of +# potentially "mismatched" pairs of additions and deletions. +# +# The result of this operation is similar to that of +# Diff::LCS::SDiffCallbacks. They may be compared as: +# +# s = Diff::LCS.sdiff(seq1, seq2).reject { |e| e.action == "=" } +# c = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextDiffCallbacks).flatten(1) +# +# s == c # -> true +# +# === Use +# +# This callback object must be initialised and can be used by the +# Diff::LCS#diff or Diff::LCS#sdiff methods. +# +# cbo = Diff::LCS::ContextDiffCallbacks.new +# Diff::LCS.LCS(seq1, seq2, cbo) +# cbo.finish +# +# Note that the call to #finish is absolutely necessary, or the last set of +# changes will not be visible. Alternatively, can be used as: +# +# cbo = Diff::LCS::ContextDiffCallbacks.new { |tcbo| Diff::LCS.LCS(seq1, seq2, tcbo) } +# +# The necessary #finish call will be made. +# +# === Simplified Array Format +# +# The simplified array format used in the example above can be obtained +# with: +# +# require 'pp' +# pp diffs.map { |e| e.map { |f| f.to_a } } +class Diff::LCS::ContextDiffCallbacks < Diff::LCS::DiffCallbacks + def discard_a(event) + @hunk << Diff::LCS::ContextChange.simplify(event) + end + + def discard_b(event) + @hunk << Diff::LCS::ContextChange.simplify(event) + end + + def change(event) + @hunk << Diff::LCS::ContextChange.simplify(event) + end +end + +# This will produce a simple array of diff change objects. Each element in +# the #diffs array is a single ContextChange. In the set of #diffs provided +# by SDiffCallbacks, both old and new objects will be presented for both +# changed and unchanged objects. +nil+ will be substituted +# for a discarded object. +# +# The diffset produced by this callback, when provided to Diff::LCS#sdiff, +# will compute and display the necessary components to show two sequences +# and their minimized differences side by side, just like the Unix utility +# +sdiff+. +# +# same same +# before | after +# old < - +# - > new +# +# seq1 = %w(a b c e h j l m n p) +# seq2 = %w(b c d e f j k l m r s t) +# +# diffs = Diff::LCS.sdiff(seq1, seq2) +# # This example shows a simplified array format. +# # [ [ "-", [ 0, "a"], [ 0, nil ] ], +# # [ "=", [ 1, "b"], [ 0, "b" ] ], +# # [ "=", [ 2, "c"], [ 1, "c" ] ], +# # [ "+", [ 3, nil], [ 2, "d" ] ], +# # [ "=", [ 3, "e"], [ 3, "e" ] ], +# # [ "!", [ 4, "h"], [ 4, "f" ] ], +# # [ "=", [ 5, "j"], [ 5, "j" ] ], +# # [ "+", [ 6, nil], [ 6, "k" ] ], +# # [ "=", [ 6, "l"], [ 7, "l" ] ], +# # [ "=", [ 7, "m"], [ 8, "m" ] ], +# # [ "!", [ 8, "n"], [ 9, "r" ] ], +# # [ "!", [ 9, "p"], [ 10, "s" ] ], +# # [ "+", [ 10, nil], [ 11, "t" ] ] ] +# +# The result of this operation is similar to that of +# Diff::LCS::ContextDiffCallbacks. They may be compared as: +# +# s = Diff::LCS.sdiff(seq1, seq2).reject { |e| e.action == "=" } +# c = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextDiffCallbacks).flatten(1) +# +# s == c # -> true +# +# === Use +# +# This callback object must be initialised and is used by the Diff::LCS#sdiff +# method. +# +# cbo = Diff::LCS::SDiffCallbacks.new +# Diff::LCS.LCS(seq1, seq2, cbo) +# +# As with the other initialisable callback objects, +# Diff::LCS::SDiffCallbacks can be initialised with a block. As there is no +# "fininishing" to be done, this has no effect on the state of the object. +# +# cbo = Diff::LCS::SDiffCallbacks.new { |tcbo| Diff::LCS.LCS(seq1, seq2, tcbo) } +# +# === Simplified Array Format +# +# The simplified array format used in the example above can be obtained +# with: +# +# require 'pp' +# pp diffs.map { |e| e.to_a } +class Diff::LCS::SDiffCallbacks + # Returns the difference set collected during the diff process. + attr_reader :diffs + + def initialize #:yields self: + @diffs = [] + yield self if block_given? + end + + def match(event) + @diffs << Diff::LCS::ContextChange.simplify(event) + end + + def discard_a(event) + @diffs << Diff::LCS::ContextChange.simplify(event) + end + + def discard_b(event) + @diffs << Diff::LCS::ContextChange.simplify(event) + end + + def change(event) + @diffs << Diff::LCS::ContextChange.simplify(event) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/change.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/change.rb new file mode 100644 index 0000000..76faf83 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/change.rb @@ -0,0 +1,174 @@ +# frozen_string_literal: true + +# Represents a simplistic (non-contextual) change. Represents the removal or +# addition of an element from either the old or the new sequenced +# enumerable. +class Diff::LCS::Change + IntClass = 1.class # Fixnum is deprecated in Ruby 2.4 # rubocop:disable Naming/ConstantName + + # The only actions valid for changes are '+' (add), '-' (delete), '=' + # (no change), '!' (changed), '<' (tail changes from first sequence), or + # '>' (tail changes from second sequence). The last two ('<>') are only + # found with Diff::LCS::diff and Diff::LCS::sdiff. + VALID_ACTIONS = %w(+ - = ! > <).freeze + + def self.valid_action?(action) + VALID_ACTIONS.include? action + end + + # Returns the action this Change represents. + attr_reader :action + + # Returns the position of the Change. + attr_reader :position + # Returns the sequence element of the Change. + attr_reader :element + + def initialize(*args) + @action, @position, @element = *args + + fail "Invalid Change Action '#{@action}'" unless Diff::LCS::Change.valid_action?(@action) + fail 'Invalid Position Type' unless @position.kind_of? IntClass + end + + def inspect(*_args) + "#<#{self.class}: #{to_a.inspect}>" + end + + def to_a + [@action, @position, @element] + end + + alias to_ary to_a + + def self.from_a(arr) + arr = arr.flatten(1) + case arr.size + when 5 + Diff::LCS::ContextChange.new(*(arr[0...5])) + when 3 + Diff::LCS::Change.new(*(arr[0...3])) + else + fail 'Invalid change array format provided.' + end + end + + include Comparable + + def ==(other) + (self.class == other.class) and + (action == other.action) and + (position == other.position) and + (element == other.element) + end + + def <=>(other) + r = action <=> other.action + r = position <=> other.position if r.zero? + r = element <=> other.element if r.zero? + r + end + + def adding? + @action == '+' + end + + def deleting? + @action == '-' + end + + def unchanged? + @action == '=' + end + + def changed? + @action == '!' + end + + def finished_a? + @action == '>' + end + + def finished_b? + @action == '<' + end +end + +# Represents a contextual change. Contains the position and values of the +# elements in the old and the new sequenced enumerables as well as the action +# taken. +class Diff::LCS::ContextChange < Diff::LCS::Change + # We don't need these two values. + undef :position + undef :element + + # Returns the old position being changed. + attr_reader :old_position + # Returns the new position being changed. + attr_reader :new_position + # Returns the old element being changed. + attr_reader :old_element + # Returns the new element being changed. + attr_reader :new_element + + def initialize(*args) + @action, @old_position, @old_element, @new_position, @new_element = *args + + fail "Invalid Change Action '#{@action}'" unless Diff::LCS::Change.valid_action?(@action) + fail 'Invalid (Old) Position Type' unless @old_position.nil? or @old_position.kind_of? IntClass + fail 'Invalid (New) Position Type' unless @new_position.nil? or @new_position.kind_of? IntClass + end + + def to_a + [ + @action, + [@old_position, @old_element], + [@new_position, @new_element] + ] + end + + alias to_ary to_a + + def self.from_a(arr) + Diff::LCS::Change.from_a(arr) + end + + # Simplifies a context change for use in some diff callbacks. '<' actions + # are converted to '-' and '>' actions are converted to '+'. + def self.simplify(event) + ea = event.to_a + + case ea[0] + when '-' + ea[2][1] = nil + when '<' + ea[0] = '-' + ea[2][1] = nil + when '+' + ea[1][1] = nil + when '>' + ea[0] = '+' + ea[1][1] = nil + end + + Diff::LCS::ContextChange.from_a(ea) + end + + def ==(other) + (self.class == other.class) and + (@action == other.action) and + (@old_position == other.old_position) and + (@new_position == other.new_position) and + (@old_element == other.old_element) and + (@new_element == other.new_element) + end + + def <=>(other) + r = @action <=> other.action + r = @old_position <=> other.old_position if r.zero? + r = @new_position <=> other.new_position if r.zero? + r = @old_element <=> other.old_element if r.zero? + r = @new_element <=> other.new_element if r.zero? + r + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/htmldiff.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/htmldiff.rb new file mode 100644 index 0000000..f12220b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/htmldiff.rb @@ -0,0 +1,150 @@ +# frozen_string_literal: true + +require 'cgi' + +# Produce a simple HTML diff view. +class Diff::LCS::HTMLDiff + class << self + attr_accessor :can_expand_tabs #:nodoc: + end + self.can_expand_tabs = true + + class Callbacks #:nodoc: + attr_accessor :output + attr_accessor :match_class + attr_accessor :only_a_class + attr_accessor :only_b_class + + def initialize(output, options = {}) + @output = output + options ||= {} + + @match_class = options[:match_class] || 'match' + @only_a_class = options[:only_a_class] || 'only_a' + @only_b_class = options[:only_b_class] || 'only_b' + end + + def htmlize(element, css_class) + element = ' ' if element.empty? + %Q(
#{element}
\n) + end + private :htmlize + + # This will be called with both lines are the same + def match(event) + @output << htmlize(event.old_element, :match_class) + end + + # This will be called when there is a line in A that isn't in B + def discard_a(event) + @output << htmlize(event.old_element, :only_a_class) + end + + # This will be called when there is a line in B that isn't in A + def discard_b(event) + @output << htmlize(event.new_element, :only_b_class) + end + end + + DEFAULT_OPTIONS = { + :expand_tabs => nil, + :output => nil, + :css => nil, + :title => nil + }.freeze + + DEFAULT_CSS = <<-CSS +body { margin: 0; } +.diff +{ + border: 1px solid black; + margin: 1em 2em; +} +p +{ + margin-left: 2em; +} +pre +{ + padding-left: 1em; + margin: 0; + font-family: Inconsolata, Consolas, Lucida, Courier, monospaced; + white-space: pre; +} +.match { } +.only_a +{ + background-color: #fdd; + color: red; + text-decoration: line-through; +} +.only_b +{ + background-color: #ddf; + color: blue; + border-left: 3px solid blue +} +h1 { margin-left: 2em; } + CSS + + def initialize(left, right, options = nil) + @left = left + @right = right + @options = options + + @options = DEFAULT_OPTIONS.dup if @options.nil? + end + + def verify_options + @options[:expand_tabs] ||= 4 + @options[:expand_tabs] = 4 if @options[:expand_tabs].negative? + + @options[:output] ||= $stdout + + @options[:css] ||= DEFAULT_CSS.dup + + @options[:title] ||= 'diff' + end + private :verify_options + + attr_reader :options + + def run + verify_options + + if @options[:expand_tabs].positive? && self.class.can_expand_tabs + formatter = Text::Format.new + formatter.tabstop = @options[:expand_tabs] + + @left.map! do |line| formatter.expand(line.chomp) end + @right.map! do |line| formatter.expand(line.chomp) end + end + + @left.map! do |line| CGI.escapeHTML(line.chomp) end + @right.map! do |line| CGI.escapeHTML(line.chomp) end + + @options[:output] << <<-OUTPUT + + + #{@options[:title]} + + + +

#{@options[:title]}

+

Legend: Only in Old  + Only in New

+
+ OUTPUT + + callbacks = Callbacks.new(@options[:output]) + Diff::LCS.traverse_sequences(@left, @right, callbacks) + + @options[:output] << <<-OUTPUT +
+ + + OUTPUT + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/hunk.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/hunk.rb new file mode 100644 index 0000000..49b520e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/hunk.rb @@ -0,0 +1,358 @@ +# frozen_string_literal: true + +require 'diff/lcs/block' + +# A Hunk is a group of Blocks which overlap because of the context surrounding +# each block. (So if we're not using context, every hunk will contain one +# block.) Used in the diff program (bin/ldiff). +class Diff::LCS::Hunk + OLD_DIFF_OP_ACTION = { '+' => 'a', '-' => 'd', '!' => 'c' }.freeze #:nodoc: + ED_DIFF_OP_ACTION = { '+' => 'a', '-' => 'd', '!' => 'c' }.freeze #:nodoc: + + private_constant :OLD_DIFF_OP_ACTION, :ED_DIFF_OP_ACTION if respond_to?(:private_constant) + + # Create a hunk using references to both the old and new data, as well as the + # piece of data. + def initialize(data_old, data_new, piece, flag_context, file_length_difference) + # At first, a hunk will have just one Block in it + @blocks = [Diff::LCS::Block.new(piece)] + + if @blocks[0].remove.empty? && @blocks[0].insert.empty? + fail "Cannot build a hunk from #{piece.inspect}; has no add or remove actions" + end + + if String.method_defined?(:encoding) + @preferred_data_encoding = data_old.fetch(0, data_new.fetch(0, '')).encoding + end + + @data_old = data_old + @data_new = data_new + + before = after = file_length_difference + after += @blocks[0].diff_size + @file_length_difference = after # The caller must get this manually + @max_diff_size = @blocks.map { |e| e.diff_size.abs }.max + + + # Save the start & end of each array. If the array doesn't exist (e.g., + # we're only adding items in this block), then figure out the line number + # based on the line number of the other file and the current difference in + # file lengths. + if @blocks[0].remove.empty? + a1 = a2 = nil + else + a1 = @blocks[0].remove[0].position + a2 = @blocks[0].remove[-1].position + end + + if @blocks[0].insert.empty? + b1 = b2 = nil + else + b1 = @blocks[0].insert[0].position + b2 = @blocks[0].insert[-1].position + end + + @start_old = a1 || (b1 - before) + @start_new = b1 || (a1 + before) + @end_old = a2 || (b2 - after) + @end_new = b2 || (a2 + after) + + self.flag_context = flag_context + end + + attr_reader :blocks + attr_reader :start_old, :start_new + attr_reader :end_old, :end_new + attr_reader :file_length_difference + + # Change the "start" and "end" fields to note that context should be added + # to this hunk. + attr_accessor :flag_context # rubocop:disable Layout/EmptyLinesAroundAttributeAccessor + undef :flag_context= + def flag_context=(context) #:nodoc: # rubocop:disable Lint/DuplicateMethods + return if context.nil? or context.zero? + + add_start = context > @start_old ? @start_old : context + + @start_old -= add_start + @start_new -= add_start + + old_size = @data_old.size + + add_end = + if (@end_old + context) > old_size + old_size - @end_old + else + context + end + + add_end = @max_diff_size if add_end >= old_size + + @end_old += add_end + @end_new += add_end + end + + # Merges this hunk and the provided hunk together if they overlap. Returns + # a truthy value so that if there is no overlap, you can know the merge + # was skipped. + def merge(hunk) + return unless overlaps?(hunk) + + @start_old = hunk.start_old + @start_new = hunk.start_new + blocks.unshift(*hunk.blocks) + end + alias unshift merge + + # Determines whether there is an overlap between this hunk and the + # provided hunk. This will be true if the difference between the two hunks + # start or end positions is within one position of each other. + def overlaps?(hunk) + hunk and (((@start_old - hunk.end_old) <= 1) or + ((@start_new - hunk.end_new) <= 1)) + end + + # Returns a diff string based on a format. + def diff(format, last = false) + case format + when :old + old_diff(last) + when :unified + unified_diff(last) + when :context + context_diff(last) + when :ed + self + when :reverse_ed, :ed_finish + ed_diff(format, last) + else + fail "Unknown diff format #{format}." + end + end + + # Note that an old diff can't have any context. Therefore, we know that + # there's only one block in the hunk. + def old_diff(_last = false) + warn 'Expecting only one block in an old diff hunk!' if @blocks.size > 1 + + block = @blocks[0] + + # Calculate item number range. Old diff range is just like a context + # diff range, except the ranges are on one line with the action between + # them. + s = encode("#{context_range(:old, ',')}#{OLD_DIFF_OP_ACTION[block.op]}#{context_range(:new, ',')}\n") + # If removing anything, just print out all the remove lines in the hunk + # which is just all the remove lines in the block. + unless block.remove.empty? + @data_old[@start_old..@end_old].each { |e| s << encode('< ') + e.chomp + encode("\n") } + end + + s << encode("---\n") if block.op == '!' + + unless block.insert.empty? + @data_new[@start_new..@end_new].each { |e| s << encode('> ') + e.chomp + encode("\n") } + end + + s + end + private :old_diff + + def unified_diff(last = false) + # Calculate item number range. + s = encode("@@ -#{unified_range(:old, last)} +#{unified_range(:new, last)} @@\n") + + # Outlist starts containing the hunk of the old file. Removing an item + # just means putting a '-' in front of it. Inserting an item requires + # getting it from the new file and splicing it in. We splice in + # +num_added+ items. Remove blocks use +num_added+ because splicing + # changed the length of outlist. + # + # We remove +num_removed+ items. Insert blocks use +num_removed+ + # because their item numbers -- corresponding to positions in the NEW + # file -- don't take removed items into account. + lo, hi, num_added, num_removed = @start_old, @end_old, 0, 0 + + outlist = @data_old[lo..hi].map { |e| String.new("#{encode(' ')}#{e.chomp}") } + + last_block = blocks[-1] + + if last + old_missing_newline = missing_last_newline?(@data_old) + new_missing_newline = missing_last_newline?(@data_new) + end + + @blocks.each do |block| + block.remove.each do |item| + op = item.action.to_s # - + offset = item.position - lo + num_added + outlist[offset][0, 1] = encode(op) + num_removed += 1 + end + + if last && block == last_block && old_missing_newline && !new_missing_newline + outlist << encode('\\ No newline at end of file') + num_removed += 1 + end + + block.insert.each do |item| + op = item.action.to_s # + + offset = item.position - @start_new + num_removed + outlist[offset, 0] = encode(op) + @data_new[item.position].chomp + num_added += 1 + end + end + + outlist << encode('\\ No newline at end of file') if last && new_missing_newline + + s << outlist.join(encode("\n")) + + s + end + private :unified_diff + + def context_diff(last = false) + s = encode("***************\n") + s << encode("*** #{context_range(:old, ',', last)} ****\n") + r = context_range(:new, ',', last) + + if last + old_missing_newline = missing_last_newline?(@data_old) + new_missing_newline = missing_last_newline?(@data_new) + end + + # Print out file 1 part for each block in context diff format if there + # are any blocks that remove items + lo, hi = @start_old, @end_old + removes = @blocks.reject { |e| e.remove.empty? } + + unless removes.empty? + outlist = @data_old[lo..hi].map { |e| String.new("#{encode(' ')}#{e.chomp}") } + + last_block = removes[-1] + + removes.each do |block| + block.remove.each do |item| + outlist[item.position - lo][0, 1] = encode(block.op) # - or ! + end + + if last && block == last_block && old_missing_newline + outlist << encode('\\ No newline at end of file') + end + end + + s << outlist.join(encode("\n")) << encode("\n") + end + + s << encode("--- #{r} ----\n") + lo, hi = @start_new, @end_new + inserts = @blocks.reject { |e| e.insert.empty? } + + unless inserts.empty? + outlist = @data_new[lo..hi].map { |e| String.new("#{encode(' ')}#{e.chomp}") } + + last_block = inserts[-1] + + inserts.each do |block| + block.insert.each do |item| + outlist[item.position - lo][0, 1] = encode(block.op) # + or ! + end + + if last && block == last_block && new_missing_newline + outlist << encode('\\ No newline at end of file') + end + end + s << outlist.join(encode("\n")) + end + + s + end + private :context_diff + + def ed_diff(format, _last = false) + warn 'Expecting only one block in an old diff hunk!' if @blocks.size > 1 + + s = + if format == :reverse_ed + encode("#{ED_DIFF_OP_ACTION[@blocks[0].op]}#{context_range(:old, ',')}\n") + else + encode("#{context_range(:old, ' ')}#{ED_DIFF_OP_ACTION[@blocks[0].op]}\n") + end + + unless @blocks[0].insert.empty? + @data_new[@start_new..@end_new].each do |e| + s << e.chomp + encode("\n") + end + s << encode(".\n") + end + s + end + private :ed_diff + + # Generate a range of item numbers to print. Only print 1 number if the + # range has only one item in it. Otherwise, it's 'start,end' + def context_range(mode, op, last = false) + case mode + when :old + s, e = (@start_old + 1), (@end_old + 1) + when :new + s, e = (@start_new + 1), (@end_new + 1) + end + + e -= 1 if last + e = 1 if e.zero? + + s < e ? "#{s}#{op}#{e}" : e.to_s + end + private :context_range + + # Generate a range of item numbers to print for unified diff. Print number + # where block starts, followed by number of lines in the block + # (don't print number of lines if it's 1) + def unified_range(mode, last) + case mode + when :old + s, e = (@start_old + 1), (@end_old + 1) + when :new + s, e = (@start_new + 1), (@end_new + 1) + end + + length = e - s + (last ? 0 : 1) + + first = length < 2 ? e : s # "strange, but correct" + length <= 1 ? first.to_s : "#{first},#{length}" + end + private :unified_range + + def missing_last_newline?(data) + newline = encode("\n") + + if data[-2] + data[-2].end_with?(newline) && !data[-1].end_with?(newline) + elsif data[-1] + !data[-1].end_with?(newline) + else + true + end + end + + if String.method_defined?(:encoding) + def encode(literal, target_encoding = @preferred_data_encoding) + literal.encode target_encoding + end + + def encode_as(string, *args) + args.map { |arg| arg.encode(string.encoding) } + end + else + def encode(literal, _target_encoding = nil) + literal + end + + def encode_as(_string, *args) + args + end + end + + private :encode + private :encode_as +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/internals.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/internals.rb new file mode 100644 index 0000000..ef77667 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/internals.rb @@ -0,0 +1,308 @@ +# frozen_string_literal: true + +class << Diff::LCS + def diff_traversal(method, seq1, seq2, callbacks, &block) + callbacks = callbacks_for(callbacks) + case method + when :diff + traverse_sequences(seq1, seq2, callbacks) + when :sdiff + traverse_balanced(seq1, seq2, callbacks) + end + callbacks.finish if callbacks.respond_to? :finish + + if block + callbacks.diffs.map do |hunk| + if hunk.kind_of? Array + hunk.map { |hunk_block| block[hunk_block] } + else + block[hunk] + end + end + else + callbacks.diffs + end + end + private :diff_traversal +end + +module Diff::LCS::Internals # :nodoc: +end + +class << Diff::LCS::Internals + # Compute the longest common subsequence between the sequenced + # Enumerables +a+ and +b+. The result is an array whose contents is such + # that + # + # result = Diff::LCS::Internals.lcs(a, b) + # result.each_with_index do |e, i| + # assert_equal(a[i], b[e]) unless e.nil? + # end + def lcs(a, b) + a_start = b_start = 0 + a_finish = a.size - 1 + b_finish = b.size - 1 + vector = [] + + # Collect any common elements at the beginning... + while (a_start <= a_finish) and (b_start <= b_finish) and (a[a_start] == b[b_start]) + vector[a_start] = b_start + a_start += 1 + b_start += 1 + end + + # Now the end... + while (a_start <= a_finish) and (b_start <= b_finish) and (a[a_finish] == b[b_finish]) + vector[a_finish] = b_finish + a_finish -= 1 + b_finish -= 1 + end + + # Now, compute the equivalence classes of positions of elements. + # An explanation for how this works: https://codeforces.com/topic/92191 + b_matches = position_hash(b, b_start..b_finish) + + thresh = [] + links = [] + string = a.kind_of?(String) + + (a_start..a_finish).each do |i| + ai = string ? a[i, 1] : a[i] + bm = b_matches[ai] + k = nil + bm.reverse_each do |j| + # Although the threshold check is not mandatory for this to work, + # it may have an optimization purpose + # An attempt to remove it: https://github.com/halostatue/diff-lcs/pull/72 + # Why it is reintroduced: https://github.com/halostatue/diff-lcs/issues/78 + if k and (thresh[k] > j) and (thresh[k - 1] < j) + thresh[k] = j + else + k = replace_next_larger(thresh, j, k) + end + links[k] = [k.positive? ? links[k - 1] : nil, i, j] unless k.nil? + end + end + + unless thresh.empty? + link = links[thresh.size - 1] + until link.nil? + vector[link[1]] = link[2] + link = link[0] + end + end + + vector + end + + # This method will analyze the provided patchset to provide a single-pass + # normalization (conversion of the array form of Diff::LCS::Change objects to + # the object form of same) and detection of whether the patchset represents + # changes to be made. + def analyze_patchset(patchset, depth = 0) + fail 'Patchset too complex' if depth > 1 + + has_changes = false + new_patchset = [] + + # Format: + # [ # patchset + # # hunk (change) + # [ # hunk + # # change + # ] + # ] + + patchset.each do |hunk| + case hunk + when Diff::LCS::Change + has_changes ||= !hunk.unchanged? + new_patchset << hunk + when Array + # Detect if the 'hunk' is actually an array-format change object. + if Diff::LCS::Change.valid_action? hunk[0] + hunk = Diff::LCS::Change.from_a(hunk) + has_changes ||= !hunk.unchanged? + new_patchset << hunk + else + with_changes, hunk = analyze_patchset(hunk, depth + 1) + has_changes ||= with_changes + new_patchset.concat(hunk) + end + else + fail ArgumentError, "Cannot normalise a hunk of class #{hunk.class}." + end + end + + [has_changes, new_patchset] + end + + # Examine the patchset and the source to see in which direction the + # patch should be applied. + # + # WARNING: By default, this examines the whole patch, so this could take + # some time. This also works better with Diff::LCS::ContextChange or + # Diff::LCS::Change as its source, as an array will cause the creation + # of one of the above. + def intuit_diff_direction(src, patchset, limit = nil) + string = src.kind_of?(String) + count = left_match = left_miss = right_match = right_miss = 0 + + patchset.each do |change| + count += 1 + + case change + when Diff::LCS::ContextChange + le = string ? src[change.old_position, 1] : src[change.old_position] + re = string ? src[change.new_position, 1] : src[change.new_position] + + case change.action + when '-' # Remove details from the old string + if le == change.old_element + left_match += 1 + else + left_miss += 1 + end + when '+' + if re == change.new_element + right_match += 1 + else + right_miss += 1 + end + when '=' + left_miss += 1 if le != change.old_element + right_miss += 1 if re != change.new_element + when '!' + if le == change.old_element + left_match += 1 + elsif re == change.new_element + right_match += 1 + else + left_miss += 1 + right_miss += 1 + end + end + when Diff::LCS::Change + # With a simplistic change, we can't tell the difference between + # the left and right on '!' actions, so we ignore those. On '=' + # actions, if there's a miss, we miss both left and right. + element = string ? src[change.position, 1] : src[change.position] + + case change.action + when '-' + if element == change.element + left_match += 1 + else + left_miss += 1 + end + when '+' + if element == change.element + right_match += 1 + else + right_miss += 1 + end + when '=' + if element != change.element + left_miss += 1 + right_miss += 1 + end + end + end + + break if !limit.nil? && (count > limit) + end + + no_left = left_match.zero? && left_miss.positive? + no_right = right_match.zero? && right_miss.positive? + + case [no_left, no_right] + when [false, true] + :patch + when [true, false] + :unpatch + else + case left_match <=> right_match + when 1 + if left_miss.zero? + :patch + else + :unpatch + end + when -1 + if right_miss.zero? + :unpatch + else + :patch + end + else + fail "The provided patchset does not appear to apply to the provided \ +enumerable as either source or destination value." + end + end + end + + # Find the place at which +value+ would normally be inserted into the + # Enumerable. If that place is already occupied by +value+, do nothing + # and return +nil+. If the place does not exist (i.e., it is off the end + # of the Enumerable), add it to the end. Otherwise, replace the element + # at that point with +value+. It is assumed that the Enumerable's values + # are numeric. + # + # This operation preserves the sort order. + def replace_next_larger(enum, value, last_index = nil) + # Off the end? + if enum.empty? or (value > enum[-1]) + enum << value + return enum.size - 1 + end + + # Binary search for the insertion point + last_index ||= enum.size - 1 + first_index = 0 + while first_index <= last_index + i = (first_index + last_index) >> 1 + + found = enum[i] + + return nil if value == found + + if value > found + first_index = i + 1 + else + last_index = i - 1 + end + end + + # The insertion point is in first_index; overwrite the next larger + # value. + enum[first_index] = value + first_index + end + private :replace_next_larger + + # If +vector+ maps the matching elements of another collection onto this + # Enumerable, compute the inverse of +vector+ that maps this Enumerable + # onto the collection. (Currently unused.) + def inverse_vector(a, vector) + inverse = a.dup + (0...vector.size).each do |i| + inverse[vector[i]] = i unless vector[i].nil? + end + inverse + end + private :inverse_vector + + # Returns a hash mapping each element of an Enumerable to the set of + # positions it occupies in the Enumerable, optionally restricted to the + # elements specified in the range of indexes specified by +interval+. + def position_hash(enum, interval) + string = enum.kind_of?(String) + hash = Hash.new { |h, k| h[k] = [] } + interval.each do |i| + k = string ? enum[i, 1] : enum[i] + hash[k] << i + end + hash + end + private :position_hash +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/ldiff.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/ldiff.rb new file mode 100644 index 0000000..17b374c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/ldiff.rb @@ -0,0 +1,171 @@ +# frozen_string_literal: true + +require 'optparse' +require 'ostruct' +require 'diff/lcs/hunk' + +module Diff::LCS::Ldiff #:nodoc: + BANNER = <<-COPYRIGHT +ldiff #{Diff::LCS::VERSION} + Copyright 2004-2019 Austin Ziegler + + Part of Diff::LCS. + https://github.com/halostatue/diff-lcs + + This program is free software. It may be redistributed and/or modified under + the terms of the GPL version 2 (or later), the Perl Artistic licence, or the + MIT licence. + COPYRIGHT +end + +class << Diff::LCS::Ldiff + attr_reader :format, :lines #:nodoc: + attr_reader :file_old, :file_new #:nodoc: + attr_reader :data_old, :data_new #:nodoc: + + def run(args, _input = $stdin, output = $stdout, error = $stderr) #:nodoc: + @binary = nil + + args.options do |o| + o.banner = "Usage: #{File.basename($0)} [options] oldfile newfile" + o.separator '' + o.on( + '-c', '-C', '--context [LINES]', Integer, + 'Displays a context diff with LINES lines', 'of context. Default 3 lines.' + ) do |ctx| + @format = :context + @lines = ctx || 3 + end + o.on( + '-u', '-U', '--unified [LINES]', Integer, + 'Displays a unified diff with LINES lines', 'of context. Default 3 lines.' + ) do |ctx| + @format = :unified + @lines = ctx || 3 + end + o.on('-e', 'Creates an \'ed\' script to change', 'oldfile to newfile.') do |_ctx| + @format = :ed + end + o.on('-f', 'Creates an \'ed\' script to change', 'oldfile to newfile in reverse order.') do |_ctx| + @format = :reverse_ed + end + o.on( + '-a', '--text', + 'Treat the files as text and compare them', 'line-by-line, even if they do not seem', 'to be text.' + ) do |_txt| + @binary = false + end + o.on('--binary', 'Treats the files as binary.') do |_bin| + @binary = true + end + o.on('-q', '--brief', 'Report only whether or not the files', 'differ, not the details.') do |_ctx| + @format = :report + end + o.on_tail('--help', 'Shows this text.') do + error << o + return 0 + end + o.on_tail('--version', 'Shows the version of Diff::LCS.') do + error << Diff::LCS::Ldiff::BANNER + return 0 + end + o.on_tail '' + o.on_tail 'By default, runs produces an "old-style" diff, with output like UNIX diff.' + o.parse! + end + + unless args.size == 2 + error << args.options + return 127 + end + + # Defaults are for old-style diff + @format ||= :old + @lines ||= 0 + + file_old, file_new = *ARGV + + case @format + when :context + char_old = '*' * 3 + char_new = '-' * 3 + when :unified + char_old = '-' * 3 + char_new = '+' * 3 + end + + # After we've read up to a certain point in each file, the number of + # items we've read from each file will differ by FLD (could be 0). + file_length_difference = 0 + + data_old = IO.read(file_old) + data_new = IO.read(file_new) + + # Test binary status + if @binary.nil? + old_txt = data_old[0, 4096].scan(/\0/).empty? + new_txt = data_new[0, 4096].scan(/\0/).empty? + @binary = !old_txt || !new_txt + end + + unless @binary + data_old = data_old.lines.to_a + data_new = data_new.lines.to_a + end + + # diff yields lots of pieces, each of which is basically a Block object + if @binary + diffs = (data_old == data_new) + else + diffs = Diff::LCS.diff(data_old, data_new) + diffs = nil if diffs.empty? + end + + return 0 unless diffs + + if @format == :report + output << "Files #{file_old} and #{file_new} differ\n" + return 1 + end + + if (@format == :unified) or (@format == :context) + ft = File.stat(file_old).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S.000000000 %z') + output << "#{char_old} #{file_old}\t#{ft}\n" + ft = File.stat(file_new).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S.000000000 %z') + output << "#{char_new} #{file_new}\t#{ft}\n" + end + + # Loop over hunks. If a hunk overlaps with the last hunk, join them. + # Otherwise, print out the old one. + oldhunk = hunk = nil + + if @format == :ed + real_output = output + output = [] + end + + diffs.each do |piece| + begin # rubocop:disable Style/RedundantBegin + hunk = Diff::LCS::Hunk.new(data_old, data_new, piece, @lines, file_length_difference) + file_length_difference = hunk.file_length_difference + + next unless oldhunk + next if @lines.positive? and hunk.merge(oldhunk) + + output << oldhunk.diff(@format) + output << "\n" if @format == :unified + ensure + oldhunk = hunk + end + end + + last = oldhunk.diff(@format, true) + last << "\n" if last.respond_to?(:end_with?) && !last.end_with?("\n") + + output << last + + output.reverse_each { |e| real_output << e.diff(:ed_finish) } if @format == :ed + + 1 + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/string.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/string.rb new file mode 100644 index 0000000..9ab32e9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/lib/diff/lcs/string.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class String + include Diff::LCS +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/change_spec.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/change_spec.rb new file mode 100644 index 0000000..b8d3443 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/change_spec.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Diff::LCS::Change do + describe 'an add' do + subject { described_class.new('+', 0, 'element') } + it { should_not be_deleting } + it { should be_adding } + it { should_not be_unchanged } + it { should_not be_changed } + it { should_not be_finished_a } + it { should_not be_finished_b } + end + + describe 'a delete' do + subject { described_class.new('-', 0, 'element') } + it { should be_deleting } + it { should_not be_adding } + it { should_not be_unchanged } + it { should_not be_changed } + it { should_not be_finished_a } + it { should_not be_finished_b } + end + + describe 'an unchanged' do + subject { described_class.new('=', 0, 'element') } + it { should_not be_deleting } + it { should_not be_adding } + it { should be_unchanged } + it { should_not be_changed } + it { should_not be_finished_a } + it { should_not be_finished_b } + end + + describe 'a changed' do + subject { described_class.new('!', 0, 'element') } + it { should_not be_deleting } + it { should_not be_adding } + it { should_not be_unchanged } + it { should be_changed } + it { should_not be_finished_a } + it { should_not be_finished_b } + end + + describe 'a finished_a' do + subject { described_class.new('>', 0, 'element') } + it { should_not be_deleting } + it { should_not be_adding } + it { should_not be_unchanged } + it { should_not be_changed } + it { should be_finished_a } + it { should_not be_finished_b } + end + + describe 'a finished_b' do + subject { described_class.new('<', 0, 'element') } + it { should_not be_deleting } + it { should_not be_adding } + it { should_not be_unchanged } + it { should_not be_changed } + it { should_not be_finished_a } + it { should be_finished_b } + end + + describe 'as array' do + it 'should be converted' do + action, position, element = described_class.new('!', 0, 'element') + expect(action).to eq '!' + expect(position).to eq 0 + expect(element).to eq 'element' + end + end +end + +describe Diff::LCS::ContextChange do + describe 'as array' do + it 'should be converted' do + action, (old_position, old_element), (new_position, new_element) = + described_class.new('!', 1, 'old_element', 2, 'new_element') + + expect(action).to eq '!' + expect(old_position).to eq 1 + expect(old_element).to eq 'old_element' + expect(new_position).to eq 2 + expect(new_element).to eq 'new_element' + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/diff_spec.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/diff_spec.rb new file mode 100644 index 0000000..e7d632a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/diff_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Diff::LCS, '.diff' do + include Diff::LCS::SpecHelper::Matchers + + it 'correctly diffs seq1 to seq2' do + diff_s1_s2 = Diff::LCS.diff(seq1, seq2) + expect(change_diff(correct_forward_diff)).to eq(diff_s1_s2) + end + + it 'correctly diffs seq2 to seq1' do + diff_s2_s1 = Diff::LCS.diff(seq2, seq1) + expect(change_diff(correct_backward_diff)).to eq(diff_s2_s1) + end + + it 'correctly diffs against an empty sequence' do + diff = Diff::LCS.diff(word_sequence, []) + correct_diff = [ + [ + ['-', 0, 'abcd'], + ['-', 1, 'efgh'], + ['-', 2, 'ijkl'], + ['-', 3, 'mnopqrstuvwxyz'] + ] + ] + + expect(change_diff(correct_diff)).to eq(diff) + + diff = Diff::LCS.diff([], word_sequence) + correct_diff.each do |hunk| + hunk.each do |change| change[0] = '+' end + end + expect(change_diff(correct_diff)).to eq(diff) + end + + it "correctly diffs 'xx' and 'xaxb'" do + left = 'xx' + right = 'xaxb' + expect(Diff::LCS.patch(left, Diff::LCS.diff(left, right))).to eq(right) + end + + it 'returns an empty diff with (hello, hello)' do + expect(Diff::LCS.diff(hello, hello)).to be_empty + end + + it 'returns an empty diff with (hello_ary, hello_ary)' do + expect(Diff::LCS.diff(hello_ary, hello_ary)).to be_empty + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/aX b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/aX new file mode 100644 index 0000000..5765d6a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/aX @@ -0,0 +1 @@ +aX diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/bXaX b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/bXaX new file mode 100644 index 0000000..a1c813d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/bXaX @@ -0,0 +1 @@ +bXaX diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ds1.csv b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ds1.csv new file mode 100644 index 0000000..9ac8428 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ds1.csv @@ -0,0 +1,50 @@ +1,3 +2,7 +3,13 +4,21 +5,31 +6,43 +7,57 +8,73 +9,91 +10,111 +11,133 +12,157 +13,183 +14,211 +15,241 +16,273 +17,307 +18,343 +19,381 +20,421 +21,463 +22,507 +23,553 +24,601 +25,651 +26,703 +27,757 +28,813 +29,871 +30,931 +31,993 +32,1057 +33,1123 +34,1191 +35,1261 +36,1333 +37,1407 +38,1483 +39,1561 +40,1641 +41,1723 +42,1807 +43,1893 +44,1981 +45,2071 +46,2163 +47,2257 +48,2353 +49,2451 +50,2500 \ No newline at end of file diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ds2.csv b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ds2.csv new file mode 100644 index 0000000..797de76 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ds2.csv @@ -0,0 +1,51 @@ + 1,3 +2,7 +3,13 +4,21 +5,31 +6,42 +7,57 +8,73 +9,91 +10,111 +11,133 +12,157 +13,183 +14,211 +15,241 +16,273 +17,307 +18,343 +19,200 +20,421 +21,463 +22,507 +23,553 +24,601 +25,651 +26,703 +27,757 +28,813 +29,871 +30,931 +31,123 +32,1057 +33,1123 +34,1000 +35,1261 +36,1333 +37,1407 +38,1483 +39,1561 +40,1641 +41,1723 +42,1807 +43,1893 +44,1981 +45,2071 +46,2163 +47,1524 +48,2353 +49,2451 +50,2500 +51,2520 diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff new file mode 100644 index 0000000..fa1a347 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff @@ -0,0 +1,4 @@ +1c1 +< aX +--- +> bXaX diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff-c b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff-c new file mode 100644 index 0000000..0e1ad99 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff-c @@ -0,0 +1,7 @@ +*** spec/fixtures/aX 2020-06-23 11:15:32.000000000 -0400 +--- spec/fixtures/bXaX 2020-06-23 11:15:32.000000000 -0400 +*************** +*** 1 **** +! aX +--- 1 ---- +! bXaX diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff-e b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff-e new file mode 100644 index 0000000..13e0f7f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff-e @@ -0,0 +1,3 @@ +1c +bXaX +. diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff-f b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff-f new file mode 100644 index 0000000..77710c7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff-f @@ -0,0 +1,3 @@ +c1 +bXaX +. diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff-u b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff-u new file mode 100644 index 0000000..b84f718 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff-u @@ -0,0 +1,5 @@ +--- spec/fixtures/aX 2020-06-23 11:15:32.000000000 -0400 ++++ spec/fixtures/bXaX 2020-06-23 11:15:32.000000000 -0400 +@@ -1 +1 @@ +-aX ++bXaX diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef new file mode 100644 index 0000000..8b98efb --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef @@ -0,0 +1,4 @@ +3c3 +< "description": "hi" +--- +> "description": "lo" diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef-c b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef-c new file mode 100644 index 0000000..efbfa19 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef-c @@ -0,0 +1,15 @@ +*** spec/fixtures/old-chef 2020-06-23 23:18:20.000000000 -0400 +--- spec/fixtures/new-chef 2020-06-23 23:18:20.000000000 -0400 +*************** +*** 1,4 **** + { + "name": "x", +! "description": "hi" + } +\ No newline at end of file +--- 1,4 ---- + { + "name": "x", +! "description": "lo" + } +\ No newline at end of file diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef-e b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef-e new file mode 100644 index 0000000..775d881 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef-e @@ -0,0 +1,3 @@ +3c + "description": "lo" +. diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef-f b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef-f new file mode 100644 index 0000000..9bf1e67 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef-f @@ -0,0 +1,3 @@ +c3 + "description": "lo" +. diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef-u b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef-u new file mode 100644 index 0000000..dbacd88 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef-u @@ -0,0 +1,9 @@ +--- spec/fixtures/old-chef 2020-06-23 23:18:20.000000000 -0400 ++++ spec/fixtures/new-chef 2020-06-23 23:18:20.000000000 -0400 +@@ -1,4 +1,4 @@ + { + "name": "x", +- "description": "hi" ++ "description": "lo" + } +\ No newline at end of file diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef2 b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef2 new file mode 100644 index 0000000..496b3dc --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef2 @@ -0,0 +1,7 @@ +2d1 +< recipe[b::default] +14a14,17 +> recipe[o::new] +> recipe[p::new] +> recipe[q::new] +> recipe[r::new] diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef2-c b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef2-c new file mode 100644 index 0000000..8349a7a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef2-c @@ -0,0 +1,20 @@ +*** spec/fixtures/old-chef2 2020-06-30 09:43:35.000000000 -0400 +--- spec/fixtures/new-chef2 2020-06-30 09:44:32.000000000 -0400 +*************** +*** 1,5 **** + recipe[a::default] +- recipe[b::default] + recipe[c::default] + recipe[d::default] + recipe[e::default] +--- 1,4 ---- +*************** +*** 12,14 **** +--- 11,17 ---- + recipe[l::default] + recipe[m::default] + recipe[n::default] ++ recipe[o::new] ++ recipe[p::new] ++ recipe[q::new] ++ recipe[r::new] diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef2-d b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef2-d new file mode 100644 index 0000000..ca32a49 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef2-d @@ -0,0 +1,7 @@ +d2 +a14 +recipe[o::new] +recipe[p::new] +recipe[q::new] +recipe[r::new] +. diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef2-e b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef2-e new file mode 100644 index 0000000..89f3fa0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef2-e @@ -0,0 +1,7 @@ +14a +recipe[o::new] +recipe[p::new] +recipe[q::new] +recipe[r::new] +. +2d diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef2-f b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef2-f new file mode 100644 index 0000000..ca32a49 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef2-f @@ -0,0 +1,7 @@ +d2 +a14 +recipe[o::new] +recipe[p::new] +recipe[q::new] +recipe[r::new] +. diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef2-u b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef2-u new file mode 100644 index 0000000..ef025c7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/ldiff/output.diff.chef2-u @@ -0,0 +1,16 @@ +--- spec/fixtures/old-chef2 2020-06-30 09:43:35.000000000 -0400 ++++ spec/fixtures/new-chef2 2020-06-30 09:44:32.000000000 -0400 +@@ -1,5 +1,4 @@ + recipe[a::default] +-recipe[b::default] + recipe[c::default] + recipe[d::default] + recipe[e::default] +@@ -12,3 +11,7 @@ + recipe[l::default] + recipe[m::default] + recipe[n::default] ++recipe[o::new] ++recipe[p::new] ++recipe[q::new] ++recipe[r::new] diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/new-chef b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/new-chef new file mode 100644 index 0000000..d7babfe --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/new-chef @@ -0,0 +1,4 @@ +{ + "name": "x", + "description": "lo" +} \ No newline at end of file diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/new-chef2 b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/new-chef2 new file mode 100644 index 0000000..8213c73 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/new-chef2 @@ -0,0 +1,17 @@ +recipe[a::default] +recipe[c::default] +recipe[d::default] +recipe[e::default] +recipe[f::default] +recipe[g::default] +recipe[h::default] +recipe[i::default] +recipe[j::default] +recipe[k::default] +recipe[l::default] +recipe[m::default] +recipe[n::default] +recipe[o::new] +recipe[p::new] +recipe[q::new] +recipe[r::new] diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/old-chef b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/old-chef new file mode 100644 index 0000000..5f9e38b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/old-chef @@ -0,0 +1,4 @@ +{ + "name": "x", + "description": "hi" +} \ No newline at end of file diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/old-chef2 b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/old-chef2 new file mode 100644 index 0000000..4a23407 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/fixtures/old-chef2 @@ -0,0 +1,14 @@ +recipe[a::default] +recipe[b::default] +recipe[c::default] +recipe[d::default] +recipe[e::default] +recipe[f::default] +recipe[g::default] +recipe[h::default] +recipe[i::default] +recipe[j::default] +recipe[k::default] +recipe[l::default] +recipe[m::default] +recipe[n::default] diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/hunk_spec.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/hunk_spec.rb new file mode 100644 index 0000000..b3616bf --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/hunk_spec.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +require 'spec_helper' + +if String.method_defined?(:encoding) + require 'diff/lcs/hunk' + + describe Diff::LCS::Hunk do + let(:old_data) { ['Tu a un carté avec {count} itéms'.encode('UTF-16LE')] } + let(:new_data) { ['Tu a un carte avec {count} items'.encode('UTF-16LE')] } + let(:pieces) { Diff::LCS.diff old_data, new_data } + let(:hunk) { Diff::LCS::Hunk.new(old_data, new_data, pieces[0], 3, 0) } + + it 'produces a unified diff from the two pieces' do + expected = <<-EXPECTED.gsub(/^\s+/, '').encode('UTF-16LE').chomp + @@ -1 +1 @@ + -Tu a un carté avec {count} itéms + +Tu a un carte avec {count} items + EXPECTED + + expect(hunk.diff(:unified)).to eq(expected) + end + + it 'produces a unified diff from the two pieces (last entry)' do + expected = <<-EXPECTED.gsub(/^\s+/, '').encode('UTF-16LE').chomp + @@ -1 +1 @@ + -Tu a un carté avec {count} itéms + +Tu a un carte avec {count} items + \\ No newline at end of file + EXPECTED + + expect(hunk.diff(:unified, true)).to eq(expected) + end + + it 'produces a context diff from the two pieces' do + expected = <<-EXPECTED.gsub(/^\s+/, '').encode('UTF-16LE').chomp + *************** + *** 1 **** + ! Tu a un carté avec {count} itéms + --- 1 ---- + ! Tu a un carte avec {count} items + EXPECTED + + expect(hunk.diff(:context)).to eq(expected) + end + + it 'produces an old diff from the two pieces' do + expected = <<-EXPECTED.gsub(/^ +/, '').encode('UTF-16LE').chomp + 1c1 + < Tu a un carté avec {count} itéms + --- + > Tu a un carte avec {count} items + + EXPECTED + + expect(hunk.diff(:old)).to eq(expected) + end + + it 'produces a reverse ed diff from the two pieces' do + expected = <<-EXPECTED.gsub(/^ +/, '').encode('UTF-16LE').chomp + c1 + Tu a un carte avec {count} items + . + + EXPECTED + + expect(hunk.diff(:reverse_ed)).to eq(expected) + end + + context 'with empty first data set' do + let(:old_data) { [] } + + it 'produces a unified diff' do + expected = <<-EXPECTED.gsub(/^\s+/, '').encode('UTF-16LE').chomp + @@ -1 +1,2 @@ + +Tu a un carte avec {count} items + EXPECTED + + expect(hunk.diff(:unified)).to eq(expected) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/issues_spec.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/issues_spec.rb new file mode 100644 index 0000000..ad73123 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/issues_spec.rb @@ -0,0 +1,154 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'diff/lcs/hunk' + +describe 'Diff::LCS Issues' do + include Diff::LCS::SpecHelper::Matchers + + describe 'issue #1' do + shared_examples 'handles simple diffs' do |s1, s2, forward_diff| + before do + @diff_s1_s2 = Diff::LCS.diff(s1, s2) + end + + it 'creates the correct diff' do + expect(change_diff(forward_diff)).to eq(@diff_s1_s2) + end + + it 'creates the correct patch s1->s2' do + expect(Diff::LCS.patch(s1, @diff_s1_s2)).to eq(s2) + end + + it 'creates the correct patch s2->s1' do + expect(Diff::LCS.patch(s2, @diff_s1_s2)).to eq(s1) + end + end + + describe 'string' do + it_has_behavior 'handles simple diffs', 'aX', 'bXaX', [ + [ + ['+', 0, 'b'], + ['+', 1, 'X'] + ] + ] + it_has_behavior 'handles simple diffs', 'bXaX', 'aX', [ + [ + ['-', 0, 'b'], + ['-', 1, 'X'] + ] + ] + end + + describe 'array' do + it_has_behavior 'handles simple diffs', %w(a X), %w(b X a X), [ + [ + ['+', 0, 'b'], + ['+', 1, 'X'] + ] + ] + it_has_behavior 'handles simple diffs', %w(b X a X), %w(a X), [ + [ + ['-', 0, 'b'], + ['-', 1, 'X'] + ] + ] + end + end + + describe 'issue #57' do + it 'should fail with a correct error' do + expect { + actual = { :category => 'app.rack.request' } + expected = { :category => 'rack.middleware', :title => 'Anonymous Middleware' } + expect(actual).to eq(expected) + }.to raise_error(RSpec::Expectations::ExpectationNotMetError) + end + end + + describe 'issue #60' do + it 'should produce unified output with correct context' do + old_data = <<-DATA_OLD.strip.split("\n").map(&:chomp) +{ + "name": "x", + "description": "hi" +} + DATA_OLD + + new_data = <<-DATA_NEW.strip.split("\n").map(&:chomp) +{ + "name": "x", + "description": "lo" +} + DATA_NEW + + diff = ::Diff::LCS.diff(old_data, new_data) + hunk = ::Diff::LCS::Hunk.new(old_data, new_data, diff.first, 3, 0) + + expect(hunk.diff(:unified)).to eq(<<-EXPECTED.chomp) +@@ -1,5 +1,5 @@ + { + "name": "x", +- "description": "hi" ++ "description": "lo" + } + EXPECTED + end + end + + describe 'issue #65' do + def diff_lines(old_lines, new_lines) + file_length_difference = 0 + previous_hunk = nil + output = [] + + Diff::LCS.diff(old_lines, new_lines).each do |piece| + hunk = Diff::LCS::Hunk.new(old_lines, new_lines, piece, 3, file_length_difference) + file_length_difference = hunk.file_length_difference + maybe_contiguous_hunks = (previous_hunk.nil? || hunk.merge(previous_hunk)) + + output << "#{previous_hunk.diff(:unified)}\n" unless maybe_contiguous_hunks + + previous_hunk = hunk + end + output << "#{previous_hunk.diff(:unified, true)}\n" unless previous_hunk.nil? + output.join + end + + it 'should not misplace the new chunk' do + old_data = [ + 'recipe[a::default]', 'recipe[b::default]', 'recipe[c::default]', + 'recipe[d::default]', 'recipe[e::default]', 'recipe[f::default]', + 'recipe[g::default]', 'recipe[h::default]', 'recipe[i::default]', + 'recipe[j::default]', 'recipe[k::default]', 'recipe[l::default]', + 'recipe[m::default]', 'recipe[n::default]' + ] + + new_data = [ + 'recipe[a::default]', 'recipe[c::default]', 'recipe[d::default]', + 'recipe[e::default]', 'recipe[f::default]', 'recipe[g::default]', + 'recipe[h::default]', 'recipe[i::default]', 'recipe[j::default]', + 'recipe[k::default]', 'recipe[l::default]', 'recipe[m::default]', + 'recipe[n::default]', 'recipe[o::new]', 'recipe[p::new]', + 'recipe[q::new]', 'recipe[r::new]' + ] + + expect(diff_lines(old_data, new_data)).to eq(<<-EODIFF) +@@ -1,5 +1,4 @@ + recipe[a::default] +-recipe[b::default] + recipe[c::default] + recipe[d::default] + recipe[e::default] +@@ -12,3 +11,7 @@ + recipe[l::default] + recipe[m::default] + recipe[n::default] ++recipe[o::new] ++recipe[p::new] ++recipe[q::new] ++recipe[r::new] + EODIFF + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/lcs_spec.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/lcs_spec.rb new file mode 100644 index 0000000..94428fd --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/lcs_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Diff::LCS::Internals, '.lcs' do + include Diff::LCS::SpecHelper::Matchers + + it 'returns a meaningful LCS array with (seq1, seq2)' do + res = Diff::LCS::Internals.lcs(seq1, seq2) + # The result of the LCS (less the +nil+ values) must be as long as the + # correct result. + expect(res.compact.size).to eq(correct_lcs.size) + expect(res).to correctly_map_sequence(seq1).to_other_sequence(seq2) + + # Compact these transformations and they should be the correct LCS. + x_seq1 = (0...res.size).map { |ix| res[ix] ? seq1[ix] : nil }.compact + x_seq2 = (0...res.size).map { |ix| res[ix] ? seq2[res[ix]] : nil }.compact + + expect(x_seq1).to eq(correct_lcs) + expect(x_seq2).to eq(correct_lcs) + end + + it 'returns all indexes with (hello, hello)' do + expect(Diff::LCS::Internals.lcs(hello, hello)).to \ + eq((0...hello.size).to_a) + end + + it 'returns all indexes with (hello_ary, hello_ary)' do + expect(Diff::LCS::Internals.lcs(hello_ary, hello_ary)).to \ + eq((0...hello_ary.size).to_a) + end +end + +describe Diff::LCS, '.LCS' do + include Diff::LCS::SpecHelper::Matchers + + it 'returns the correct compacted values from Diff::LCS.LCS' do + res = Diff::LCS.LCS(seq1, seq2) + expect(res).to eq(correct_lcs) + expect(res.compact).to eq(res) + end + + it 'is transitive' do + res = Diff::LCS.LCS(seq2, seq1) + expect(res).to eq(correct_lcs) + expect(res.compact).to eq(res) + end + + it 'returns %W(h e l l o) with (hello, hello)' do + expect(Diff::LCS.LCS(hello, hello)).to eq(hello.split(//)) + end + + it 'returns hello_ary with (hello_ary, hello_ary)' do + expect(Diff::LCS.LCS(hello_ary, hello_ary)).to eq(hello_ary) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/ldiff_spec.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/ldiff_spec.rb new file mode 100644 index 0000000..a2468f8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/ldiff_spec.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'bin/ldiff' do + include CaptureSubprocessIO + + fixtures = [ + { :name => 'output.diff', :left => 'aX', :right => 'bXaX' }, + { :name => 'output.diff.chef', :left => 'old-chef', :right => 'new-chef' }, + { :name => 'output.diff.chef2', :left => 'old-chef2', :right => 'new-chef2' } + ].product([nil, '-e', '-f', '-c', '-u']).map { |(fixture, flag)| + fixture = fixture.dup + fixture[:flag] = flag + fixture + } + + def self.test_ldiff(fixture) + desc = [ + fixture[:flag], + "spec/fixtures/#{fixture[:left]}", + "spec/fixtures/#{fixture[:right]}", + '#', + '=>', + "spec/fixtures/ldiff/#{fixture[:name]}#{fixture[:flag]}" + ].join(' ') + + it desc do + expect(run_ldiff(fixture)).to eq(read_fixture(fixture)) + end + end + + fixtures.each do |fixture| + test_ldiff(fixture) + end + + def read_fixture(options) + fixture = options.fetch(:name) + flag = options.fetch(:flag) + name = "spec/fixtures/ldiff/#{fixture}#{flag}" + data = IO.__send__(IO.respond_to?(:binread) ? :binread : :read, name) + clean_data(data, flag) + end + + def clean_data(data, flag) + data = + case flag + when '-c', '-u' + clean_output_timestamp(data) + else + data + end + data.gsub(/\r\n?/, "\n") + end + + def clean_output_timestamp(data) + data.gsub( + %r{ + ^ + [-+*]{3} + \s* + spec/fixtures/(\S+) + \s* + \d{4}-\d\d-\d\d + \s* + \d\d:\d\d:\d\d(?:\.\d+) + \s* + (?:[-+]\d{4}|Z) + }x, + '*** spec/fixtures/\1 0000-00-00 :00 =>:00 =>00.000000000 -0000' + ) + end + + def run_ldiff(options) + flag = options.fetch(:flag) + left = options.fetch(:left) + right = options.fetch(:right) + + stdout, stderr = capture_subprocess_io do + system("ruby -Ilib bin/ldiff #{flag} spec/fixtures/#{left} spec/fixtures/#{right}") + end + + expect(stderr).to be_empty if RUBY_VERSION >= '1.9' + expect(stdout).not_to be_empty + clean_data(stdout, flag) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/patch_spec.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/patch_spec.rb new file mode 100644 index 0000000..11b0981 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/patch_spec.rb @@ -0,0 +1,416 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Diff::LCS.patch' do + include Diff::LCS::SpecHelper::Matchers + + shared_examples 'patch sequences correctly' do + it 'correctly patches left-to-right (patch autodiscovery)' do + expect(Diff::LCS.patch(s1, patch_set)).to eq(s2) + end + + it 'correctly patches left-to-right (explicit patch)' do + expect(Diff::LCS.patch(s1, patch_set, :patch)).to eq(s2) + expect(Diff::LCS.patch!(s1, patch_set)).to eq(s2) + end + + it 'correctly patches right-to-left (unpatch autodiscovery)' do + expect(Diff::LCS.patch(s2, patch_set)).to eq(s1) + end + + it 'correctly patches right-to-left (explicit unpatch)' do + expect(Diff::LCS.patch(s2, patch_set, :unpatch)).to eq(s1) + expect(Diff::LCS.unpatch!(s2, patch_set)).to eq(s1) + end + end + + describe 'using a Diff::LCS.diff patchset' do + describe 'an empty patchset returns the source' do + it 'works on a string (hello)' do + diff = Diff::LCS.diff(hello, hello) + expect(Diff::LCS.patch(hello, diff)).to eq(hello) + end + + it 'works on an array %W(h e l l o)' do + diff = Diff::LCS.diff(hello_ary, hello_ary) + expect(Diff::LCS.patch(hello_ary, diff)).to eq(hello_ary) + end + end + + describe 'with default diff callbacks (DiffCallbacks)' do + describe 'forward (s1 -> s2)' do + it_has_behavior 'patch sequences correctly' do + let(:s1) { seq1 } + let(:s2) { seq2 } + let(:patch_set) { Diff::LCS.diff(seq1, seq2) } + end + end + + describe 'reverse (s2 -> s1)' do + it_has_behavior 'patch sequences correctly' do + let(:s1) { seq2 } + let(:s2) { seq1 } + let(:patch_set) { Diff::LCS.diff(seq2, seq1) } + end + end + end + + describe 'with context diff callbacks (ContextDiffCallbacks)' do + describe 'forward (s1 -> s2)' do + it_has_behavior 'patch sequences correctly' do + let(:s1) { seq1 } + let(:s2) { seq2 } + let(:patch_set) { + Diff::LCS.diff(seq1, seq2, Diff::LCS::ContextDiffCallbacks) + } + end + end + + describe 'reverse (s2 -> s1)' do + it_has_behavior 'patch sequences correctly' do + let(:s1) { seq2 } + let(:s2) { seq1 } + let(:patch_set) { + Diff::LCS.diff(seq2, seq1, Diff::LCS::ContextDiffCallbacks) + } + end + end + end + + describe 'with sdiff callbacks (SDiffCallbacks)' do + describe 'forward (s1 -> s2)' do + it_has_behavior 'patch sequences correctly' do + let(:s1) { seq1 } + let(:s2) { seq2 } + let(:patch_set) { + Diff::LCS.diff(seq1, seq2, Diff::LCS::SDiffCallbacks) + } + end + end + + describe 'reverse (s2 -> s1)' do + it_has_behavior 'patch sequences correctly' do + let(:s1) { seq2 } + let(:s2) { seq1 } + let(:patch_set) { + Diff::LCS.diff(seq2, seq1, Diff::LCS::SDiffCallbacks) + } + end + end + end + end + + describe 'using a Diff::LCS.sdiff patchset' do + describe 'an empty patchset returns the source' do + it 'works on a string (hello)' do + expect(Diff::LCS.patch(hello, Diff::LCS.sdiff(hello, hello))).to eq(hello) + end + + it 'works on an array %W(h e l l o)' do + expect(Diff::LCS.patch(hello_ary, Diff::LCS.sdiff(hello_ary, hello_ary))).to eq(hello_ary) + end + end + + describe 'with default diff callbacks (DiffCallbacks)' do + describe 'forward (s1 -> s2)' do + it_has_behavior 'patch sequences correctly' do + let(:s1) { seq1 } + let(:s2) { seq2 } + let(:patch_set) { + Diff::LCS.sdiff(seq1, seq2, Diff::LCS::DiffCallbacks) + } + end + end + + describe 'reverse (s2 -> s1)' do + it_has_behavior 'patch sequences correctly' do + let(:s1) { seq2 } + let(:s2) { seq1 } + let(:patch_set) { + Diff::LCS.sdiff(seq2, seq1, Diff::LCS::DiffCallbacks) + } + end + end + end + + describe 'with context diff callbacks (DiffCallbacks)' do + describe 'forward (s1 -> s2)' do + it_has_behavior 'patch sequences correctly' do + let(:s1) { seq1 } + let(:s2) { seq2 } + let(:patch_set) { + Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextDiffCallbacks) + } + end + end + + describe 'reverse (s2 -> s1)' do + it_has_behavior 'patch sequences correctly' do + let(:s1) { seq2 } + let(:s2) { seq1 } + let(:patch_set) { + Diff::LCS.sdiff(seq2, seq1, Diff::LCS::ContextDiffCallbacks) + } + end + end + end + + describe 'with sdiff callbacks (SDiffCallbacks)' do + describe 'forward (s1 -> s2)' do + it_has_behavior 'patch sequences correctly' do + let(:s1) { seq1 } + let(:s2) { seq2 } + let(:patch_set) { Diff::LCS.sdiff(seq1, seq2) } + end + end + + describe 'reverse (s2 -> s1)' do + it_has_behavior 'patch sequences correctly' do + let(:s1) { seq2 } + let(:s2) { seq1 } + let(:patch_set) { Diff::LCS.sdiff(seq2, seq1) } + end + end + end + end + + # Note: because of the error in autodiscovery ("does not autodiscover s1 + # to s2 patches"), this cannot use the "patch sequences correctly" shared + # set. Once the bug in autodiscovery is fixed, this can be converted as + # above. + describe 'fix bug 891: patchsets do not contain the last equal part' do + before :each do + @s1 = %w(a b c d e f g h i j k) # rubocop:disable Layout/SpaceInsideArrayPercentLiteral + @s2 = %w(a b c d D e f g h i j k) + end + + describe 'using Diff::LCS.diff with default diff callbacks' do + before :each do + @patch_set_s1_s2 = Diff::LCS.diff(@s1, @s2) + @patch_set_s2_s1 = Diff::LCS.diff(@s2, @s1) + end + + it 'autodiscovers s1 to s2 patches' do + expect do + expect(Diff::LCS.patch(@s1, @patch_set_s1_s2)).to eq(@s2) + end.to_not raise_error + end + + it 'autodiscovers s2 to s1 patches' do + expect do + expect(Diff::LCS.patch(@s1, @patch_set_s2_s1)).to eq(@s2) + end.to_not raise_error + end + + it 'autodiscovers s2 to s1 the left-to-right patches' do + expect(Diff::LCS.patch(@s2, @patch_set_s2_s1)).to eq(@s1) + expect(Diff::LCS.patch(@s2, @patch_set_s1_s2)).to eq(@s1) + end + + it 'correctly patches left-to-right (explicit patch)' do + expect(Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch)).to eq(@s2) + expect(Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch)).to eq(@s1) + expect(Diff::LCS.patch!(@s1, @patch_set_s1_s2)).to eq(@s2) + expect(Diff::LCS.patch!(@s2, @patch_set_s2_s1)).to eq(@s1) + end + + it 'correctly patches right-to-left (explicit unpatch)' do + expect(Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch)).to eq(@s1) + expect(Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch)).to eq(@s2) + expect(Diff::LCS.unpatch!(@s2, @patch_set_s1_s2)).to eq(@s1) + expect(Diff::LCS.unpatch!(@s1, @patch_set_s2_s1)).to eq(@s2) + end + end + + describe 'using Diff::LCS.diff with context diff callbacks' do + before :each do + @patch_set_s1_s2 = Diff::LCS.diff(@s1, @s2, Diff::LCS::ContextDiffCallbacks) + @patch_set_s2_s1 = Diff::LCS.diff(@s2, @s1, Diff::LCS::ContextDiffCallbacks) + end + + it 'autodiscovers s1 to s2 patches' do + expect do + expect(Diff::LCS.patch(@s1, @patch_set_s1_s2)).to eq(@s2) + end.to_not raise_error + end + + it 'autodiscovers s2 to s1 patches' do + expect do + expect(Diff::LCS.patch(@s1, @patch_set_s2_s1)).to eq(@s2) + end.to_not raise_error + end + + it 'autodiscovers s2 to s1 the left-to-right patches' do + expect(Diff::LCS.patch(@s2, @patch_set_s2_s1)).to eq(@s1) + expect(Diff::LCS.patch(@s2, @patch_set_s1_s2)).to eq(@s1) + end + + it 'correctly patches left-to-right (explicit patch)' do + expect(Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch)).to eq(@s2) + expect(Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch)).to eq(@s1) + expect(Diff::LCS.patch!(@s1, @patch_set_s1_s2)).to eq(@s2) + expect(Diff::LCS.patch!(@s2, @patch_set_s2_s1)).to eq(@s1) + end + + it 'correctly patches right-to-left (explicit unpatch)' do + expect(Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch)).to eq(@s1) + expect(Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch)).to eq(@s2) + expect(Diff::LCS.unpatch!(@s2, @patch_set_s1_s2)).to eq(@s1) + expect(Diff::LCS.unpatch!(@s1, @patch_set_s2_s1)).to eq(@s2) + end + end + + describe 'using Diff::LCS.diff with sdiff callbacks' do + before(:each) do + @patch_set_s1_s2 = Diff::LCS.diff(@s1, @s2, Diff::LCS::SDiffCallbacks) + @patch_set_s2_s1 = Diff::LCS.diff(@s2, @s1, Diff::LCS::SDiffCallbacks) + end + + it 'autodiscovers s1 to s2 patches' do + expect do + expect(Diff::LCS.patch(@s1, @patch_set_s1_s2)).to eq(@s2) + end.to_not raise_error + end + + it 'autodiscovers s2 to s1 patches' do + expect do + expect(Diff::LCS.patch(@s1, @patch_set_s2_s1)).to eq(@s2) + end.to_not raise_error + end + + it 'autodiscovers s2 to s1 the left-to-right patches' do + expect(Diff::LCS.patch(@s2, @patch_set_s2_s1)).to eq(@s1) + expect(Diff::LCS.patch(@s2, @patch_set_s1_s2)).to eq(@s1) + end + + it 'correctly patches left-to-right (explicit patch)' do + expect(Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch)).to eq(@s2) + expect(Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch)).to eq(@s1) + expect(Diff::LCS.patch!(@s1, @patch_set_s1_s2)).to eq(@s2) + expect(Diff::LCS.patch!(@s2, @patch_set_s2_s1)).to eq(@s1) + end + + it 'correctly patches right-to-left (explicit unpatch)' do + expect(Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch)).to eq(@s1) + expect(Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch)).to eq(@s2) + expect(Diff::LCS.unpatch!(@s2, @patch_set_s1_s2)).to eq(@s1) + expect(Diff::LCS.unpatch!(@s1, @patch_set_s2_s1)).to eq(@s2) + end + end + + describe 'using Diff::LCS.sdiff with default sdiff callbacks' do + before(:each) do + @patch_set_s1_s2 = Diff::LCS.sdiff(@s1, @s2) + @patch_set_s2_s1 = Diff::LCS.sdiff(@s2, @s1) + end + + it 'autodiscovers s1 to s2 patches' do + expect do + expect(Diff::LCS.patch(@s1, @patch_set_s1_s2)).to eq(@s2) + end.to_not raise_error + end + + it 'autodiscovers s2 to s1 patches' do + expect do + expect(Diff::LCS.patch(@s1, @patch_set_s2_s1)).to eq(@s2) + end.to_not raise_error + end + + it 'autodiscovers s2 to s1 the left-to-right patches' do + expect(Diff::LCS.patch(@s2, @patch_set_s2_s1)).to eq(@s1) + expect(Diff::LCS.patch(@s2, @patch_set_s1_s2)).to eq(@s1) + end + + it 'correctly patches left-to-right (explicit patch)' do + expect(Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch)).to eq(@s2) + expect(Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch)).to eq(@s1) + expect(Diff::LCS.patch!(@s1, @patch_set_s1_s2)).to eq(@s2) + expect(Diff::LCS.patch!(@s2, @patch_set_s2_s1)).to eq(@s1) + end + + it 'correctly patches right-to-left (explicit unpatch)' do + expect(Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch)).to eq(@s1) + expect(Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch)).to eq(@s2) + expect(Diff::LCS.unpatch!(@s2, @patch_set_s1_s2)).to eq(@s1) + expect(Diff::LCS.unpatch!(@s1, @patch_set_s2_s1)).to eq(@s2) + end + end + + describe 'using Diff::LCS.sdiff with context diff callbacks' do + before(:each) do + @patch_set_s1_s2 = Diff::LCS.sdiff(@s1, @s2, Diff::LCS::ContextDiffCallbacks) + @patch_set_s2_s1 = Diff::LCS.sdiff(@s2, @s1, Diff::LCS::ContextDiffCallbacks) + end + + it 'autodiscovers s1 to s2 patches' do + expect do + expect(Diff::LCS.patch(@s1, @patch_set_s1_s2)).to eq(@s2) + end.to_not raise_error + end + + it 'autodiscovers s2 to s1 patches' do + expect do + expect(Diff::LCS.patch(@s1, @patch_set_s2_s1)).to eq(@s2) + end.to_not raise_error + end + + it 'autodiscovers s2 to s1 the left-to-right patches' do + expect(Diff::LCS.patch(@s2, @patch_set_s2_s1)).to eq(@s1) + expect(Diff::LCS.patch(@s2, @patch_set_s1_s2)).to eq(@s1) + end + + it 'correctly patches left-to-right (explicit patch)' do + expect(Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch)).to eq(@s2) + expect(Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch)).to eq(@s1) + expect(Diff::LCS.patch!(@s1, @patch_set_s1_s2)).to eq(@s2) + expect(Diff::LCS.patch!(@s2, @patch_set_s2_s1)).to eq(@s1) + end + + it 'correctly patches right-to-left (explicit unpatch)' do + expect(Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch)).to eq(@s1) + expect(Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch)).to eq(@s2) + expect(Diff::LCS.unpatch!(@s2, @patch_set_s1_s2)).to eq(@s1) + expect(Diff::LCS.unpatch!(@s1, @patch_set_s2_s1)).to eq(@s2) + end + end + + describe 'using Diff::LCS.sdiff with default diff callbacks' do + before(:each) do + @patch_set_s1_s2 = Diff::LCS.sdiff(@s1, @s2, Diff::LCS::DiffCallbacks) + @patch_set_s2_s1 = Diff::LCS.sdiff(@s2, @s1, Diff::LCS::DiffCallbacks) + end + + it 'autodiscovers s1 to s2 patches' do + expect do + expect(Diff::LCS.patch(@s1, @patch_set_s1_s2)).to eq(@s2) + end.to_not raise_error + end + + it 'autodiscovers s2 to s1 patches' do + expect do + expect(Diff::LCS.patch(@s1, @patch_set_s2_s1)).to eq(@s2) + end.to_not raise_error + end + + it 'autodiscovers s2 to s1 the left-to-right patches' do + expect(Diff::LCS.patch(@s2, @patch_set_s2_s1)).to eq(@s1) + expect(Diff::LCS.patch(@s2, @patch_set_s1_s2)).to eq(@s1) + end + + it 'correctly patches left-to-right (explicit patch)' do + expect(Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch)).to eq(@s2) + expect(Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch)).to eq(@s1) + expect(Diff::LCS.patch!(@s1, @patch_set_s1_s2)).to eq(@s2) + expect(Diff::LCS.patch!(@s2, @patch_set_s2_s1)).to eq(@s1) + end + + it 'correctly patches right-to-left (explicit unpatch)' do + expect(Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch)).to eq(@s1) + expect(Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch)).to eq(@s2) + expect(Diff::LCS.unpatch!(@s2, @patch_set_s1_s2)).to eq(@s1) + expect(Diff::LCS.unpatch!(@s1, @patch_set_s2_s1)).to eq(@s2) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/sdiff_spec.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/sdiff_spec.rb new file mode 100644 index 0000000..06d39d6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/sdiff_spec.rb @@ -0,0 +1,214 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Diff::LCS.sdiff' do + include Diff::LCS::SpecHelper::Matchers + + shared_examples 'compare sequences correctly' do + it 'compares s1 -> s2 correctly' do + expect(Diff::LCS.sdiff(s1, s2)).to eq(context_diff(result)) + end + + it 'compares s2 -> s1 correctly' do + expect(Diff::LCS.sdiff(s2, s1)).to eq(context_diff(reverse_sdiff(result))) + end + end + + describe 'using seq1 & seq2' do + let(:s1) { seq1 } + let(:s2) { seq2 } + let(:result) { correct_forward_sdiff } + + it_has_behavior 'compare sequences correctly' + end + + describe 'using %w(abc def yyy xxx ghi jkl) & %w(abc dxf xxx ghi jkl)' do + let(:s1) { %w(abc def yyy xxx ghi jkl) } + let(:s2) { %w(abc dxf xxx ghi jkl) } + let(:result) { + [ + ['=', [0, 'abc'], [0, 'abc']], + ['!', [1, 'def'], [1, 'dxf']], + ['-', [2, 'yyy'], [2, nil]], + ['=', [3, 'xxx'], [2, 'xxx']], + ['=', [4, 'ghi'], [3, 'ghi']], + ['=', [5, 'jkl'], [4, 'jkl']] + ] + } + + it_has_behavior 'compare sequences correctly' + end + + describe 'using %w(a b c d e) & %w(a e)' do + let(:s1) { %w(a b c d e) } + let(:s2) { %w(a e) } + let(:result) { + [ + ['=', [0, 'a'], [0, 'a']], + ['-', [1, 'b'], [1, nil]], + ['-', [2, 'c'], [1, nil]], + ['-', [3, 'd'], [1, nil]], + ['=', [4, 'e'], [1, 'e']] + ] + } + + it_has_behavior 'compare sequences correctly' + end + + describe 'using %w(a e) & %w(a b c d e)' do + let(:s1) { %w(a e) } + let(:s2) { %w(a b c d e) } + let(:result) { + [ + ['=', [0, 'a'], [0, 'a']], + ['+', [1, nil], [1, 'b']], + ['+', [1, nil], [2, 'c']], + ['+', [1, nil], [3, 'd']], + ['=', [1, 'e'], [4, 'e']] + ] + } + + it_has_behavior 'compare sequences correctly' + end + + describe 'using %w(v x a e) & %w(w y a b c d e)' do + let(:s1) { %w(v x a e) } + let(:s2) { %w(w y a b c d e) } + let(:result) { + [ + ['!', [0, 'v'], [0, 'w']], + ['!', [1, 'x'], [1, 'y']], + ['=', [2, 'a'], [2, 'a']], + ['+', [3, nil], [3, 'b']], + ['+', [3, nil], [4, 'c']], + ['+', [3, nil], [5, 'd']], + ['=', [3, 'e'], [6, 'e']] + ] + } + + it_has_behavior 'compare sequences correctly' + end + + describe 'using %w(x a e) & %w(a b c d e)' do + let(:s1) { %w(x a e) } + let(:s2) { %w(a b c d e) } + let(:result) { + [ + ['-', [0, 'x'], [0, nil]], + ['=', [1, 'a'], [0, 'a']], + ['+', [2, nil], [1, 'b']], + ['+', [2, nil], [2, 'c']], + ['+', [2, nil], [3, 'd']], + ['=', [2, 'e'], [4, 'e']] + ] + } + + it_has_behavior 'compare sequences correctly' + end + + describe 'using %w(a e) & %w(x a b c d e)' do + let(:s1) { %w(a e) } + let(:s2) { %w(x a b c d e) } + let(:result) { + [ + ['+', [0, nil], [0, 'x']], + ['=', [0, 'a'], [1, 'a']], + ['+', [1, nil], [2, 'b']], + ['+', [1, nil], [3, 'c']], + ['+', [1, nil], [4, 'd']], + ['=', [1, 'e'], [5, 'e']] + ] + } + + it_has_behavior 'compare sequences correctly' + end + + describe 'using %w(a e v) & %w(x a b c d e w x)' do + let(:s1) { %w(a e v) } + let(:s2) { %w(x a b c d e w x) } + let(:result) { + [ + ['+', [0, nil], [0, 'x']], + ['=', [0, 'a'], [1, 'a']], + ['+', [1, nil], [2, 'b']], + ['+', [1, nil], [3, 'c']], + ['+', [1, nil], [4, 'd']], + ['=', [1, 'e'], [5, 'e']], + ['!', [2, 'v'], [6, 'w']], + ['+', [3, nil], [7, 'x']] + ] + } + + it_has_behavior 'compare sequences correctly' + end + + describe 'using %w() & %w(a b c)' do + let(:s1) { %w() } + let(:s2) { %w(a b c) } + let(:result) { + [ + ['+', [0, nil], [0, 'a']], + ['+', [0, nil], [1, 'b']], + ['+', [0, nil], [2, 'c']] + ] + } + + it_has_behavior 'compare sequences correctly' + end + + describe 'using %w(a b c) & %w(1)' do + let(:s1) { %w(a b c) } + let(:s2) { %w(1) } + let(:result) { + [ + ['!', [0, 'a'], [0, '1']], + ['-', [1, 'b'], [1, nil]], + ['-', [2, 'c'], [1, nil]] + ] + } + + it_has_behavior 'compare sequences correctly' + end + + describe 'using %w(a b c) & %w(c)' do + let(:s1) { %w(a b c) } + let(:s2) { %w(c) } + let(:result) { + [ + ['-', [0, 'a'], [0, nil]], + ['-', [1, 'b'], [0, nil]], + ['=', [2, 'c'], [0, 'c']] + ] + } + + it_has_behavior 'compare sequences correctly' + end + + describe 'using %w(abcd efgh ijkl mnop) & []' do + let(:s1) { %w(abcd efgh ijkl mnop) } + let(:s2) { [] } + let(:result) { + [ + ['-', [0, 'abcd'], [0, nil]], + ['-', [1, 'efgh'], [0, nil]], + ['-', [2, 'ijkl'], [0, nil]], + ['-', [3, 'mnop'], [0, nil]] + ] + } + + it_has_behavior 'compare sequences correctly' + end + + describe 'using [[1,2]] & []' do + let(:s1) { [[1, 2]] } + let(:s2) { [] } + let(:result) { + [ + ['-', [0, [1, 2]], [0, nil]] + ] + } + + it_has_behavior 'compare sequences correctly' + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/spec_helper.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/spec_helper.rb new file mode 100644 index 0000000..41e1f8e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/spec_helper.rb @@ -0,0 +1,374 @@ +# frozen_string_literal: true + +require 'rubygems' +require 'pathname' + +require 'psych' if RUBY_VERSION >= '1.9' + +if ENV['COVERAGE'] + require 'simplecov' + + def require_do(resource) + require resource + yield if block_given? + rescue LoadError + nil + end + + formatters = [SimpleCov::Formatter::HTMLFormatter] + + require_do('simplecov-rcov') { + formatters << SimpleCov::Formatter::RcovFormatter + } + require_do('simplecov-vim/formatter') { + formatters << SimpleCov::Formatter::VimFormatter + } + require_do('simplecov-sublime-ruby-coverage') { + formatters << SimpleCov::Formatter::SublimeRubyCoverageFormatter + } + + SimpleCov.start do + formatter SimpleCov::Formatter::MultiFormatter.new(formatters) + end +end + +file = Pathname.new(__FILE__).expand_path +path = file.parent +parent = path.parent + +$:.unshift parent.join('lib') + +module CaptureSubprocessIO + def _synchronize + yield + end + + def capture_subprocess_io + _synchronize { _capture_subprocess_io { yield } } + end + + def _capture_subprocess_io + require 'tempfile' + + captured_stdout, captured_stderr = Tempfile.new('out'), Tempfile.new('err') + + orig_stdout, orig_stderr = $stdout.dup, $stderr.dup + $stdout.reopen captured_stdout + $stderr.reopen captured_stderr + + yield + + $stdout.rewind + $stderr.rewind + + [captured_stdout.read, captured_stderr.read] + ensure + captured_stdout.unlink + captured_stderr.unlink + $stdout.reopen orig_stdout + $stderr.reopen orig_stderr + end + private :_capture_subprocess_io +end + +require 'diff-lcs' + +module Diff::LCS::SpecHelper + def hello + 'hello' + end + + def hello_ary + %w(h e l l o) + end + + def seq1 + %w(a b c e h j l m n p) + end + + def skipped_seq1 + %w(a h n p) + end + + def seq2 + %w(b c d e f j k l m r s t) + end + + def skipped_seq2 + %w(d f k r s t) + end + + def word_sequence + %w(abcd efgh ijkl mnopqrstuvwxyz) + end + + def correct_lcs + %w(b c e j l m) + end + + def correct_forward_diff + [ + [ + ['-', 0, 'a'] + ], + [ + ['+', 2, 'd'] + ], + [ + ['-', 4, 'h'], + ['+', 4, 'f'] + ], + [ + ['+', 6, 'k'] + ], + [ + ['-', 8, 'n'], + ['+', 9, 'r'], + ['-', 9, 'p'], + ['+', 10, 's'], + ['+', 11, 't'] + ] + ] + end + + def correct_backward_diff + [ + [ + ['+', 0, 'a'] + ], + [ + ['-', 2, 'd'] + ], + [ + ['-', 4, 'f'], + ['+', 4, 'h'] + ], + [ + ['-', 6, 'k'] + ], + [ + ['-', 9, 'r'], + ['+', 8, 'n'], + ['-', 10, 's'], + ['+', 9, 'p'], + ['-', 11, 't'] + ] + ] + end + + def correct_forward_sdiff + [ + ['-', [0, 'a'], [0, nil]], + ['=', [1, 'b'], [0, 'b']], + ['=', [2, 'c'], [1, 'c']], + ['+', [3, nil], [2, 'd']], + ['=', [3, 'e'], [3, 'e']], + ['!', [4, 'h'], [4, 'f']], + ['=', [5, 'j'], [5, 'j']], + ['+', [6, nil], [6, 'k']], + ['=', [6, 'l'], [7, 'l']], + ['=', [7, 'm'], [8, 'm']], + ['!', [8, 'n'], [9, 'r']], + ['!', [9, 'p'], [10, 's']], + ['+', [10, nil], [11, 't']] + ] + end + + def reverse_sdiff(forward_sdiff) + forward_sdiff.map { |line| + line[1], line[2] = line[2], line[1] + case line[0] + when '-' then line[0] = '+' + when '+' then line[0] = '-' + end + line + } + end + + def change_diff(diff) + map_diffs(diff, Diff::LCS::Change) + end + + def context_diff(diff) + map_diffs(diff, Diff::LCS::ContextChange) + end + + def format_diffs(diffs) + diffs.map { |e| + if e.kind_of?(Array) + e.map { |f| f.to_a.join }.join(', ') + else + e.to_a.join + end + }.join("\n") + end + + def map_diffs(diffs, klass = Diff::LCS::ContextChange) + diffs.map do |chunks| + if klass == Diff::LCS::ContextChange + klass.from_a(chunks) + else + chunks.map { |changes| klass.from_a(changes) } + end + end + end + + def balanced_traversal(s1, s2, callback_type) + callback = __send__(callback_type) + Diff::LCS.traverse_balanced(s1, s2, callback) + callback + end + + def balanced_reverse(change_result) + new_result = [] + change_result.each do |line| + line = [line[0], line[2], line[1]] + case line[0] + when '<' + line[0] = '>' + when '>' + line[0] = '<' + end + new_result << line + end + new_result.sort_by { |line| [line[1], line[2]] } + end + + def map_to_no_change(change_result) + new_result = [] + change_result.each do |line| + case line[0] + when '!' + new_result << ['<', line[1], line[2]] + new_result << ['>', line[1] + 1, line[2]] + else + new_result << line + end + end + new_result + end + + def simple_callback + callbacks = Object.new + class << callbacks + attr_reader :matched_a + attr_reader :matched_b + attr_reader :discards_a + attr_reader :discards_b + attr_reader :done_a + attr_reader :done_b + + def reset + @matched_a = [] + @matched_b = [] + @discards_a = [] + @discards_b = [] + @done_a = [] + @done_b = [] + end + + def match(event) + @matched_a << event.old_element + @matched_b << event.new_element + end + + def discard_b(event) + @discards_b << event.new_element + end + + def discard_a(event) + @discards_a << event.old_element + end + + def finished_a(event) + @done_a << [ + event.old_element, event.old_position, + event.new_element, event.new_position + ] + end + + def finished_b(event) + @done_b << [ + event.old_element, event.old_position, + event.new_element, event.new_position + ] + end + end + callbacks.reset + callbacks + end + + def simple_callback_no_finishers + simple = simple_callback + class << simple + undef :finished_a + undef :finished_b + end + simple + end + + def balanced_callback + cb = Object.new + class << cb + attr_reader :result + + def reset + @result = [] + end + + def match(event) + @result << ['=', event.old_position, event.new_position] + end + + def discard_a(event) + @result << ['<', event.old_position, event.new_position] + end + + def discard_b(event) + @result << ['>', event.old_position, event.new_position] + end + + def change(event) + @result << ['!', event.old_position, event.new_position] + end + end + cb.reset + cb + end + + def balanced_callback_no_change + balanced = balanced_callback + class << balanced + undef :change + end + balanced + end + + module Matchers + extend RSpec::Matchers::DSL + + matcher :be_nil_or_match_values do |ii, s1, s2| + match do |ee| + expect(ee).to(satisfy { |vee| vee.nil? || s1[ii] == s2[ee] }) + end + end + + matcher :correctly_map_sequence do |s1| + match do |actual| + actual.each_with_index { |ee, ii| + expect(ee).to be_nil_or_match_values(ii, s1, @s2) + } + end + + chain :to_other_sequence do |s2| + @s2 = s2 + end + end + end +end + +RSpec.configure do |conf| + conf.include Diff::LCS::SpecHelper + conf.alias_it_should_behave_like_to :it_has_behavior, 'has behavior:' + conf.filter_run_excluding :broken => true +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/traverse_balanced_spec.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/traverse_balanced_spec.rb new file mode 100644 index 0000000..9ee68ea --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/traverse_balanced_spec.rb @@ -0,0 +1,310 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Diff::LCS.traverse_balanced' do + include Diff::LCS::SpecHelper::Matchers + + shared_examples 'with a #change callback' do |s1, s2, result| + it 'traverses s1 -> s2 correctly' do + traversal = balanced_traversal(s1, s2, :balanced_callback) + expect(traversal.result).to eq(result) + end + + it 'traverses s2 -> s1 correctly' do + traversal = balanced_traversal(s2, s1, :balanced_callback) + expect(traversal.result).to eq(balanced_reverse(result)) + end + end + + shared_examples 'without a #change callback' do |s1, s2, result| + it 'traverses s1 -> s2 correctly' do + traversal = balanced_traversal(s1, s2, :balanced_callback_no_change) + expect(traversal.result).to eq(map_to_no_change(result)) + end + + it 'traverses s2 -> s1 correctly' do + traversal = balanced_traversal(s2, s1, :balanced_callback_no_change) + expect(traversal.result).to eq(map_to_no_change(balanced_reverse(result))) + end + end + + describe "identical string sequences ('abc')" do + s1 = s2 = 'abc' + + result = [ + ['=', 0, 0], + ['=', 1, 1], + ['=', 2, 2] + ] + + it_has_behavior 'with a #change callback', s1, s2, result + it_has_behavior 'without a #change callback', s1, s2, result + end + + describe 'identical array sequences %w(a b c)' do + s1 = s2 = %w(a b c) + + result = [ + ['=', 0, 0], + ['=', 1, 1], + ['=', 2, 2] + ] + + it_has_behavior 'with a #change callback', s1, s2, result + it_has_behavior 'without a #change callback', s1, s2, result + end + + describe 'sequences %w(a b c) & %w(a x c)' do + s1 = %w(a b c) + s2 = %w(a x c) + + result = [ + ['=', 0, 0], + ['!', 1, 1], + ['=', 2, 2] + ] + + it_has_behavior 'with a #change callback', s1, s2, result + it_has_behavior 'without a #change callback', s1, s2, result + end + + describe 'sequences %w(a x y c) & %w(a v w c)' do + s1 = %w(a x y c) + s2 = %w(a v w c) + + result = [ + ['=', 0, 0], + ['!', 1, 1], + ['!', 2, 2], + ['=', 3, 3] + ] + + it_has_behavior 'with a #change callback', s1, s2, result + it_has_behavior 'without a #change callback', s1, s2, result + end + + describe 'sequences %w(x y c) & %w(v w c)' do + s1 = %w(x y c) + s2 = %w(v w c) + result = [ + ['!', 0, 0], + ['!', 1, 1], + ['=', 2, 2] + ] + + it_has_behavior 'with a #change callback', s1, s2, result + it_has_behavior 'without a #change callback', s1, s2, result + end + + describe 'sequences %w(a x y z) & %w(b v w)' do + s1 = %w(a x y z) + s2 = %w(b v w) + result = [ + ['!', 0, 0], + ['!', 1, 1], + ['!', 2, 2], + ['<', 3, 3] + ] + + it_has_behavior 'with a #change callback', s1, s2, result + it_has_behavior 'without a #change callback', s1, s2, result + end + + describe 'sequences %w(a z) & %w(a)' do + s1 = %w(a z) + s2 = %w(a) + result = [ + ['=', 0, 0], + ['<', 1, 1] + ] + + it_has_behavior 'with a #change callback', s1, s2, result + it_has_behavior 'without a #change callback', s1, s2, result + end + + describe 'sequences %w(z a) & %w(a)' do + s1 = %w(z a) + s2 = %w(a) + result = [ + ['<', 0, 0], + ['=', 1, 0] + ] + + it_has_behavior 'with a #change callback', s1, s2, result + it_has_behavior 'without a #change callback', s1, s2, result + end + + describe 'sequences %w(a b c) & %w(x y z)' do + s1 = %w(a b c) + s2 = %w(x y z) + result = [ + ['!', 0, 0], + ['!', 1, 1], + ['!', 2, 2] + ] + + it_has_behavior 'with a #change callback', s1, s2, result + it_has_behavior 'without a #change callback', s1, s2, result + end + + describe 'sequences %w(abcd efgh ijkl mnoopqrstuvwxyz) & []' do + s1 = %w(abcd efgh ijkl mnopqrstuvwxyz) + s2 = [] + result = [ + ['<', 0, 0], + ['<', 1, 0], + ['<', 2, 0], + ['<', 3, 0] + ] + + it_has_behavior 'with a #change callback', s1, s2, result + it_has_behavior 'without a #change callback', s1, s2, result + end + + describe 'strings %q(a b c) & %q(a x c)' do + s1 = 'a b c' + s2 = 'a x c' + + result = [ + ['=', 0, 0], + ['=', 1, 1], + ['!', 2, 2], + ['=', 3, 3], + ['=', 4, 4] + ] + + it_has_behavior 'with a #change callback', s1, s2, result + it_has_behavior 'without a #change callback', s1, s2, result + end + + describe 'strings %q(a x y c) & %q(a v w c)' do + s1 = 'a x y c' + s2 = 'a v w c' + + result = [ + ['=', 0, 0], + ['=', 1, 1], + ['!', 2, 2], + ['=', 3, 3], + ['!', 4, 4], + ['=', 5, 5], + ['=', 6, 6] + ] + + it_has_behavior 'with a #change callback', s1, s2, result + it_has_behavior 'without a #change callback', s1, s2, result + end + + describe 'strings %q(x y c) & %q(v w c)' do + s1 = 'x y c' + s2 = 'v w c' + result = [ + ['!', 0, 0], + ['=', 1, 1], + ['!', 2, 2], + ['=', 3, 3], + ['=', 4, 4] + ] + + it_has_behavior 'with a #change callback', s1, s2, result + it_has_behavior 'without a #change callback', s1, s2, result + end + + describe 'strings %q(a x y z) & %q(b v w)' do + s1 = 'a x y z' + s2 = 'b v w' + result = [ + ['!', 0, 0], + ['=', 1, 1], + ['!', 2, 2], + ['=', 3, 3], + ['!', 4, 4], + ['<', 5, 5], + ['<', 6, 5] + ] + + it_has_behavior 'with a #change callback', s1, s2, result + it_has_behavior 'without a #change callback', s1, s2, result + end + + describe 'strings %q(a z) & %q(a)' do + s1 = 'a z' + s2 = 'a' + result = [ + ['=', 0, 0], + ['<', 1, 1], + ['<', 2, 1] + ] + + it_has_behavior 'with a #change callback', s1, s2, result + it_has_behavior 'without a #change callback', s1, s2, result + end + + describe 'strings %q(z a) & %q(a)' do + s1 = 'z a' + s2 = 'a' + result = [ + ['<', 0, 0], + ['<', 1, 0], + ['=', 2, 0] + ] + + it_has_behavior 'with a #change callback', s1, s2, result + it_has_behavior 'without a #change callback', s1, s2, result + end + + describe 'strings %q(a b c) & %q(x y z)' do + s1 = 'a b c' + s2 = 'x y z' + result = [ + ['!', 0, 0], + ['=', 1, 1], + ['!', 2, 2], + ['=', 3, 3], + ['!', 4, 4] + ] + + it_has_behavior 'with a #change callback', s1, s2, result + it_has_behavior 'without a #change callback', s1, s2, result + end + + describe 'strings %q(abcd efgh ijkl mnopqrstuvwxyz) & %q()' do + s1 = 'abcd efgh ijkl mnopqrstuvwxyz' + s2 = '' + result = [ + ['<', 0, 0], + ['<', 1, 0], + ['<', 2, 0], + ['<', 3, 0], + ['<', 4, 0], + ['<', 5, 0], + ['<', 6, 0], + ['<', 7, 0], + ['<', 8, 0], + ['<', 9, 0], + ['<', 10, 0], + ['<', 11, 0], + ['<', 12, 0], + ['<', 13, 0], + ['<', 14, 0], + ['<', 15, 0], + ['<', 16, 0], + ['<', 17, 0], + ['<', 18, 0], + ['<', 19, 0], + ['<', 20, 0], + ['<', 21, 0], + ['<', 22, 0], + ['<', 23, 0], + ['<', 24, 0], + ['<', 25, 0], + ['<', 26, 0], + ['<', 27, 0], + ['<', 28, 0] + ] + + it_has_behavior 'with a #change callback', s1, s2, result + it_has_behavior 'without a #change callback', s1, s2, result + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/traverse_sequences_spec.rb b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/traverse_sequences_spec.rb new file mode 100644 index 0000000..b185e1d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/diff-lcs-1.5.0/spec/traverse_sequences_spec.rb @@ -0,0 +1,137 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Diff::LCS.traverse_sequences' do + describe 'callback with no finishers' do + describe 'over (seq1, seq2)' do + before(:each) do + @callback_s1_s2 = simple_callback_no_finishers + Diff::LCS.traverse_sequences(seq1, seq2, @callback_s1_s2) + + @callback_s2_s1 = simple_callback_no_finishers + Diff::LCS.traverse_sequences(seq2, seq1, @callback_s2_s1) + end + + it 'has the correct LCS result on left-matches' do + expect(@callback_s1_s2.matched_a).to eq(correct_lcs) + expect(@callback_s2_s1.matched_a).to eq(correct_lcs) + end + + it 'has the correct LCS result on right-matches' do + expect(@callback_s1_s2.matched_b).to eq(correct_lcs) + expect(@callback_s2_s1.matched_b).to eq(correct_lcs) + end + + it 'has the correct skipped sequences with the left sequence' do + expect(@callback_s1_s2.discards_a).to eq(skipped_seq1) + expect(@callback_s2_s1.discards_a).to eq(skipped_seq2) + end + + it 'has the correct skipped sequences with the right sequence' do + expect(@callback_s1_s2.discards_b).to eq(skipped_seq2) + expect(@callback_s2_s1.discards_b).to eq(skipped_seq1) + end + + it 'does not have anything done markers from the left or right sequences' do + expect(@callback_s1_s2.done_a).to be_empty + expect(@callback_s1_s2.done_b).to be_empty + expect(@callback_s2_s1.done_a).to be_empty + expect(@callback_s2_s1.done_b).to be_empty + end + end + + describe 'over (hello, hello)' do + before(:each) do + @callback = simple_callback_no_finishers + Diff::LCS.traverse_sequences(hello, hello, @callback) + end + + it 'has the correct LCS result on left-matches' do + expect(@callback.matched_a).to eq(hello.split(//)) + end + + it 'has the correct LCS result on right-matches' do + expect(@callback.matched_b).to eq(hello.split(//)) + end + + it 'has the correct skipped sequences with the left sequence', :only => true do + expect(@callback.discards_a).to be_empty + end + + it 'has the correct skipped sequences with the right sequence' do + expect(@callback.discards_b).to be_empty + end + + it 'does not have anything done markers from the left or right sequences' do + expect(@callback.done_a).to be_empty + expect(@callback.done_b).to be_empty + end + end + + describe 'over (hello_ary, hello_ary)' do + before(:each) do + @callback = simple_callback_no_finishers + Diff::LCS.traverse_sequences(hello_ary, hello_ary, @callback) + end + + it 'has the correct LCS result on left-matches' do + expect(@callback.matched_a).to eq(hello_ary) + end + + it 'has the correct LCS result on right-matches' do + expect(@callback.matched_b).to eq(hello_ary) + end + + it 'has the correct skipped sequences with the left sequence' do + expect(@callback.discards_a).to be_empty + end + + it 'has the correct skipped sequences with the right sequence' do + expect(@callback.discards_b).to be_empty + end + + it 'does not have anything done markers from the left or right sequences' do + expect(@callback.done_a).to be_empty + expect(@callback.done_b).to be_empty + end + end + end + + describe 'callback with finisher' do + before(:each) do + @callback_s1_s2 = simple_callback + Diff::LCS.traverse_sequences(seq1, seq2, @callback_s1_s2) + @callback_s2_s1 = simple_callback + Diff::LCS.traverse_sequences(seq2, seq1, @callback_s2_s1) + end + + it 'has the correct LCS result on left-matches' do + expect(@callback_s1_s2.matched_a).to eq(correct_lcs) + expect(@callback_s2_s1.matched_a).to eq(correct_lcs) + end + + it 'has the correct LCS result on right-matches' do + expect(@callback_s1_s2.matched_b).to eq(correct_lcs) + expect(@callback_s2_s1.matched_b).to eq(correct_lcs) + end + + it 'has the correct skipped sequences for the left sequence' do + expect(@callback_s1_s2.discards_a).to eq(skipped_seq1) + expect(@callback_s2_s1.discards_a).to eq(skipped_seq2) + end + + it 'has the correct skipped sequences for the right sequence' do + expect(@callback_s1_s2.discards_b).to eq(skipped_seq2) + expect(@callback_s2_s1.discards_b).to eq(skipped_seq1) + end + + it 'has done markers differently-sized sequences' do + expect(@callback_s1_s2.done_a).to eq([['p', 9, 't', 11]]) + expect(@callback_s1_s2.done_b).to be_empty + + expect(@callback_s2_s1.done_a).to be_empty + expect(@callback_s2_s1.done_b).to eq([['t', 11, 'p', 9]]) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/CHANGELOG.md b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/CHANGELOG.md new file mode 100644 index 0000000..c9bc56f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/CHANGELOG.md @@ -0,0 +1,574 @@ +# Faraday Changelog + +## The changelog has moved! + +This file is not being updated anymore. Instead, please check the [Releases](https://github.com/lostisland/faraday/releases) page. + +## [2.2.0](https://github.com/lostisland/faraday/compare/v2.1.0...v2.2.0) (2022-02-03) + +* Reintroduce the possibility to register middleware with symbols, strings or procs in [#1391](https://github.com/lostisland/faraday/pull/1391) + +## [2.1.0](https://github.com/lostisland/faraday/compare/v2.0.1...v2.1.0) (2022-01-15) + +* Fix test adapter thread safety by @iMacTia in [#1380](https://github.com/lostisland/faraday/pull/1380) +* Add default adapter options by @hirasawayuki in [#1382](https://github.com/lostisland/faraday/pull/1382) +* CI: Add Ruby 3.1 to matrix by @petergoldstein in [#1374](https://github.com/lostisland/faraday/pull/1374) +* docs: fix regex pattern in logger.md examples by @hirasawayuki in [#1378](https://github.com/lostisland/faraday/pull/1378) + +## [2.0.1](https://github.com/lostisland/faraday/compare/v2.0.0...v2.0.1) (2022-01-05) + +* Re-add `faraday-net_http` as default adapter by @iMacTia in [#1366](https://github.com/lostisland/faraday/pull/1366) +* Updated sample format in UPGRADING.md by @vimutter in [#1361](https://github.com/lostisland/faraday/pull/1361) +* docs: Make UPGRADING examples more copyable by @olleolleolle in [#1363](https://github.com/lostisland/faraday/pull/1363) + +## [2.0.0](https://github.com/lostisland/faraday/compare/v1.8.0...v2.0.0) (2022-01-04) + +The next major release is here, and it comes almost 2 years after the release of v1.0! + +This release changes the way you use Faraday and embraces a new paradigm of Faraday as an ecosystem, rather than a library. + +What does that mean? It means that Faraday is less of a bundled tool and more of a framework for the community to build on top of. + +As a result, all adapters and some middleware have moved out and are now shipped as standalone gems 🙌! + +But this doesn't mean that upgrading from Faraday 1.x to Faraday 2.0 should be hard, in fact we've listed everything you need to do in the [UPGRADING.md](https://github.com/lostisland/faraday/blob/main/UPGRADING.md) doc. + +Moreover, we've setup a new [awesome-faraday](https://github.com/lostisland/awesome-faraday) repository that will showcase a curated list of adapters and middleware 😎. + +This release was the result of the efforts of the core team and all the contributors, new and old, that have helped achieve this milestone 👏. + +## What's Changed + +* Autoloading, dependency loading and middleware registry cleanup by @iMacTia in [#1301](https://github.com/lostisland/faraday/pull/1301) +* Move JSON middleware (request and response) from faraday_middleware by @iMacTia in [#1300](https://github.com/lostisland/faraday/pull/1300) +* Remove deprecated `Faraday::Request#method` by @olleolleolle in [#1303](https://github.com/lostisland/faraday/pull/1303) +* Remove deprecated `Faraday::UploadIO` by @iMacTia in [#1307](https://github.com/lostisland/faraday/pull/1307) +* [1.x] Deprecate Authorization helpers in `Faraday::Connection` by @iMacTia in [#1306](https://github.com/lostisland/faraday/pull/1306) +* Drop deprecated auth helpers from Connection and refactor auth middleware by @iMacTia in [#1308](https://github.com/lostisland/faraday/pull/1308) +* Add Faraday 1.x examples in authentication.md docs by @iMacTia in [#1320](https://github.com/lostisland/faraday/pull/1320) +* Fix passing a URL with embedded basic auth by @iMacTia in [#1324](https://github.com/lostisland/faraday/pull/1324) +* Register JSON middleware by @mollerhoj in [#1331](https://github.com/lostisland/faraday/pull/1331) +* Retry middleware should handle string exception class name consistently by @jrochkind in [#1334](https://github.com/lostisland/faraday/pull/1334) +* Improve request info in exceptions raised by RaiseError Middleware by @willianzocolau in [#1335](https://github.com/lostisland/faraday/pull/1335) +* Remove net-http adapter and update docs by @iMacTia in [#1336](https://github.com/lostisland/faraday/pull/1336) +* Explain plan for faraday_middleware in UPGRADING.md by @iMacTia in [#1339](https://github.com/lostisland/faraday/pull/1339) +* Scripts folder cleanup by @iMacTia in [#1340](https://github.com/lostisland/faraday/pull/1340) +* Replace `Hash#merge` with `Utils#deep_merge` for connection options by @xkwd in [#1343](https://github.com/lostisland/faraday/pull/1343) +* Callable authorizers by @sled in [#1345](https://github.com/lostisland/faraday/pull/1345) +* Default value for exc error by @DariuszMusielak in [#1351](https://github.com/lostisland/faraday/pull/1351) +* Don't call `retry_block` unless a retry is going to happen by @jrochkind in [#1350](https://github.com/lostisland/faraday/pull/1350) +* Improve documentation for v2 by @iMacTia in [#1353](https://github.com/lostisland/faraday/pull/1353) +* Remove default `default_adapter` (yes, you read that right) by @iMacTia in [#1354](https://github.com/lostisland/faraday/pull/1354) +* Remove retry middleware by @iMacTia in [#1356](https://github.com/lostisland/faraday/pull/1356) +* Remove multipart middleware and all its documentation and tests by @iMacTia in [#1357](https://github.com/lostisland/faraday/pull/1357) + +## [1.9.3](https://github.com/lostisland/faraday/compare/v1.9.2...v1.9.3) (2022-01-06) + +* Re-add support for Ruby 2.4+ by @iMacTia in [#1371](https://github.com/lostisland/faraday/pull/1371) + +## [1.9.2](https://github.com/lostisland/faraday/compare/v1.9.1...v1.9.2) (2022-01-06) + +* Add alias with legacy name to gemified middleware by @iMacTia in [#1372](https://github.com/lostisland/faraday/pull/1372) + +## [1.9.1](https://github.com/lostisland/faraday/compare/v1.9.0...v1.9.1) (2022-01-06) + +* Update adapter dependencies in Gemspec by @iMacTia in [#1370](https://github.com/lostisland/faraday/pull/1370) + +## [1.9.0](https://github.com/lostisland/faraday/compare/v1.8.0...v1.9.0) (2022-01-06) + +* Use external multipart and retry middleware by @iMacTia in [#1367](https://github.com/lostisland/faraday/pull/1367) + +## [1.8.0](https://github.com/lostisland/faraday/releases/tag/v1.8.0) (2021-09-18) + +### Features + +* Backport authorization procs (#1322, @jarl-dk) + +## [v1.7.0](https://github.com/lostisland/faraday/releases/tag/v1.7.0) (2021-08-09) + +### Features + +* Add strict_mode to Test::Stubs (#1298, @yykamei) + +## [v1.6.0](https://github.com/lostisland/faraday/releases/tag/v1.6.0) (2021-08-01) + +### Misc + +* Use external Rack adapter (#1296, @iMacTia) + +## [v1.5.1](https://github.com/lostisland/faraday/releases/tag/v1.5.1) (2021-07-11) + +### Fixes + +* Fix JRuby incompatibility after moving out EM adapters (#1294, @ahorek) + +### Documentation + +* Update YARD to follow RackBuilder (#1292, @kachick) + +## [v1.5.0](https://github.com/lostisland/faraday/releases/tag/v1.5.0) (2021-07-04) + +### Misc + +* Use external httpclient adapter (#1289, @iMacTia) +* Use external patron adapter (#1290, @iMacTia) + +## [v1.4.3](https://github.com/lostisland/faraday/releases/tag/v1.4.3) (2021-06-24) + +### Fixes + +* Silence warning (#1286, @gurgeous) +* Always dup url_prefix in Connection#build_exclusive_url (#1288, @alexeyds) + +## [v1.4.2](https://github.com/lostisland/faraday/releases/tag/v1.4.2) (2021-05-22) + +### Fixes +* Add proxy setting when url_prefix is changed (#1276, @ci) +* Default proxy scheme to http:// if necessary, fixes #1282 (#1283, @gurgeous) + +### Documentation +* Improve introduction page (#1273, @gurgeous) +* Docs: add more middleware examples (#1277, @gurgeous) + +### Misc +* Use external `em_http` and `em_synchrony` adapters (#1274, @iMacTia) + +## [v1.4.1](https://github.com/lostisland/faraday/releases/tag/v1.4.1) (2021-04-18) + +### Fixes + +* Fix dependencies from external adapter gems (#1269, @iMacTia) + +## [v1.4.0](https://github.com/lostisland/faraday/releases/tag/v1.4.0) (2021-04-16) + +### Highlights + +With this release, we continue the work of gradually moving out adapters into their own gems 🎉 +Thanks to @MikeRogers0 for helping the Faraday team in progressing with this quest 👏 + +And thanks to @olleolleolle efforts, Faraday is becoming more inclusive than ever 🤗 +Faraday's `master` branch has been renamed into `main`, we have an official policy on inclusive language and even a rubocop plugin to check for non-inclusive words ❤️! +Checkout the "Misc" section below for more details 🙌 ! + +### Fixes + +* Fix NoMethodError undefined method 'coverage' (#1255, @Maroo-b) + +### Documentation + +* Some docs on EventMachine adapters. (#1232, @damau) +* CONTRIBUTING: Fix grammar and layout (#1261, @olleolleolle) + +### Misc + +* Replacing Net::HTTP::Persistent with faraday-net_http_persistent (#1250, @MikeRogers0) +* CI: Configure the regenerated Coveralls token (#1256, @olleolleolle) +* Replace Excon adapter with Faraday::Excon gem, and fix autoloading issue with Faraday::NetHttpPersistent (#1257, @iMacTia) +* Drop CodeClimate (#1259, @olleolleolle) +* CI: Rename default branch to main (#1263, @olleolleolle) +* Drop RDoc support file .document (#1264, @olleolleolle, @iMacTia) +* CONTRIBUTING: add a policy on inclusive language (#1262, @olleolleolle) +* Add rubocop-inclusivity (#1267, @olleolleolle, @iMacTia) + +## [v1.3.1](https://github.com/lostisland/faraday/releases/tag/v1.3.1) (2021-04-16) + +### Fixes + +* Escape colon in path segment (#1237, @yarafan) +* Handle IPv6 address String on Faraday::Connection#proxy_from_env (#1252, @cosmo0920) + +### Documentation + +* Fix broken Rubydoc.info links (#1236, @nickcampbell18) +* Add httpx to list of external adapters (#1246, @HoneyryderChuck) + +### Misc + +* Refactor CI to remove duplicated line (#1230, @tricknotes) +* Gemspec: Pick a good ruby2_keywords release (#1241, @olleolleolle) + +## [v1.3.0](https://github.com/lostisland/faraday/releases/tag/v1.3.0) (2020-12-31) + +### Highlights +Faraday v1.3.0 is the first release to officially support Ruby 3.0 in the CI pipeline 🎉 🍾! + +This is also the first release with a previously "included" adapter (Net::HTTP) being isolated into a [separate gem](https://github.com/lostisland/faraday-net_http) 🎊! +The new adapter is added to Faraday as a dependency for now, so that means full backwards-compatibility, but just to be safe be careful when upgrading! + +This is a huge step towards are Faraday v2.0 objective of pushing adapters and middleware into separate gems. +Many thanks to the Faraday Team, @JanDintel and everyone who attended the [ROSS Conf remote event](https://www.rossconf.io/event/remote/) + +### Features + +* Improves consistency with Faraday::Error and Faraday::RaiseError (#1229, @qsona, @iMacTia) + +### Fixes + +* Don't assign to global ::Timer (#1227, @bpo) + +### Documentation + +* CHANGELOG: add releases after 1.0 (#1225, @olleolleolle) +* Improves retry middleware documentation. (#1228, @iMacTia) + +### Misc + +* Move out Net::HTTP adapter (#1222, @JanDintel, @iMacTia) +* Adds Ruby 3.0 to CI Matrix (#1226, @iMacTia) + + +## [v1.2.0](https://github.com/lostisland/faraday/releases/tag/v1.2.0) (2020-12-23) + +### Features + +* Introduces `on_request` and `on_complete` methods in `Faraday::Middleware`. (#1194, @iMacTia) + +### Fixes + +* Require 'date' to avoid retry exception (#1206, @rustygeldmacher) +* Fix rdebug recursion issue (#1205, @native-api) +* Update call to `em_http_ssl_patch` (#1202, @kylekeesling) +* `EmHttp` adapter: drop superfluous loaded? check (#1213, @olleolleolle) +* Avoid 1 use of keyword hackery (#1211, @grosser) +* Fix #1219 `Net::HTTP` still uses env proxy (#1221, @iMacTia) + +### Documentation + +* Add comment in gemspec to explain exposure of `examples` and `spec` folders. (#1192, @iMacTia) +* Adapters, how to create them (#1193, @olleolleolle) +* Update documentation on using the logger (#1196, @tijmenb) +* Adjust the retry documentation and spec to align with implementation (#1198, @nbeyer) + +### Misc + +* Test against ruby head (#1208, @grosser) + +## [v1.1.0](https://github.com/lostisland/faraday/releases/tag/v1.1.0) (2020-10-17) + +### Features + +* Makes parameters sorting configurable (#1162 @wishdev) +* Introduces `flat_encode` option for multipart adapter. (#1163 @iMacTia) +* Include request info in exceptions raised by RaiseError Middleware (#1181 @SandroDamilano) + +### Fixes + +* Avoid `last arg as keyword param` warning when building user middleware on Ruby 2.7 (#1153 @dgholz) +* Limits net-http-persistent version to < 4.0 (#1156 @iMacTia) +* Update `typhoeus` to new stable version (`1.4`) (#1159 @AlexWayfer) +* Properly fix test failure with Rack 2.1+. (#1171 @voxik) + +### Documentation + +* Improves documentation on how to contribute to the site by using Docker. (#1175 @iMacTia) +* Remove retry_change_requests from documentation (#1185 @stim371) + +### Misc + +* Link from GitHub Actions badge to CI workflow (#1141 @olleolleolle) +* Return tests of `Test` adapter (#1147 @AlexWayfer) +* Add 1.0 release to wording in CONTRIBUTING (#1155 @olleolleolle) +* Fix linting bumping Rubocop to 0.90.0 (#1182 @iMacTia) +* Drop `git ls-files` in gemspec (#1183 @utkarsh2102) +* Upgrade CI to ruby/setup-ruby (#1187 @gogainda) + +## [v1.0.1](https://github.com/lostisland/faraday/releases/tag/v1.0.1) (2020-03-29) + +### Fixes + +* Use Net::HTTP#start(&block) to ensure closed TCP connections (#1117) +* Fully qualify constants to be checked (#1122) +* Allows `parse` method to be private/protected in response middleware (#1123) +* Encode Spaces in Query Strings as '%20' Instead of '+' (#1125) +* Limits rack to v2.0.x (#1127) +* Adapter Registry reads also use mutex (#1136) + +### Documentation + +* Retry middleware documentation fix (#1109) +* Docs(retry): precise usage of retry-after (#1111) +* README: Link the logo to the website (#1112) +* Website: add search bar (#1116) +* Fix request/response mix-up in docs text (#1132) + +## [v1.0](https://github.com/lostisland/faraday/releases/tag/v1.0.0) (2020-01-22) + +Features: + +* Add #trace support to Faraday::Connection #861 (@technoweenie) +* Add the log formatter that is easy to override and safe to inherit #889 (@prikha) +* Support standalone adapters #941 (@iMacTia) +* Introduce Faraday::ConflictError for 409 response code #979 (@lucasmoreno) +* Add support for setting `read_timeout` option separately #1003 (@springerigor) +* Refactor and cleanup timeout settings across adapters #1022 (@technoweenie) +* Create ParamPart class to allow multipart posts with JSON content and file upload at the same time #1017 (@jeremy-israel) +* Copy UploadIO const -> FilePart for consistency with ParamPart #1018, #1021 (@technoweenie) +* Implement streaming responses in the Excon adapter #1026 (@technoweenie) +* Add default implementation of `Middleware#close`. #1069 (@ioquatix) +* Add `Adapter#close` so that derived classes can call super. #1091 (@ioquatix) +* Add log_level option to logger default formatter #1079 (@amrrbakry) +* Fix empty array for FlatParamsEncoder `{key: []} -> "key="` #1084 (@mrexox) + +Bugs: + +* Explicitly require date for DateTime library in Retry middleware #844 (@nickpresta) +* Refactor Adapter as final endpoints #846 (@iMacTia) +* Separate Request and Response bodies in Faraday::Env #847 (@iMacTia) +* Implement Faraday::Connection#options to make HTTP requests with the OPTIONS verb. #857 (@technoweenie) +* Multipart: Drop Ruby 1.8 String behavior compat #892 (@olleolleolle) +* Fix Ruby warnings in Faraday::Options.memoized #962 (@technoweenie) +* Allow setting min/max SSL version for a Net::HTTP::Persistent connection #972, #973 (@bdewater, @olleolleolle) +* Fix instances of frozen empty string literals #1040 (@BobbyMcWho) +* remove temp_proxy and improve proxy tests #1063 (@technoweenie) +* improve error initializer consistency #1095 (@technoweenie) + +Misc: + +* Convert minitest suite to RSpec #832 (@iMacTia, with help from @gaynetdinov, @Insti, @technoweenie) +* Major effort to update code to RuboCop standards. #854 (@olleolleolle, @iMacTia, @technoweenie, @htwroclau, @jherdman, @Drenmi, @Insti) +* Rubocop #1044, #1047 (@BobbyMcWho, @olleolleolle) +* Documentation tweaks (@adsteel, @Hubro, @iMacTia, @olleolleolle, @technoweenie) +* Update license year #981 (@Kevin-Kawai) +* Configure Jekyll plugin jekyll-remote-theme to support Docker usage #999 (@Lewiscowles1986) +* Fix Ruby 2.7 warnings #1009 (@tenderlove) +* Cleanup adapter connections #1023 (@technoweenie) +* Describe clearing cached stubs #1045 (@viraptor) +* Add project metadata to the gemspec #1046 (@orien) + +## v0.17.4 + +Fixes: + +* NetHttp adapter: wrap Errno::EADDRNOTAVAIL (#1114, @embs) +* Fix === for subclasses of deprecated classes (#1243, @mervync) + +## v0.17.3 + +Fixes: + +* Reverts changes in error classes hierarchy. #1092 (@iMacTia) +* Fix Ruby 1.9 syntax errors and improve Error class testing #1094 (@BanzaiMan, + @mrexox, @technoweenie) + +Misc: + +* Stops using `&Proc.new` for block forwarding. #1083 (@olleolleolle) +* Update CI to test against ruby 2.0-2.7 #1087, #1099 (@iMacTia, @olleolleolle, + @technoweenie) +* require FARADAY_DEPRECATE=warn to show Faraday v1.0 deprecation warnings + #1098 (@technoweenie) + +## v0.17.1 + +Final release before Faraday v1.0, with important fixes for Ruby 2.7. + +Fixes: + +* RaiseError response middleware raises exception if HTTP client returns a nil + status. #1042 (@jonnyom, @BobbyMcWho) + +Misc: + +* Fix Ruby 2.7 warnings (#1009) +* Add `Faraday::Deprecate` to warn about upcoming v1.0 changes. (#1054, #1059, + #1076, #1077) +* Add release notes up to current in CHANGELOG.md (#1066) +* Port minimal rspec suite from main branch to run backported tests. (#1058) + +## v0.17.0 + +This release is the same as v0.15.4. It was pushed to cover up releases +v0.16.0-v0.16.2. + +## v0.15.4 + +* Expose `pool_size` as a option for the NetHttpPersistent adapter (#834) + +## v0.15.3 + +* Make Faraday::Request serialisable with Marshal. (#803) +* Add DEFAULT_EXCEPTIONS constant to Request::Retry (#814) +* Add support for Ruby 2.6 Net::HTTP write_timeout (#824) + +## v0.15.2 + +* Prevents `Net::HTTP` adapters to retry request internally by setting `max_retries` to 0 if available (Ruby 2.5+). (#799) +* Fixes `NestedParamsEncoder` handling of empty array values (#801) + +## v0.15.1 + +* NetHttpPersistent adapter better reuse of SSL connections (#793) +* Refactor: inline cached_connection (#797) +* Logger middleware: use $stdout instead of STDOUT (#794) +* Fix: do not memoize/reuse Patron session (#796) + +Also in this release: + +* Allow setting min/max ssl version for Net::HTTP (#792) +* Allow setting min/max ssl version for Excon (#795) + +## v0.15.0 + +Features: + +* Added retry block option to retry middleware. (#770) +* Retry middleware improvements (honour Retry-After header, retry statuses) (#773) +* Improve response logger middleware output (#784) + +Fixes: + +* Remove unused class error (#767) +* Fix minor typo in README (#760) +* Reuse persistent connections when using net-http-persistent (#778) +* Fix Retry middleware documentation (#781) +* Returns the http response when giving up on retrying by status (#783) + +## v0.14.0 + +Features: + +* Allow overriding env proxy #754 (@iMacTia) +* Remove legacy Typhoeus adapter #715 (@olleolleolle) +* External Typhoeus Adapter Compatibility #748 (@iMacTia) +* Warn about missing adapter when making a request #743 (@antstorm) +* Faraday::Adapter::Test stubs now support entire urls (with host) #741 (@erik-escobedo) + +Fixes: + +* If proxy is manually provided, this takes priority over `find_proxy` #724 (@iMacTia) +* Fixes the behaviour for Excon's open_timeout (not setting write_timeout anymore) #731 (@apachelogger) +* Handle all connection timeout messages in Patron #687 (@stayhero) + +## v0.13.1 + +* Fixes an incompatibility with Addressable::URI being used as uri_parser + +## v0.13.0 + +Features: + +* Dynamically reloads the proxy when performing a request on an absolute domain (#701) +* Adapter support for Net::HTTP::Persistent v3.0.0 (#619) + +Fixes: + +* Prefer #hostname over #host. (#714) +* Fixes an edge-case issue with response headers parsing (missing HTTP header) (#719) + +## v0.12.2 + +* Parse headers from aggregated proxy requests/responses (#681) +* Guard against invalid middleware configuration with warning (#685) +* Do not use :insecure option by default in Patron (#691) +* Fixes an issue with HTTPClient not raising a `Faraday::ConnectionFailed` (#702) +* Fixes YAML serialization/deserialization for `Faraday::Utils::Headers` (#690) +* Fixes an issue with Options having a nil value (#694) +* Fixes an issue with Faraday.default_connection not using Faraday.default_connection_options (#698) +* Fixes an issue with Options.merge! and Faraday instrumentation middleware (#710) + +## v0.12.1 + +* Fix an issue with Patron tests failing on jruby +* Fix an issue with new `rewind_files` feature that was causing an exception when the body was not an Hash +* Expose wrapped_exception in all client errors +* Add Authentication Section to the ReadMe + +## v0.12.0.1 + +* Hotfix release to address an issue with TravisCI deploy on Rubygems + +## v0.12.0 + +Features: + +* Proxy feature now relies on Ruby `URI::Generic#find_proxy` and can use `no_proxy` ENV variable (not compatible with ruby < 2.0) +* Adds support for `context` request option to pass arbitrary information to middlewares + +Fixes: + +* Fix an issue with options that was causing new options to override defaults ones unexpectedly +* Rewind `UploadIO`s on retry to fix a compatibility issue +* Make multipart boundary unique +* Improvements in `README.md` + +## v0.11.0 + +Features: + +* Add `filter` method to Logger middleware +* Add support for Ruby2.4 and Minitest 6 +* Introduce block syntax to customise the adapter + +Fixes: + +* Fix an issue that was allowing to override `default_connection_options` from a connection instance +* Fix a bug that was causing newline escape characters ("\n") to be used when building the Authorization header + +## v0.10.1 + +- Fix an issue with HTTPClient adapter that was causing the SSL to be reset on every request +- Rescue `IOError` instead of specific subclass +- `Faraday::Utils::Headers` can now be successfully serialised in YAML +- Handle `default_connection_options` set with hash + +## v0.10.0 + +Breaking changes: +- Drop support for Ruby 1.8 + +Features: +- Include wrapped exception/reponse in ClientErrors +- Add `response.reason_phrase` +- Provide option to selectively skip logging request/response headers +- Add regex support for pattern matching in `test` adapter + +Fixes: +- Add `Faraday.respond_to?` to find methods managed by `method_missing` +- em-http: `request.host` instead of `connection.host` should be taken for SSL validations +- Allow `default_connection_options` to be merged when options are passed as url parameter +- Improve splitting key-value pairs in raw HTTP headers + +## v0.9.2 + +Adapters: +- Enable gzip compression for httpclient +- Fixes default certificate store for httpclient not having default paths. +- Make excon adapter compatible with 0.44 excon version +- Add compatibility with Patron 0.4.20 +- Determine default port numbers in Net::HTTP adapters (Addressable compatibility) +- em-http: wrap "connection closed by server" as ConnectionFailed type +- Wrap Errno::ETIMEDOUT in Faraday::Error::TimeoutError + +Utils: +- Add Rack-compatible support for parsing `a[][b]=c` nested queries +- Encode nil values in queries different than empty strings. Before: `a=`; now: `a`. +- Have `Faraday::Utils::Headers#replace` clear internal key cache +- Dup the internal key cache when a Headers hash is copied + +Env and middleware: +- Ensure `env` stored on middleware response has reference to the response +- Ensure that Response properties are initialized during `on_complete` (VCR compatibility) +- Copy request options in Faraday::Connection#dup +- Env custom members should be copied by Env.from(env) +- Honour per-request `request.options.params_encoder` +- Fix `interval_randomness` data type for Retry middleware +- Add maximum interval option for Retry middleware + +## v0.9.1 + +* Refactor Net:HTTP adapter so that with_net_http_connection can be overridden to allow pooled connections. (@Ben-M) +* Add configurable methods that bypass `retry_if` in the Retry request middleware. (@mike-bourgeous) + +## v0.9.0 + +* Add HTTPClient adapter (@hakanensari) +* Improve Retry handler (@mislav) +* Remove autoloading by default (@technoweenie) +* Improve internal docs (@technoweenie, @mislav) +* Respect user/password in http proxy string (@mislav) +* Adapter options are structs. Reinforces consistent options across adapters + (@technoweenie) +* Stop stripping trailing / off base URLs in a Faraday::Connection. (@technoweenie) +* Add a configurable URI parser. (@technoweenie) +* Remove need to manually autoload when using the authorization header helpers on `Faraday::Connection`. (@technoweenie) +* `Faraday::Adapter::Test` respects the `Faraday::RequestOptions#params_encoder` option. (@technoweenie) diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/LICENSE.md b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/LICENSE.md new file mode 100644 index 0000000..e6ed1b9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/LICENSE.md @@ -0,0 +1,20 @@ +Copyright (c) 2009-2022 Rick Olson, Zack Hobson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/README.md b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/README.md new file mode 100644 index 0000000..4bf79a6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/README.md @@ -0,0 +1,55 @@ +# [![Faraday](./docs/assets/img/repo-card-slim.png)][website] + +[![Gem Version](https://badge.fury.io/rb/faraday.svg)](https://rubygems.org/gems/faraday) +[![GitHub Actions CI](https://github.com/lostisland/faraday/workflows/CI/badge.svg)](https://github.com/lostisland/faraday/actions?query=workflow%3ACI) +[![GitHub Discussions](https://img.shields.io/github/discussions/lostisland/faraday?logo=github)](https://github.com/lostisland/faraday/discussions) + + +Faraday is an HTTP client library abstraction layer that provides a common interface over many +adapters (such as Net::HTTP) and embraces the concept of Rack middleware when processing the request/response cycle. +You probably don't want to use Faraday directly in your project, as it will lack an actual client library to perform +requests. Instead, you probably want to have a look at [Awesome Faraday][awesome] for a list of available adapters. + +## Getting Started + +The best starting point is the [Faraday Website][website], with its introduction and explanation. +Need more details? See the [Faraday API Documentation][apidoc] to see how it works internally. + +## Supported Ruby versions + +This library aims to support and is [tested against][actions] the currently officially supported Ruby +implementations. This means that, even without a major release, we could add or drop support for Ruby versions, +following their [EOL](https://endoflife.date/ruby). +Currently that means we support Ruby 2.6+ + +If something doesn't work on one of these Ruby versions, it's a bug. + +This library may inadvertently work (or seem to work) on other Ruby +implementations and versions, however support will only be provided for the versions listed +above. + +If you would like this library to support another Ruby version, you may +volunteer to be a maintainer. Being a maintainer entails making sure all tests +run and pass on that implementation. When something breaks on your +implementation, you will be responsible for providing patches in a timely +fashion. If critical issues for a particular implementation exist at the time +of a major release, support for that Ruby version may be dropped. + +## Contribute + +Do you want to contribute to Faraday? +Open the issues page and check for the `help wanted` label! +But before you start coding, please read our [Contributing Guide][contributing] + +## Copyright +© 2009 - 2022, the [Faraday Team][faraday_team]. Website and branding design by [Elena Lo Piccolo](https://elelopic.design). + +[awesome]: https://github.com/lostisland/awesome-faraday/#adapters +[website]: https://lostisland.github.io/faraday +[faraday_team]: https://lostisland.github.io/faraday/team +[contributing]: https://github.com/lostisland/faraday/blob/master/.github/CONTRIBUTING.md +[apidoc]: https://www.rubydoc.info/github/lostisland/faraday +[actions]: https://github.com/lostisland/faraday/actions +[jruby]: http://jruby.org/ +[rubinius]: http://rubini.us/ +[license]: LICENSE.md diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/Rakefile b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/Rakefile new file mode 100644 index 0000000..cffdd09 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/Rakefile @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require 'rspec/core/rake_task' + +RSpec::Core::RakeTask.new(:spec) + +task default: :spec diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/examples/client_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/examples/client_spec.rb new file mode 100644 index 0000000..e30d86f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/examples/client_spec.rb @@ -0,0 +1,119 @@ +# frozen_string_literal: true + +# Requires Ruby with rspec and faraday gems. +# rspec client_spec.rb + +require 'faraday' +require 'json' + +# Example API client +class Client + def initialize(conn) + @conn = conn + end + + def httpbingo(jname, params: {}) + res = @conn.get("/#{jname}", params) + data = JSON.parse(res.body) + data['origin'] + end + + def foo(params) + res = @conn.post('/foo', JSON.dump(params)) + res.status + end +end + +RSpec.describe Client do + let(:stubs) { Faraday::Adapter::Test::Stubs.new } + let(:conn) { Faraday.new { |b| b.adapter(:test, stubs) } } + let(:client) { Client.new(conn) } + + it 'parses origin' do + stubs.get('/ip') do |env| + # optional: you can inspect the Faraday::Env + expect(env.url.path).to eq('/ip') + [ + 200, + { 'Content-Type': 'application/javascript' }, + '{"origin": "127.0.0.1"}' + ] + end + + # uncomment to trigger stubs.verify_stubbed_calls failure + # stubs.get('/unused') { [404, {}, ''] } + + expect(client.httpbingo('ip')).to eq('127.0.0.1') + stubs.verify_stubbed_calls + end + + it 'handles 404' do + stubs.get('/api') do + [ + 404, + { 'Content-Type': 'application/javascript' }, + '{}' + ] + end + expect(client.httpbingo('api')).to be_nil + stubs.verify_stubbed_calls + end + + it 'handles exception' do + stubs.get('/api') do + raise Faraday::ConnectionFailed + end + + expect { client.httpbingo('api') }.to raise_error(Faraday::ConnectionFailed) + stubs.verify_stubbed_calls + end + + context 'When the test stub is run in strict_mode' do + let(:stubs) { Faraday::Adapter::Test::Stubs.new(strict_mode: true) } + + it 'verifies the all parameter values are identical' do + stubs.get('/api?abc=123') do + [ + 200, + { 'Content-Type': 'application/javascript' }, + '{"origin": "127.0.0.1"}' + ] + end + + # uncomment to raise Stubs::NotFound + # expect(client.httpbingo('api', params: { abc: 123, foo: 'Kappa' })).to eq('127.0.0.1') + expect(client.httpbingo('api', params: { abc: 123 })).to eq('127.0.0.1') + stubs.verify_stubbed_calls + end + end + + context 'When the Faraday connection is configured with FlatParamsEncoder' do + let(:conn) { Faraday.new(request: { params_encoder: Faraday::FlatParamsEncoder }) { |b| b.adapter(:test, stubs) } } + + it 'handles the same multiple URL parameters' do + stubs.get('/api?a=x&a=y&a=z') { [200, { 'Content-Type' => 'application/json' }, '{"origin": "127.0.0.1"}'] } + + # uncomment to raise Stubs::NotFound + # expect(client.httpbingo('api', params: { a: %w[x y] })).to eq('127.0.0.1') + expect(client.httpbingo('api', params: { a: %w[x y z] })).to eq('127.0.0.1') + stubs.verify_stubbed_calls + end + end + + context 'When you want to test the body, you can use a proc as well as string' do + it 'tests with a string' do + stubs.post('/foo', '{"name":"YK"}') { [200, {}, ''] } + + expect(client.foo(name: 'YK')).to eq 200 + stubs.verify_stubbed_calls + end + + it 'tests with a proc' do + check = ->(request_body) { JSON.parse(request_body).slice('name') == { 'name' => 'YK' } } + stubs.post('/foo', check) { [200, {}, ''] } + + expect(client.foo(name: 'YK', created_at: Time.now)).to eq 200 + stubs.verify_stubbed_calls + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/examples/client_test.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/examples/client_test.rb new file mode 100644 index 0000000..3aad957 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/examples/client_test.rb @@ -0,0 +1,144 @@ +# frozen_string_literal: true + +# Requires Ruby with test-unit and faraday gems. +# ruby client_test.rb + +require 'faraday' +require 'json' +require 'test/unit' + +# Example API client +class Client + def initialize(conn) + @conn = conn + end + + def httpbingo(jname, params: {}) + res = @conn.get("/#{jname}", params) + data = JSON.parse(res.body) + data['origin'] + end + + def foo(params) + res = @conn.post('/foo', JSON.dump(params)) + res.status + end +end + +# Example API client test +class ClientTest < Test::Unit::TestCase + def test_httpbingo_name + stubs = Faraday::Adapter::Test::Stubs.new + stubs.get('/api') do |env| + # optional: you can inspect the Faraday::Env + assert_equal '/api', env.url.path + [ + 200, + { 'Content-Type': 'application/javascript' }, + '{"origin": "127.0.0.1"}' + ] + end + + # uncomment to trigger stubs.verify_stubbed_calls failure + # stubs.get('/unused') { [404, {}, ''] } + + cli = client(stubs) + assert_equal '127.0.0.1', cli.httpbingo('api') + stubs.verify_stubbed_calls + end + + def test_httpbingo_not_found + stubs = Faraday::Adapter::Test::Stubs.new + stubs.get('/api') do + [ + 404, + { 'Content-Type': 'application/javascript' }, + '{}' + ] + end + + cli = client(stubs) + assert_nil cli.httpbingo('api') + stubs.verify_stubbed_calls + end + + def test_httpbingo_exception + stubs = Faraday::Adapter::Test::Stubs.new + stubs.get('/api') do + raise Faraday::ConnectionFailed + end + + cli = client(stubs) + assert_raise Faraday::ConnectionFailed do + cli.httpbingo('api') + end + stubs.verify_stubbed_calls + end + + def test_strict_mode + stubs = Faraday::Adapter::Test::Stubs.new(strict_mode: true) + stubs.get('/api?abc=123') do + [ + 200, + { 'Content-Type': 'application/javascript' }, + '{"origin": "127.0.0.1"}' + ] + end + + cli = client(stubs) + assert_equal '127.0.0.1', cli.httpbingo('api', params: { abc: 123 }) + + # uncomment to raise Stubs::NotFound + # assert_equal '127.0.0.1', cli.httpbingo('api', params: { abc: 123, foo: 'Kappa' }) + stubs.verify_stubbed_calls + end + + def test_non_default_params_encoder + stubs = Faraday::Adapter::Test::Stubs.new(strict_mode: true) + stubs.get('/api?a=x&a=y&a=z') do + [ + 200, + { 'Content-Type': 'application/javascript' }, + '{"origin": "127.0.0.1"}' + ] + end + conn = Faraday.new(request: { params_encoder: Faraday::FlatParamsEncoder }) do |builder| + builder.adapter :test, stubs + end + + cli = Client.new(conn) + assert_equal '127.0.0.1', cli.httpbingo('api', params: { a: %w[x y z] }) + + # uncomment to raise Stubs::NotFound + # assert_equal '127.0.0.1', cli.httpbingo('api', params: { a: %w[x y] }) + stubs.verify_stubbed_calls + end + + def test_with_string_body + stubs = Faraday::Adapter::Test::Stubs.new do |stub| + stub.post('/foo', '{"name":"YK"}') { [200, {}, ''] } + end + cli = client(stubs) + assert_equal 200, cli.foo(name: 'YK') + + stubs.verify_stubbed_calls + end + + def test_with_proc_body + stubs = Faraday::Adapter::Test::Stubs.new do |stub| + check = ->(request_body) { JSON.parse(request_body).slice('name') == { 'name' => 'YK' } } + stub.post('/foo', check) { [200, {}, ''] } + end + cli = client(stubs) + assert_equal 200, cli.foo(name: 'YK', created_at: Time.now) + + stubs.verify_stubbed_calls + end + + def client(stubs) + conn = Faraday.new do |builder| + builder.adapter :test, stubs + end + Client.new(conn) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday.rb new file mode 100644 index 0000000..758a08d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday.rb @@ -0,0 +1,157 @@ +# frozen_string_literal: true + +require 'cgi' +require 'date' +require 'set' +require 'forwardable' +require 'faraday/version' +require 'faraday/methods' +require 'faraday/error' +require 'faraday/middleware_registry' +require 'faraday/utils' +require 'faraday/options' +require 'faraday/connection' +require 'faraday/rack_builder' +require 'faraday/parameters' +require 'faraday/middleware' +require 'faraday/adapter' +require 'faraday/request' +require 'faraday/response' +require 'faraday/net_http' +# This is the main namespace for Faraday. +# +# It provides methods to create {Connection} objects, and HTTP-related +# methods to use directly. +# +# @example Helpful class methods for easy usage +# Faraday.get "http://faraday.com" +# +# @example Helpful class method `.new` to create {Connection} objects. +# conn = Faraday.new "http://faraday.com" +# conn.get '/' +# +module Faraday + CONTENT_TYPE = 'Content-Type' + + class << self + # The root path that Faraday is being loaded from. + # + # This is the root from where the libraries are auto-loaded. + # + # @return [String] + attr_accessor :root_path + + # Gets or sets the path that the Faraday libs are loaded from. + # @return [String] + attr_accessor :lib_path + + # @overload default_adapter + # Gets the Symbol key identifying a default Adapter to use + # for the default {Faraday::Connection}. Defaults to `:net_http`. + # @return [Symbol] the default adapter + # @overload default_adapter=(adapter) + # Updates default adapter while resetting {.default_connection}. + # @return [Symbol] the new default_adapter. + attr_reader :default_adapter + + # Option for the default_adapter + # @return [Hash] default_adapter options + attr_accessor :default_adapter_options + + # Documented below, see default_connection + attr_writer :default_connection + + # Tells Faraday to ignore the environment proxy (http_proxy). + # Defaults to `false`. + # @return [Boolean] + attr_accessor :ignore_env_proxy + + # Initializes a new {Connection}. + # + # @param url [String,Hash] The optional String base URL to use as a prefix + # for all requests. Can also be the options Hash. Any of these + # values will be set on every request made, unless overridden + # for a specific request. + # @param options [Hash] + # @option options [String] :url Base URL + # @option options [Hash] :params Hash of unencoded URI query params. + # @option options [Hash] :headers Hash of unencoded HTTP headers. + # @option options [Hash] :request Hash of request options. + # @option options [Hash] :ssl Hash of SSL options. + # @option options [Hash] :proxy Hash of Proxy options. + # @return [Faraday::Connection] + # + # @example With an URL argument + # Faraday.new 'http://faraday.com' + # # => Faraday::Connection to http://faraday.com + # + # @example With an URL argument and an options hash + # Faraday.new 'http://faraday.com', params: { page: 1 } + # # => Faraday::Connection to http://faraday.com?page=1 + # + # @example With everything in an options hash + # Faraday.new url: 'http://faraday.com', + # params: { page: 1 } + # # => Faraday::Connection to http://faraday.com?page=1 + def new(url = nil, options = {}, &block) + options = Utils.deep_merge(default_connection_options, options) + Faraday::Connection.new(url, options, &block) + end + + # Documented elsewhere, see default_adapter reader + def default_adapter=(adapter) + @default_connection = nil + @default_adapter = adapter + end + + def respond_to_missing?(symbol, include_private = false) + default_connection.respond_to?(symbol, include_private) || super + end + + # @overload default_connection + # Gets the default connection used for simple scripts. + # @return [Faraday::Connection] a connection configured with + # the default_adapter. + # @overload default_connection=(connection) + # @param connection [Faraday::Connection] + # Sets the default {Faraday::Connection} for simple scripts that + # access the Faraday constant directly, such as + # Faraday.get "https://faraday.com". + def default_connection + @default_connection ||= Connection.new(default_connection_options) + end + + # Gets the default connection options used when calling {Faraday#new}. + # + # @return [Faraday::ConnectionOptions] + def default_connection_options + @default_connection_options ||= ConnectionOptions.new + end + + # Sets the default options used when calling {Faraday#new}. + # + # @param options [Hash, Faraday::ConnectionOptions] + def default_connection_options=(options) + @default_connection = nil + @default_connection_options = ConnectionOptions.from(options) + end + + private + + # Internal: Proxies method calls on the Faraday constant to + # .default_connection. + def method_missing(name, *args, &block) + if default_connection.respond_to?(name) + default_connection.send(name, *args, &block) + else + super + end + end + end + + self.ignore_env_proxy = false + self.root_path = File.expand_path __dir__ + self.lib_path = File.expand_path 'faraday', __dir__ + self.default_adapter = :net_http + self.default_adapter_options = {} +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/adapter.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/adapter.rb new file mode 100644 index 0000000..f3e6af7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/adapter.rb @@ -0,0 +1,102 @@ +# frozen_string_literal: true + +module Faraday + # Base class for all Faraday adapters. Adapters are + # responsible for fulfilling a Faraday request. + class Adapter + extend MiddlewareRegistry + + CONTENT_LENGTH = 'Content-Length' + + # This module marks an Adapter as supporting parallel requests. + module Parallelism + attr_writer :supports_parallel + + def supports_parallel? + @supports_parallel + end + + def inherited(subclass) + super + subclass.supports_parallel = supports_parallel? + end + end + + extend Parallelism + self.supports_parallel = false + + def initialize(_app = nil, opts = {}, &block) + @app = ->(env) { env.response } + @connection_options = opts + @config_block = block + end + + # Yields or returns an adapter's configured connection. Depends on + # #build_connection being defined on this adapter. + # + # @param env [Faraday::Env, Hash] The env object for a faraday request. + # + # @return The return value of the given block, or the HTTP connection object + # if no block is given. + def connection(env) + conn = build_connection(env) + return conn unless block_given? + + yield conn + end + + # Close any persistent connections. The adapter should still be usable + # after calling close. + def close + # Possible implementation: + # @app.close if @app.respond_to?(:close) + end + + def call(env) + env.clear_body if env.needs_body? + env.response = Response.new + end + + private + + def save_response(env, status, body, headers = nil, reason_phrase = nil, finished: true) + env.status = status + env.body = body + env.reason_phrase = reason_phrase&.to_s&.strip + env.response_headers = Utils::Headers.new.tap do |response_headers| + response_headers.update headers unless headers.nil? + yield(response_headers) if block_given? + end + + env.response.finish(env) unless env.parallel? || !finished + env.response + end + + # Fetches either a read, write, or open timeout setting. Defaults to the + # :timeout value if a more specific one is not given. + # + # @param type [Symbol] Describes which timeout setting to get: :read, + # :write, or :open. + # @param options [Hash] Hash containing Symbol keys like :timeout, + # :read_timeout, :write_timeout, :open_timeout, or + # :timeout + # + # @return [Integer, nil] Timeout duration in seconds, or nil if no timeout + # has been set. + def request_timeout(type, options) + key = TIMEOUT_KEYS.fetch(type) do + msg = "Expected :read, :write, :open. Got #{type.inspect} :(" + raise ArgumentError, msg + end + options[key] || options[:timeout] + end + + TIMEOUT_KEYS = { + read: :read_timeout, + open: :open_timeout, + write: :write_timeout + }.freeze + end +end + +require 'faraday/adapter/test' diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/adapter/test.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/adapter/test.rb new file mode 100644 index 0000000..b8a5b28 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/adapter/test.rb @@ -0,0 +1,298 @@ +# frozen_string_literal: true + +module Faraday + class Adapter + # @example + # test = Faraday::Connection.new do + # use Faraday::Adapter::Test do |stub| + # # Define matcher to match the request + # stub.get '/resource.json' do + # # return static content + # [200, {'Content-Type' => 'application/json'}, 'hi world'] + # end + # + # # response with content generated based on request + # stub.get '/showget' do |env| + # [200, {'Content-Type' => 'text/plain'}, env[:method].to_s] + # end + # + # # A regular expression can be used as matching filter + # stub.get /\A\/items\/(\d+)\z/ do |env, meta| + # # in case regular expression is used, an instance of MatchData + # # can be received + # [200, + # {'Content-Type' => 'text/plain'}, + # "showing item: #{meta[:match_data][1]}" + # ] + # end + # + # # Test the request body is the same as the stubbed body + # stub.post('/bar', 'name=YK&word=call') { [200, {}, ''] } + # + # # You can pass a proc as a stubbed body and check the request body in your way. + # # In this case, the proc should return true or false. + # stub.post('/foo', ->(request_body) do + # JSON.parse(request_body).slice('name') == { 'name' => 'YK' } }) { [200, {}, ''] + # end + # + # # You can set strict_mode to exactly match the stubbed requests. + # stub.strict_mode = true + # end + # end + # + # resp = test.get '/resource.json' + # resp.body # => 'hi world' + # + # resp = test.get '/showget' + # resp.body # => 'get' + # + # resp = test.get '/items/1' + # resp.body # => 'showing item: 1' + # + # resp = test.get '/items/2' + # resp.body # => 'showing item: 2' + # + # resp = test.post '/bar', 'name=YK&word=call' + # resp.status # => 200 + # + # resp = test.post '/foo', JSON.dump(name: 'YK', created_at: Time.now) + # resp.status # => 200 + class Test < Faraday::Adapter + attr_accessor :stubs + + # A stack of Stubs + class Stubs + class NotFound < StandardError + end + + def initialize(strict_mode: false) + # { get: [Stub, Stub] } + @stack = {} + @consumed = {} + @strict_mode = strict_mode + @stubs_mutex = Monitor.new + yield(self) if block_given? + end + + def empty? + @stack.empty? + end + + # @param env [Faraday::Env] + def match(env) + request_method = env[:method] + return false unless @stack.key?(request_method) + + stack = @stack[request_method] + consumed = (@consumed[request_method] ||= []) + + @stubs_mutex.synchronize do + stub, meta = matches?(stack, env) + if stub + removed = stack.delete(stub) + consumed << removed unless removed.nil? + return stub, meta + end + end + matches?(consumed, env) + end + + def get(path, headers = {}, &block) + new_stub(:get, path, headers, &block) + end + + def head(path, headers = {}, &block) + new_stub(:head, path, headers, &block) + end + + def post(path, body = nil, headers = {}, &block) + new_stub(:post, path, headers, body, &block) + end + + def put(path, body = nil, headers = {}, &block) + new_stub(:put, path, headers, body, &block) + end + + def patch(path, body = nil, headers = {}, &block) + new_stub(:patch, path, headers, body, &block) + end + + def delete(path, headers = {}, &block) + new_stub(:delete, path, headers, &block) + end + + def options(path, headers = {}, &block) + new_stub(:options, path, headers, &block) + end + + # Raises an error if any of the stubbed calls have not been made. + def verify_stubbed_calls + failed_stubs = [] + @stack.each do |method, stubs| + next if stubs.empty? + + failed_stubs.concat( + stubs.map do |stub| + "Expected #{method} #{stub}." + end + ) + end + raise failed_stubs.join(' ') unless failed_stubs.empty? + end + + # Set strict_mode. If the value is true, this adapter tries to find matched requests strictly, + # which means that all of a path, parameters, and headers must be the same as an actual request. + def strict_mode=(value) + @strict_mode = value + @stack.each do |_method, stubs| + stubs.each do |stub| + stub.strict_mode = value + end + end + end + + protected + + def new_stub(request_method, path, headers = {}, body = nil, &block) + normalized_path, host = + if path.is_a?(Regexp) + path + else + [ + Faraday::Utils.normalize_path(path), + Faraday::Utils.URI(path).host + ] + end + path, query = normalized_path.respond_to?(:split) ? normalized_path.split('?') : normalized_path + headers = Utils::Headers.new(headers) + + stub = Stub.new(host, path, query, headers, body, @strict_mode, block) + (@stack[request_method] ||= []) << stub + end + + # @param stack [Hash] + # @param env [Faraday::Env] + def matches?(stack, env) + stack.each do |stub| + match_result, meta = stub.matches?(env) + return stub, meta if match_result + end + nil + end + end + + # Stub request + class Stub < Struct.new(:host, :path, :query, :headers, :body, :strict_mode, :block) # rubocop:disable Style/StructInheritance + # @param env [Faraday::Env] + def matches?(env) + request_host = env[:url].host + request_path = Faraday::Utils.normalize_path(env[:url].path) + request_headers = env.request_headers + request_body = env[:body] + + # meta is a hash used as carrier + # that will be yielded to consumer block + meta = {} + [(host.nil? || host == request_host) && + path_match?(request_path, meta) && + params_match?(env) && + body_match?(request_body) && + headers_match?(request_headers), meta] + end + + def path_match?(request_path, meta) + if path.is_a?(Regexp) + !!(meta[:match_data] = path.match(request_path)) + else + path == request_path + end + end + + # @param env [Faraday::Env] + def params_match?(env) + request_params = env[:params] + params = env.params_encoder.decode(query) || {} + + if strict_mode + return Set.new(params) == Set.new(request_params) + end + + params.keys.all? do |key| + request_params[key] == params[key] + end + end + + def headers_match?(request_headers) + if strict_mode + headers_with_user_agent = headers.dup.tap do |hs| + # NOTE: Set User-Agent in case it's not set when creating Stubs. + # Users would not want to set Faraday's User-Agent explicitly. + hs[:user_agent] ||= Connection::USER_AGENT + end + return Set.new(headers_with_user_agent) == Set.new(request_headers) + end + + headers.keys.all? do |key| + request_headers[key] == headers[key] + end + end + + def body_match?(request_body) + return true if body.to_s.size.zero? + + case body + when Proc + body.call(request_body) + else + request_body == body + end + end + + def to_s + "#{path} #{body}" + end + end + + def initialize(app, stubs = nil, &block) + super(app) + @stubs = stubs || Stubs.new + configure(&block) if block + end + + def configure + yield(stubs) + end + + # @param env [Faraday::Env] + def call(env) + super + + env.request.params_encoder ||= Faraday::Utils.default_params_encoder + env[:params] = env.params_encoder.decode(env[:url].query) || {} + stub, meta = stubs.match(env) + + unless stub + raise Stubs::NotFound, "no stubbed request for #{env[:method]} " \ + "#{env[:url]} #{env[:body]}" + end + + block_arity = stub.block.arity + status, headers, body = + if block_arity >= 0 + stub.block.call(*[env, meta].take(block_arity)) + else + stub.block.call(env, meta) + end + + # We need to explicitly pass `reason_phrase = nil` here to avoid keyword args conflicts. + # See https://github.com/lostisland/faraday/issues/1444 + # TODO: remove `nil` explicit reason_phrase once Ruby 3.0 becomes minimum req. version + save_response(env, status, body, headers, nil) + + @app.call(env) + end + end + end +end + +Faraday::Adapter.register_middleware(test: Faraday::Adapter::Test) diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/adapter_registry.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/adapter_registry.rb new file mode 100644 index 0000000..1cd1e7e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/adapter_registry.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require 'monitor' + +module Faraday + # AdapterRegistry registers adapter class names so they can be looked up by a + # String or Symbol name. + class AdapterRegistry + def initialize + @lock = Monitor.new + @constants = {} + end + + def get(name) + klass = @lock.synchronize do + @constants[name] + end + return klass if klass + + Object.const_get(name).tap { |c| set(c, name) } + end + + def set(klass, name = nil) + name ||= klass.to_s + @lock.synchronize do + @constants[name] = klass + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/connection.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/connection.rb new file mode 100644 index 0000000..b86245d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/connection.rb @@ -0,0 +1,561 @@ +# frozen_string_literal: true + +module Faraday + # Connection objects manage the default properties and the middleware + # stack for fulfilling an HTTP request. + # + # @example + # + # conn = Faraday::Connection.new 'http://httpbingo.org' + # + # # GET http://httpbingo.org/nigiri + # conn.get 'nigiri' + # # => # + # + class Connection + # A Set of allowed HTTP verbs. + METHODS = Set.new %i[get post put delete head patch options trace] + USER_AGENT = "Faraday v#{VERSION}" + + # @return [Hash] URI query unencoded key/value pairs. + attr_reader :params + + # @return [Hash] unencoded HTTP header key/value pairs. + attr_reader :headers + + # @return [String] a URI with the prefix used for all requests from this + # Connection. This includes a default host name, scheme, port, and path. + attr_reader :url_prefix + + # @return [Faraday::RackBuilder] Builder for this Connection. + attr_reader :builder + + # @return [Hash] SSL options. + attr_reader :ssl + + # @return [Object] the parallel manager for this Connection. + attr_reader :parallel_manager + + # Sets the default parallel manager for this connection. + attr_writer :default_parallel_manager + + # @return [Hash] proxy options. + attr_reader :proxy + + # Initializes a new Faraday::Connection. + # + # @param url [URI, String] URI or String base URL to use as a prefix for all + # requests (optional). + # @param options [Hash, Faraday::ConnectionOptions] + # @option options [URI, String] :url ('http:/') URI or String base URL + # @option options [Hash String>] :params URI query unencoded + # key/value pairs. + # @option options [Hash String>] :headers Hash of unencoded HTTP + # header key/value pairs. + # @option options [Hash] :request Hash of request options. + # @option options [Hash] :ssl Hash of SSL options. + # @option options [Hash, URI, String] :proxy proxy options, either as a URL + # or as a Hash + # @option options [URI, String] :proxy[:uri] + # @option options [String] :proxy[:user] + # @option options [String] :proxy[:password] + # @yield [self] after all setup has been done + def initialize(url = nil, options = nil) + options = ConnectionOptions.from(options) + + if url.is_a?(Hash) || url.is_a?(ConnectionOptions) + options = Utils.deep_merge(options, url) + url = options.url + end + + @parallel_manager = nil + @headers = Utils::Headers.new + @params = Utils::ParamsHash.new + @options = options.request + @ssl = options.ssl + @default_parallel_manager = options.parallel_manager + @manual_proxy = nil + + @builder = options.builder || begin + # pass an empty block to Builder so it doesn't assume default middleware + options.new_builder(block_given? ? proc { |b| } : nil) + end + + self.url_prefix = url || 'http:/' + + @params.update(options.params) if options.params + @headers.update(options.headers) if options.headers + + initialize_proxy(url, options) + + yield(self) if block_given? + + @headers[:user_agent] ||= USER_AGENT + end + + def initialize_proxy(url, options) + @manual_proxy = !!options.proxy + @proxy = + if options.proxy + ProxyOptions.from(options.proxy) + else + proxy_from_env(url) + end + end + + # Sets the Hash of URI query unencoded key/value pairs. + # @param hash [Hash] + def params=(hash) + @params.replace hash + end + + # Sets the Hash of unencoded HTTP header key/value pairs. + # @param hash [Hash] + def headers=(hash) + @headers.replace hash + end + + extend Forwardable + + def_delegators :builder, :use, :request, :response, :adapter, :app + + # Closes the underlying resources and/or connections. In the case of + # persistent connections, this closes all currently open connections + # but does not prevent new connections from being made. + def close + app.close + end + + # @!method get(url = nil, params = nil, headers = nil) + # Makes a GET HTTP request without a body. + # @!scope class + # + # @param url [String, URI, nil] The optional String base URL to use as a prefix for + # all requests. Can also be the options Hash. + # @param params [Hash, nil] Hash of URI query unencoded key/value pairs. + # @param headers [Hash, nil] unencoded HTTP header key/value pairs. + # + # @example + # conn.get '/items', { page: 1 }, :accept => 'application/json' + # + # # ElasticSearch example sending a body with GET. + # conn.get '/twitter/tweet/_search' do |req| + # req.headers[:content_type] = 'application/json' + # req.params[:routing] = 'kimchy' + # req.body = JSON.generate(query: {...}) + # end + # + # @yield [Faraday::Request] for further request customizations + # @return [Faraday::Response] + + # @!method head(url = nil, params = nil, headers = nil) + # Makes a HEAD HTTP request without a body. + # @!scope class + # + # @param url [String, URI, nil] The optional String base URL to use as a prefix for + # all requests. Can also be the options Hash. + # @param params [Hash, nil] Hash of URI query unencoded key/value pairs. + # @param headers [Hash, nil] unencoded HTTP header key/value pairs. + # + # @example + # conn.head '/items/1' + # + # @yield [Faraday::Request] for further request customizations + # @return [Faraday::Response] + + # @!method delete(url = nil, params = nil, headers = nil) + # Makes a DELETE HTTP request without a body. + # @!scope class + # + # @param url [String, URI, nil] The optional String base URL to use as a prefix for + # all requests. Can also be the options Hash. + # @param params [Hash, nil] Hash of URI query unencoded key/value pairs. + # @param headers [Hash, nil] unencoded HTTP header key/value pairs. + # + # @example + # conn.delete '/items/1' + # + # @yield [Faraday::Request] for further request customizations + # @return [Faraday::Response] + + # @!method trace(url = nil, params = nil, headers = nil) + # Makes a TRACE HTTP request without a body. + # @!scope class + # + # @param url [String, URI, nil] The optional String base URL to use as a prefix for + # all requests. Can also be the options Hash. + # @param params [Hash, nil] Hash of URI query unencoded key/value pairs. + # @param headers [Hash, nil] unencoded HTTP header key/value pairs. + # + # @example + # conn.connect '/items/1' + # + # @yield [Faraday::Request] for further request customizations + # @return [Faraday::Response] + + # @!visibility private + METHODS_WITH_QUERY.each do |method| + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{method}(url = nil, params = nil, headers = nil) + run_request(:#{method}, url, nil, headers) do |request| + request.params.update(params) if params + yield request if block_given? + end + end + RUBY + end + + # @overload options() + # Returns current Connection options. + # + # @overload options(url, params = nil, headers = nil) + # Makes an OPTIONS HTTP request to the given URL. + # @param url [String, URI, nil] String base URL to sue as a prefix for all requests. + # @param params [Hash, nil] Hash of URI query unencoded key/value pairs. + # @param headers [Hash, nil] unencoded HTTP header key/value pairs. + # + # @example + # conn.options '/items/1' + # + # @yield [Faraday::Request] for further request customizations + # @return [Faraday::Response] + def options(*args) + return @options if args.size.zero? + + url, params, headers = *args + run_request(:options, url, nil, headers) do |request| + request.params.update(params) if params + yield request if block_given? + end + end + + # @!method post(url = nil, body = nil, headers = nil) + # Makes a POST HTTP request with a body. + # @!scope class + # + # @param url [String, URI, nil] The optional String base URL to use as a prefix for + # all requests. Can also be the options Hash. + # @param body [String, nil] body for the request. + # @param headers [Hash, nil] unencoded HTTP header key/value pairs. + # + # @example + # conn.post '/items', data, content_type: 'application/json' + # + # # Simple ElasticSearch indexing sample. + # conn.post '/twitter/tweet' do |req| + # req.headers[:content_type] = 'application/json' + # req.params[:routing] = 'kimchy' + # req.body = JSON.generate(user: 'kimchy', ...) + # end + # + # @yield [Faraday::Request] for further request customizations + # @return [Faraday::Response] + + # @!method put(url = nil, body = nil, headers = nil) + # Makes a PUT HTTP request with a body. + # @!scope class + # + # @param url [String, URI, nil] The optional String base URL to use as a prefix for + # all requests. Can also be the options Hash. + # @param body [String, nil] body for the request. + # @param headers [Hash, nil] unencoded HTTP header key/value pairs. + # + # @example + # # TODO: Make it a PUT example + # conn.post '/items', data, content_type: 'application/json' + # + # # Simple ElasticSearch indexing sample. + # conn.post '/twitter/tweet' do |req| + # req.headers[:content_type] = 'application/json' + # req.params[:routing] = 'kimchy' + # req.body = JSON.generate(user: 'kimchy', ...) + # end + # + # @yield [Faraday::Request] for further request customizations + # @return [Faraday::Response] + + # @!visibility private + METHODS_WITH_BODY.each do |method| + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{method}(url = nil, body = nil, headers = nil, &block) + run_request(:#{method}, url, body, headers, &block) + end + RUBY + end + + # Check if the adapter is parallel-capable. + # + # @yield if the adapter isn't parallel-capable, or if no adapter is set yet. + # + # @return [Object, nil] a parallel manager or nil if yielded + # @api private + def default_parallel_manager + @default_parallel_manager ||= begin + adapter = @builder.adapter.klass if @builder.adapter + + if support_parallel?(adapter) + adapter.setup_parallel_manager + elsif block_given? + yield + end + end + end + + # Determine if this Faraday::Connection can make parallel requests. + # + # @return [Boolean] + def in_parallel? + !!@parallel_manager + end + + # Sets up the parallel manager to make a set of requests. + # + # @param manager [Object] The parallel manager that this Connection's + # Adapter uses. + # + # @yield a block to execute multiple requests. + # @return [void] + def in_parallel(manager = nil) + @parallel_manager = manager || default_parallel_manager do + warn 'Warning: `in_parallel` called but no parallel-capable adapter ' \ + 'on Faraday stack' + warn caller[2, 10].join("\n") + nil + end + yield + @parallel_manager&.run + ensure + @parallel_manager = nil + end + + # Sets the Hash proxy options. + # + # @param new_value [Object] + def proxy=(new_value) + @manual_proxy = true + @proxy = new_value ? ProxyOptions.from(new_value) : nil + end + + def_delegators :url_prefix, :scheme, :scheme=, :host, :host=, :port, :port= + def_delegator :url_prefix, :path, :path_prefix + + # Parses the given URL with URI and stores the individual + # components in this connection. These components serve as defaults for + # requests made by this connection. + # + # @param url [String, URI] + # @param encoder [Object] + # + # @example + # + # conn = Faraday::Connection.new { ... } + # conn.url_prefix = "https://httpbingo.org/api" + # conn.scheme # => https + # conn.path_prefix # => "/api" + # + # conn.get("nigiri?page=2") # accesses https://httpbingo.org/api/nigiri + def url_prefix=(url, encoder = nil) + uri = @url_prefix = Utils.URI(url) + self.path_prefix = uri.path + + params.merge_query(uri.query, encoder) + uri.query = nil + + with_uri_credentials(uri) do |user, password| + set_basic_auth(user, password) + uri.user = uri.password = nil + end + + @proxy = proxy_from_env(url) unless @manual_proxy + end + + def set_basic_auth(user, password) + header = Faraday::Utils.basic_header_from(user, password) + headers[Faraday::Request::Authorization::KEY] = header + end + + # Sets the path prefix and ensures that it always has a leading + # slash. + # + # @param value [String] + # + # @return [String] the new path prefix + def path_prefix=(value) + url_prefix.path = if value + value = "/#{value}" unless value[0, 1] == '/' + value + end + end + + # Takes a relative url for a request and combines it with the defaults + # set on the connection instance. + # + # @param url [String, URI, nil] + # @param extra_params [Hash] + # + # @example + # conn = Faraday::Connection.new { ... } + # conn.url_prefix = "https://httpbingo.org/api?token=abc" + # conn.scheme # => https + # conn.path_prefix # => "/api" + # + # conn.build_url("nigiri?page=2") + # # => https://httpbingo.org/api/nigiri?token=abc&page=2 + # + # conn.build_url("nigiri", page: 2) + # # => https://httpbingo.org/api/nigiri?token=abc&page=2 + # + def build_url(url = nil, extra_params = nil) + uri = build_exclusive_url(url) + + query_values = params.dup.merge_query(uri.query, options.params_encoder) + query_values.update(extra_params) if extra_params + uri.query = + if query_values.empty? + nil + else + query_values.to_query(options.params_encoder) + end + + uri + end + + # Builds and runs the Faraday::Request. + # + # @param method [Symbol] HTTP method. + # @param url [String, URI, nil] String or URI to access. + # @param body [String, nil] The request body that will eventually be converted to + # a string. + # @param headers [Hash, nil] unencoded HTTP header key/value pairs. + # + # @return [Faraday::Response] + def run_request(method, url, body, headers) + unless METHODS.include?(method) + raise ArgumentError, "unknown http method: #{method}" + end + + request = build_request(method) do |req| + req.options.proxy = proxy_for_request(url) + req.url(url) if url + req.headers.update(headers) if headers + req.body = body if body + yield(req) if block_given? + end + + builder.build_response(self, request) + end + + # Creates and configures the request object. + # + # @param method [Symbol] + # + # @yield [Faraday::Request] if block given + # @return [Faraday::Request] + def build_request(method) + Request.create(method) do |req| + req.params = params.dup + req.headers = headers.dup + req.options = options.dup + yield(req) if block_given? + end + end + + # Build an absolute URL based on url_prefix. + # + # @param url [String, URI, nil] + # @param params [Faraday::Utils::ParamsHash] A Faraday::Utils::ParamsHash to + # replace the query values + # of the resulting url (default: nil). + # + # @return [URI] + def build_exclusive_url(url = nil, params = nil, params_encoder = nil) + url = nil if url.respond_to?(:empty?) && url.empty? + base = url_prefix.dup + if url && base.path && base.path !~ %r{/$} + base.path = "#{base.path}/" # ensure trailing slash + end + url = url.to_s.gsub(':', '%3A') if url && URI.parse(url.to_s).opaque + uri = url ? base + url : base + if params + uri.query = params.to_query(params_encoder || options.params_encoder) + end + uri.query = nil if uri.query && uri.query.empty? + uri + end + + # Creates a duplicate of this Faraday::Connection. + # + # @api private + # + # @return [Faraday::Connection] + def dup + self.class.new(build_exclusive_url, + headers: headers.dup, + params: params.dup, + builder: builder.dup, + ssl: ssl.dup, + request: options.dup) + end + + # Yields username and password extracted from a URI if they both exist. + # + # @param uri [URI] + # @yield [username, password] any username and password + # @yieldparam username [String] any username from URI + # @yieldparam password [String] any password from URI + # @return [void] + # @api private + def with_uri_credentials(uri) + return unless uri.user && uri.password + + yield(Utils.unescape(uri.user), Utils.unescape(uri.password)) + end + + def proxy_from_env(url) + return if Faraday.ignore_env_proxy + + uri = nil + if URI.parse('').respond_to?(:find_proxy) + case url + when String + uri = Utils.URI(url) + uri = if uri.host.nil? + find_default_proxy + else + URI.parse("#{uri.scheme}://#{uri.host}").find_proxy + end + when URI + uri = url.find_proxy + when nil + uri = find_default_proxy + end + else + warn 'no_proxy is unsupported' if ENV['no_proxy'] || ENV['NO_PROXY'] + uri = find_default_proxy + end + ProxyOptions.from(uri) if uri + end + + def find_default_proxy + uri = ENV.fetch('http_proxy', nil) + return unless uri && !uri.empty? + + uri = "http://#{uri}" unless uri.match?(/^http/i) + uri + end + + def proxy_for_request(url) + return proxy if @manual_proxy + + if url && Utils.URI(url).absolute? + proxy_from_env(url) + else + proxy + end + end + + def support_parallel?(adapter) + adapter.respond_to?(:supports_parallel?) && adapter&.supports_parallel? + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/encoders/flat_params_encoder.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/encoders/flat_params_encoder.rb new file mode 100644 index 0000000..bc10c8b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/encoders/flat_params_encoder.rb @@ -0,0 +1,105 @@ +# frozen_string_literal: true + +module Faraday + # FlatParamsEncoder manages URI params as a flat hash. Any Array values repeat + # the parameter multiple times. + module FlatParamsEncoder + class << self + extend Forwardable + def_delegators :'Faraday::Utils', :escape, :unescape + end + + # Encode converts the given param into a URI querystring. Keys and values + # will converted to strings and appropriately escaped for the URI. + # + # @param params [Hash] query arguments to convert. + # + # @example + # + # encode({a: %w[one two three], b: true, c: "C"}) + # # => 'a=one&a=two&a=three&b=true&c=C' + # + # @return [String] the URI querystring (without the leading '?') + def self.encode(params) + return nil if params.nil? + + unless params.is_a?(Array) + unless params.respond_to?(:to_hash) + raise TypeError, + "Can't convert #{params.class} into Hash." + end + params = params.to_hash + params = params.map do |key, value| + key = key.to_s if key.is_a?(Symbol) + [key, value] + end + + # Only to be used for non-Array inputs. Arrays should preserve order. + params.sort! if @sort_params + end + + # The params have form [['key1', 'value1'], ['key2', 'value2']]. + buffer = +'' + params.each do |key, value| + encoded_key = escape(key) + if value.nil? + buffer << "#{encoded_key}&" + elsif value.is_a?(Array) + if value.empty? + buffer << "#{encoded_key}=&" + else + value.each do |sub_value| + encoded_value = escape(sub_value) + buffer << "#{encoded_key}=#{encoded_value}&" + end + end + else + encoded_value = escape(value) + buffer << "#{encoded_key}=#{encoded_value}&" + end + end + buffer.chop + end + + # Decode converts the given URI querystring into a hash. + # + # @param query [String] query arguments to parse. + # + # @example + # + # decode('a=one&a=two&a=three&b=true&c=C') + # # => {"a"=>["one", "two", "three"], "b"=>"true", "c"=>"C"} + # + # @return [Hash] parsed keys and value strings from the querystring. + def self.decode(query) + return nil if query.nil? + + empty_accumulator = {} + + split_query = (query.split('&').map do |pair| + pair.split('=', 2) if pair && !pair.empty? + end).compact + split_query.each_with_object(empty_accumulator.dup) do |pair, accu| + pair[0] = unescape(pair[0]) + pair[1] = true if pair[1].nil? + if pair[1].respond_to?(:to_str) + pair[1] = unescape(pair[1].to_str.tr('+', ' ')) + end + if accu[pair[0]].is_a?(Array) + accu[pair[0]] << pair[1] + elsif accu[pair[0]] + accu[pair[0]] = [accu[pair[0]], pair[1]] + else + accu[pair[0]] = pair[1] + end + end + end + + class << self + attr_accessor :sort_params + end + + # Useful default for OAuth and caching. + @sort_params = true + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/encoders/nested_params_encoder.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/encoders/nested_params_encoder.rb new file mode 100644 index 0000000..afc940a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/encoders/nested_params_encoder.rb @@ -0,0 +1,183 @@ +# frozen_string_literal: true + +module Faraday + # Sub-module for encoding parameters into query-string. + module EncodeMethods + # @param params [nil, Array, #to_hash] parameters to be encoded + # + # @return [String] the encoded params + # + # @raise [TypeError] if params can not be converted to a Hash + def encode(params) + return nil if params.nil? + + unless params.is_a?(Array) + unless params.respond_to?(:to_hash) + raise TypeError, "Can't convert #{params.class} into Hash." + end + + params = params.to_hash + params = params.map do |key, value| + key = key.to_s if key.is_a?(Symbol) + [key, value] + end + + # Only to be used for non-Array inputs. Arrays should preserve order. + params.sort! if @sort_params + end + + # The params have form [['key1', 'value1'], ['key2', 'value2']]. + buffer = +'' + params.each do |parent, value| + encoded_parent = escape(parent) + buffer << "#{encode_pair(encoded_parent, value)}&" + end + buffer.chop + end + + protected + + def encode_pair(parent, value) + if value.is_a?(Hash) + encode_hash(parent, value) + elsif value.is_a?(Array) + encode_array(parent, value) + elsif value.nil? + parent + else + encoded_value = escape(value) + "#{parent}=#{encoded_value}" + end + end + + def encode_hash(parent, value) + value = value.map { |key, val| [escape(key), val] }.sort + + buffer = +'' + value.each do |key, val| + new_parent = "#{parent}%5B#{key}%5D" + buffer << "#{encode_pair(new_parent, val)}&" + end + buffer.chop + end + + def encode_array(parent, value) + return "#{parent}%5B%5D" if value.empty? + + buffer = +'' + value.each_with_index do |val, index| + new_parent = if @array_indices + "#{parent}%5B#{index}%5D" + else + "#{parent}%5B%5D" + end + buffer << "#{encode_pair(new_parent, val)}&" + end + buffer.chop + end + end + + # Sub-module for decoding query-string into parameters. + module DecodeMethods + # @param query [nil, String] + # + # @return [Array] the decoded params + # + # @raise [TypeError] if the nesting is incorrect + def decode(query) + return nil if query.nil? + + params = {} + query.split('&').each do |pair| + next if pair.empty? + + key, value = pair.split('=', 2) + key = unescape(key) + value = unescape(value.tr('+', ' ')) if value + decode_pair(key, value, params) + end + + dehash(params, 0) + end + + protected + + SUBKEYS_REGEX = /[^\[\]]+(?:\]?\[\])?/.freeze + + def decode_pair(key, value, context) + subkeys = key.scan(SUBKEYS_REGEX) + subkeys.each_with_index do |subkey, i| + is_array = subkey =~ /[\[\]]+\Z/ + subkey = Regexp.last_match.pre_match if is_array + last_subkey = i == subkeys.length - 1 + + context = prepare_context(context, subkey, is_array, last_subkey) + add_to_context(is_array, context, value, subkey) if last_subkey + end + end + + def prepare_context(context, subkey, is_array, last_subkey) + if !last_subkey || is_array + context = new_context(subkey, is_array, context) + end + if context.is_a?(Array) && !is_array + context = match_context(context, subkey) + end + context + end + + def new_context(subkey, is_array, context) + value_type = is_array ? Array : Hash + if context[subkey] && !context[subkey].is_a?(value_type) + raise TypeError, "expected #{value_type.name} " \ + "(got #{context[subkey].class.name}) for param `#{subkey}'" + end + + context[subkey] ||= value_type.new + end + + def match_context(context, subkey) + context << {} if !context.last.is_a?(Hash) || context.last.key?(subkey) + context.last + end + + def add_to_context(is_array, context, value, subkey) + is_array ? context << value : context[subkey] = value + end + + # Internal: convert a nested hash with purely numeric keys into an array. + # FIXME: this is not compatible with Rack::Utils.parse_nested_query + # @!visibility private + def dehash(hash, depth) + hash.each do |key, value| + hash[key] = dehash(value, depth + 1) if value.is_a?(Hash) + end + + if depth.positive? && !hash.empty? && hash.keys.all? { |k| k =~ /^\d+$/ } + hash.sort.map(&:last) + else + hash + end + end + end + + # This is the default encoder for Faraday requests. + # Using this encoder, parameters will be encoded respecting their structure, + # so you can send objects such as Arrays or Hashes as parameters + # for your requests. + module NestedParamsEncoder + class << self + attr_accessor :sort_params, :array_indices + + extend Forwardable + def_delegators :'Faraday::Utils', :escape, :unescape + end + + # Useful default for OAuth and caching. + @sort_params = true + @array_indices = false + + extend EncodeMethods + extend DecodeMethods + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/error.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/error.rb new file mode 100644 index 0000000..2f0c76c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/error.rb @@ -0,0 +1,147 @@ +# frozen_string_literal: true + +# Faraday namespace. +module Faraday + # Faraday error base class. + class Error < StandardError + attr_reader :response, :wrapped_exception + + def initialize(exc = nil, response = nil) + @wrapped_exception = nil unless defined?(@wrapped_exception) + @response = nil unless defined?(@response) + super(exc_msg_and_response!(exc, response)) + end + + def backtrace + if @wrapped_exception + @wrapped_exception.backtrace + else + super + end + end + + def inspect + inner = +'' + inner << " wrapped=#{@wrapped_exception.inspect}" if @wrapped_exception + inner << " response=#{@response.inspect}" if @response + inner << " #{super}" if inner.empty? + %(#<#{self.class}#{inner}>) + end + + def response_status + @response[:status] if @response + end + + def response_headers + @response[:headers] if @response + end + + def response_body + @response[:body] if @response + end + + protected + + # Pulls out potential parent exception and response hash, storing them in + # instance variables. + # exc - Either an Exception, a string message, or a response hash. + # response - Hash + # :status - Optional integer HTTP response status + # :headers - String key/value hash of HTTP response header + # values. + # :body - Optional string HTTP response body. + # :request - Hash + # :method - Symbol with the request HTTP method. + # :url - URI object with the url requested. + # :url_path - String with the url path requested. + # :params - String key/value hash of query params + # present in the request. + # :headers - String key/value hash of HTTP request + # header values. + # :body - String HTTP request body. + # + # If a subclass has to call this, then it should pass a string message + # to `super`. See NilStatusError. + def exc_msg_and_response!(exc, response = nil) + if @response.nil? && @wrapped_exception.nil? + @wrapped_exception, msg, @response = exc_msg_and_response(exc, response) + return msg + end + + exc.to_s + end + + # Pulls out potential parent exception and response hash. + def exc_msg_and_response(exc, response = nil) + return [exc, exc.message, response] if exc.respond_to?(:backtrace) + + return [nil, "the server responded with status #{exc[:status]}", exc] \ + if exc.respond_to?(:each_key) + + [nil, exc.to_s, response] + end + end + + # Faraday client error class. Represents 4xx status responses. + class ClientError < Error + end + + # Raised by Faraday::Response::RaiseError in case of a 400 response. + class BadRequestError < ClientError + end + + # Raised by Faraday::Response::RaiseError in case of a 401 response. + class UnauthorizedError < ClientError + end + + # Raised by Faraday::Response::RaiseError in case of a 403 response. + class ForbiddenError < ClientError + end + + # Raised by Faraday::Response::RaiseError in case of a 404 response. + class ResourceNotFound < ClientError + end + + # Raised by Faraday::Response::RaiseError in case of a 407 response. + class ProxyAuthError < ClientError + end + + # Raised by Faraday::Response::RaiseError in case of a 409 response. + class ConflictError < ClientError + end + + # Raised by Faraday::Response::RaiseError in case of a 422 response. + class UnprocessableEntityError < ClientError + end + + # Faraday server error class. Represents 5xx status responses. + class ServerError < Error + end + + # A unified client error for timeouts. + class TimeoutError < ServerError + def initialize(exc = 'timeout', response = nil) + super(exc, response) + end + end + + # Raised by Faraday::Response::RaiseError in case of a nil status in response. + class NilStatusError < ServerError + def initialize(exc, response = nil) + exc_msg_and_response!(exc, response) + super('http status could not be derived from the server response') + end + end + + # A unified error for failed connections. + class ConnectionFailed < Error + end + + # A unified client error for SSL errors. + class SSLError < Error + end + + # Raised by middlewares that parse the response, like the JSON response middleware. + class ParsingError < Error + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/logging/formatter.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/logging/formatter.rb new file mode 100644 index 0000000..863cdc0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/logging/formatter.rb @@ -0,0 +1,122 @@ +# frozen_string_literal: true + +require 'pp' # rubocop:disable Lint/RedundantRequireStatement + +module Faraday + module Logging + # Serves as an integration point to customize logging + class Formatter + extend Forwardable + + DEFAULT_OPTIONS = { headers: true, bodies: false, errors: false, + log_level: :info }.freeze + + def initialize(logger:, options:) + @logger = logger + @filter = [] + @options = DEFAULT_OPTIONS.merge(options) + end + + def_delegators :@logger, :debug, :info, :warn, :error, :fatal + + def request(env) + request_log = proc do + "#{env.method.upcase} #{apply_filters(env.url.to_s)}" + end + public_send(log_level, 'request', &request_log) + + log_headers('request', env.request_headers) if log_headers?(:request) + log_body('request', env[:body]) if env[:body] && log_body?(:request) + end + + def response(env) + status = proc { "Status #{env.status}" } + public_send(log_level, 'response', &status) + + log_headers('response', env.response_headers) if log_headers?(:response) + log_body('response', env[:body]) if env[:body] && log_body?(:response) + end + + def error(error) + return unless log_errors? + + error_log = proc { error.full_message } + public_send(log_level, 'error', &error_log) + + log_headers('error', error.response_headers) if error.respond_to?(:response_headers) && log_headers?(:error) + return unless error.respond_to?(:response_body) && error.response_body && log_body?(:error) + + log_body('error', error.response_body) + end + + def filter(filter_word, filter_replacement) + @filter.push([filter_word, filter_replacement]) + end + + private + + def dump_headers(headers) + headers.map { |k, v| "#{k}: #{v.inspect}" }.join("\n") + end + + def dump_body(body) + if body.respond_to?(:to_str) + body.to_str + else + pretty_inspect(body) + end + end + + def pretty_inspect(body) + body.pretty_inspect + end + + def log_headers?(type) + case @options[:headers] + when Hash + @options[:headers][type] + else + @options[:headers] + end + end + + def log_body?(type) + case @options[:bodies] + when Hash + @options[:bodies][type] + else + @options[:bodies] + end + end + + def log_errors? + @options[:errors] + end + + def apply_filters(output) + @filter.each do |pattern, replacement| + output = output.to_s.gsub(pattern, replacement) + end + output + end + + def log_level + unless %i[debug info warn error fatal].include?(@options[:log_level]) + return :info + end + + @options[:log_level] + end + + def log_headers(type, headers) + headers_log = proc { apply_filters(dump_headers(headers)) } + public_send(log_level, type, &headers_log) + end + + def log_body(type, body) + body_log = proc { apply_filters(dump_body(body)) } + public_send(log_level, type, &body_log) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/methods.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/methods.rb new file mode 100644 index 0000000..53e3903 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/methods.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +module Faraday + METHODS_WITH_QUERY = %w[get head delete trace].freeze + METHODS_WITH_BODY = %w[post put patch].freeze +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/middleware.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/middleware.rb new file mode 100644 index 0000000..0631ca2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/middleware.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Faraday + # Middleware is the basic base class of any Faraday middleware. + class Middleware + extend MiddlewareRegistry + + attr_reader :app, :options + + def initialize(app = nil, options = {}) + @app = app + @options = options + end + + def call(env) + on_request(env) if respond_to?(:on_request) + app.call(env).on_complete do |environment| + on_complete(environment) if respond_to?(:on_complete) + end + rescue StandardError => e + on_error(e) if respond_to?(:on_error) + raise + end + + def close + if app.respond_to?(:close) + app.close + else + warn "#{app} does not implement \#close!" + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/middleware_registry.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/middleware_registry.rb new file mode 100644 index 0000000..fc70e2b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/middleware_registry.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +require 'monitor' + +module Faraday + # Adds the ability for other modules to register and lookup + # middleware classes. + module MiddlewareRegistry + def registered_middleware + @registered_middleware ||= {} + end + + # Register middleware class(es) on the current module. + # + # @param mappings [Hash] Middleware mappings from a lookup symbol to a middleware class. + # @return [void] + # + # @example Lookup by a constant + # + # module Faraday + # class Whatever < Middleware + # # Middleware looked up by :foo returns Faraday::Whatever::Foo. + # register_middleware(foo: Whatever) + # end + # end + def register_middleware(**mappings) + middleware_mutex do + registered_middleware.update(mappings) + end + end + + # Unregister a previously registered middleware class. + # + # @param key [Symbol] key for the registered middleware. + def unregister_middleware(key) + registered_middleware.delete(key) + end + + # Lookup middleware class with a registered Symbol shortcut. + # + # @param key [Symbol] key for the registered middleware. + # @return [Class] a middleware Class. + # @raise [Faraday::Error] if given key is not registered + # + # @example + # + # module Faraday + # class Whatever < Middleware + # register_middleware(foo: Whatever) + # end + # end + # + # Faraday::Middleware.lookup_middleware(:foo) + # # => Faraday::Whatever + def lookup_middleware(key) + load_middleware(key) || + raise(Faraday::Error, "#{key.inspect} is not registered on #{self}") + end + + private + + def middleware_mutex(&block) + @middleware_mutex ||= Monitor.new + @middleware_mutex.synchronize(&block) + end + + def load_middleware(key) + value = registered_middleware[key] + case value + when Module + value + when Symbol, String + middleware_mutex do + @registered_middleware[key] = const_get(value) + end + when Proc + middleware_mutex do + @registered_middleware[key] = value.call + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/options.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/options.rb new file mode 100644 index 0000000..d6d8353 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/options.rb @@ -0,0 +1,218 @@ +# frozen_string_literal: true + +module Faraday + # Subclasses Struct with some special helpers for converting from a Hash to + # a Struct. + class Options < Struct + # Public + def self.from(value) + value ? new.update(value) : new + end + + # Public + def each + return to_enum(:each) unless block_given? + + members.each do |key| + yield(key.to_sym, send(key)) + end + end + + # Public + def update(obj) + obj.each do |key, value| + sub_options = self.class.options_for(key) + if sub_options + new_value = sub_options.from(value) if value + elsif value.is_a?(Hash) + new_value = value.dup + else + new_value = value + end + + send("#{key}=", new_value) unless new_value.nil? + end + self + end + + # Public + def delete(key) + value = send(key) + send("#{key}=", nil) + value + end + + # Public + def clear + members.each { |member| delete(member) } + end + + # Public + def merge!(other) + other.each do |key, other_value| + self_value = send(key) + sub_options = self.class.options_for(key) + new_value = if self_value && sub_options && other_value + self_value.merge(other_value) + else + other_value + end + send("#{key}=", new_value) unless new_value.nil? + end + self + end + + # Public + def merge(other) + dup.merge!(other) + end + + # Public + def deep_dup + self.class.from(self) + end + + # Public + def fetch(key, *args) + unless symbolized_key_set.include?(key.to_sym) + key_setter = "#{key}=" + if !args.empty? + send(key_setter, args.first) + elsif block_given? + send(key_setter, yield(key)) + else + raise self.class.fetch_error_class, "key not found: #{key.inspect}" + end + end + send(key) + end + + # Public + def values_at(*keys) + keys.map { |key| send(key) } + end + + # Public + def keys + members.reject { |member| send(member).nil? } + end + + # Public + def empty? + keys.empty? + end + + # Public + def each_key(&block) + return to_enum(:each_key) unless block + + keys.each(&block) + end + + # Public + def key?(key) + keys.include?(key) + end + + alias has_key? key? + + # Public + def each_value(&block) + return to_enum(:each_value) unless block + + values.each(&block) + end + + # Public + def value?(value) + values.include?(value) + end + + alias has_value? value? + + # Public + def to_hash + hash = {} + members.each do |key| + value = send(key) + hash[key.to_sym] = value unless value.nil? + end + hash + end + + # Internal + def inspect + values = [] + members.each do |member| + value = send(member) + values << "#{member}=#{value.inspect}" if value + end + values = values.empty? ? '(empty)' : values.join(', ') + + %(#<#{self.class} #{values}>) + end + + # Internal + def self.options(mapping) + attribute_options.update(mapping) + end + + # Internal + def self.options_for(key) + attribute_options[key] + end + + # Internal + def self.attribute_options + @attribute_options ||= {} + end + + def self.memoized(key, &block) + unless block + raise ArgumentError, '#memoized must be called with a block' + end + + memoized_attributes[key.to_sym] = block + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{key}() self[:#{key}]; end + RUBY + end + + def self.memoized_attributes + @memoized_attributes ||= {} + end + + def [](key) + key = key.to_sym + if (method = self.class.memoized_attributes[key]) + super(key) || (self[key] = instance_eval(&method)) + else + super + end + end + + def symbolized_key_set + @symbolized_key_set ||= Set.new(keys.map(&:to_sym)) + end + + def self.inherited(subclass) + super + subclass.attribute_options.update(attribute_options) + subclass.memoized_attributes.update(memoized_attributes) + end + + def self.fetch_error_class + @fetch_error_class ||= if Object.const_defined?(:KeyError) + ::KeyError + else + ::IndexError + end + end + end +end + +require 'faraday/options/request_options' +require 'faraday/options/ssl_options' +require 'faraday/options/proxy_options' +require 'faraday/options/connection_options' +require 'faraday/options/env' diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/options/connection_options.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/options/connection_options.rb new file mode 100644 index 0000000..5a72940 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/options/connection_options.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Faraday + # ConnectionOptions contains the configurable properties for a Faraday + # connection object. + class ConnectionOptions < Options.new(:request, :proxy, :ssl, :builder, :url, + :parallel_manager, :params, :headers, + :builder_class) + + options request: RequestOptions, ssl: SSLOptions + + memoized(:request) { self.class.options_for(:request).new } + + memoized(:ssl) { self.class.options_for(:ssl).new } + + memoized(:builder_class) { RackBuilder } + + def new_builder(block) + builder_class.new(&block) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/options/env.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/options/env.rb new file mode 100644 index 0000000..1446f67 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/options/env.rb @@ -0,0 +1,199 @@ +# frozen_string_literal: true + +module Faraday + # @!attribute method + # @return [Symbol] HTTP method (`:get`, `:post`) + # + # @!attribute body + # @return [String] The request body that will eventually be converted to a + # string. + # + # @!attribute url + # @return [URI] URI instance for the current request. + # + # @!attribute request + # @return [Hash] options for configuring the request. + # Options for configuring the request. + # + # - `:timeout` open/read timeout Integer in seconds + # - `:open_timeout` - read timeout Integer in seconds + # - `:on_data` - Proc for streaming + # - `:proxy` - Hash of proxy options + # - `:uri` - Proxy Server URI + # - `:user` - Proxy server username + # - `:password` - Proxy server password + # + # @!attribute request_headers + # @return [Hash] HTTP Headers to be sent to the server. + # + # @!attribute ssl + # @return [Hash] options for configuring SSL requests + # + # @!attribute parallel_manager + # @return [Object] sent if the connection is in parallel mode + # + # @!attribute params + # @return [Hash] + # + # @!attribute response + # @return [Response] + # + # @!attribute response_headers + # @return [Hash] HTTP headers from the server + # + # @!attribute status + # @return [Integer] HTTP response status code + # + # @!attribute reason_phrase + # @return [String] + class Env < Options.new(:method, :request_body, :url, :request, + :request_headers, :ssl, :parallel_manager, :params, + :response, :response_headers, :status, + :reason_phrase, :response_body) + + # rubocop:disable Naming/ConstantName + ContentLength = 'Content-Length' + StatusesWithoutBody = Set.new [204, 304] + SuccessfulStatuses = (200..299).freeze + # rubocop:enable Naming/ConstantName + + # A Set of HTTP verbs that typically send a body. If no body is set for + # these requests, the Content-Length header is set to 0. + MethodsWithBodies = Set.new(Faraday::METHODS_WITH_BODY.map(&:to_sym)) + + options request: RequestOptions, + request_headers: Utils::Headers, response_headers: Utils::Headers + + extend Forwardable + + def_delegators :request, :params_encoder + + # Build a new Env from given value. Respects and updates `custom_members`. + # + # @param value [Object] a value fitting Option.from(v). + # @return [Env] from given value + def self.from(value) + env = super(value) + if value.respond_to?(:custom_members) + env.custom_members.update(value.custom_members) + end + env + end + + # @param key [Object] + def [](key) + return self[current_body] if key == :body + + if in_member_set?(key) + super(key) + else + custom_members[key] + end + end + + # @param key [Object] + # @param value [Object] + def []=(key, value) + if key == :body + super(current_body, value) + return + end + + if in_member_set?(key) + super(key, value) + else + custom_members[key] = value + end + end + + def current_body + !!status ? :response_body : :request_body + end + + def body + self[:body] + end + + def body=(value) + self[:body] = value + end + + # @return [Boolean] true if status is in the set of {SuccessfulStatuses}. + def success? + SuccessfulStatuses.include?(status) + end + + # @return [Boolean] true if there's no body yet, and the method is in the + # set of {MethodsWithBodies}. + def needs_body? + !body && MethodsWithBodies.include?(method) + end + + # Sets content length to zero and the body to the empty string. + def clear_body + request_headers[ContentLength] = '0' + self.body = +'' + end + + # @return [Boolean] true if the status isn't in the set of + # {StatusesWithoutBody}. + def parse_body? + !StatusesWithoutBody.include?(status) + end + + # @return [Boolean] true if there is a parallel_manager + def parallel? + !!parallel_manager + end + + def inspect + attrs = [nil] + members.each do |mem| + if (value = send(mem)) + attrs << "@#{mem}=#{value.inspect}" + end + end + attrs << "@custom=#{custom_members.inspect}" unless custom_members.empty? + %(#<#{self.class}#{attrs.join(' ')}>) + end + + def stream_response? + request.stream_response? + end + + def stream_response(&block) + size = 0 + yielded = false + block_result = block.call do |chunk| # rubocop:disable Performance/RedundantBlockCall + if chunk.bytesize.positive? || size.positive? + yielded = true + size += chunk.bytesize + request.on_data.call(chunk, size, self) + end + end + request.on_data.call(+'', 0, self) unless yielded + block_result + end + + # @private + def custom_members + @custom_members ||= {} + end + + # @private + if members.first.is_a?(Symbol) + def in_member_set?(key) + self.class.member_set.include?(key.to_sym) + end + else + def in_member_set?(key) + self.class.member_set.include?(key.to_s) + end + end + + # @private + def self.member_set + @member_set ||= Set.new(members) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/options/proxy_options.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/options/proxy_options.rb new file mode 100644 index 0000000..f94fe57 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/options/proxy_options.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Faraday + # ProxyOptions contains the configurable properties for the proxy + # configuration used when making an HTTP request. + class ProxyOptions < Options.new(:uri, :user, :password) + extend Forwardable + def_delegators :uri, :scheme, :scheme=, :host, :host=, :port, :port=, + :path, :path= + + def self.from(value) + case value + when String + # URIs without a scheme should default to http (like 'example:123'). + # This fixes #1282 and prevents a silent failure in some adapters. + value = "http://#{value}" unless value.include?('://') + value = { uri: Utils.URI(value) } + when URI + value = { uri: value } + when Hash, Options + if (uri = value.delete(:uri)) + value[:uri] = Utils.URI(uri) + end + end + + super(value) + end + + memoized(:user) { uri&.user && Utils.unescape(uri.user) } + memoized(:password) { uri&.password && Utils.unescape(uri.password) } + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/options/request_options.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/options/request_options.rb new file mode 100644 index 0000000..1a96fb8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/options/request_options.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Faraday + # RequestOptions contains the configurable properties for a Faraday request. + class RequestOptions < Options.new(:params_encoder, :proxy, :bind, + :timeout, :open_timeout, :read_timeout, + :write_timeout, :boundary, :oauth, + :context, :on_data) + + def []=(key, value) + if key && key.to_sym == :proxy + super(key, value ? ProxyOptions.from(value) : nil) + else + super(key, value) + end + end + + def stream_response? + on_data.is_a?(Proc) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/options/ssl_options.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/options/ssl_options.rb new file mode 100644 index 0000000..7abb6b1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/options/ssl_options.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +module Faraday + # SSL-related options. + # + # @!attribute verify + # @return [Boolean] whether to verify SSL certificates or not + # + # @!attribute verify_hostname + # @return [Boolean] whether to enable hostname verification on server certificates + # during the handshake or not (see https://github.com/ruby/openssl/pull/60) + # + # @!attribute ca_file + # @return [String] CA file + # + # @!attribute ca_path + # @return [String] CA path + # + # @!attribute verify_mode + # @return [Integer] Any `OpenSSL::SSL::` constant (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL.html) + # + # @!attribute cert_store + # @return [OpenSSL::X509::Store] certificate store + # + # @!attribute client_cert + # @return [String, OpenSSL::X509::Certificate] client certificate + # + # @!attribute client_key + # @return [String, OpenSSL::PKey::RSA, OpenSSL::PKey::DSA] client key + # + # @!attribute certificate + # @return [OpenSSL::X509::Certificate] certificate (Excon only) + # + # @!attribute private_key + # @return [OpenSSL::PKey::RSA, OpenSSL::PKey::DSA] private key (Excon only) + # + # @!attribute verify_depth + # @return [Integer] maximum depth for the certificate chain verification + # + # @!attribute version + # @return [String, Symbol] SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-ssl_version-3D) + # + # @!attribute min_version + # @return [String, Symbol] minimum SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-min_version-3D) + # + # @!attribute max_version + # @return [String, Symbol] maximum SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-max_version-3D) + class SSLOptions < Options.new(:verify, :verify_hostname, + :ca_file, :ca_path, :verify_mode, + :cert_store, :client_cert, :client_key, + :certificate, :private_key, :verify_depth, + :version, :min_version, :max_version) + + # @return [Boolean] true if should verify + def verify? + verify != false + end + + # @return [Boolean] true if should not verify + def disable? + !verify? + end + + # @return [Boolean] true if should verify_hostname + def verify_hostname? + verify_hostname != false + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/parameters.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/parameters.rb new file mode 100644 index 0000000..cfb35d0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/parameters.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +require 'forwardable' +require 'faraday/encoders/nested_params_encoder' +require 'faraday/encoders/flat_params_encoder' diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/rack_builder.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/rack_builder.rb new file mode 100644 index 0000000..d0da548 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/rack_builder.rb @@ -0,0 +1,252 @@ +# frozen_string_literal: true + +require 'ruby2_keywords' +require 'faraday/adapter_registry' + +module Faraday + # A Builder that processes requests into responses by passing through an inner + # middleware stack (heavily inspired by Rack). + # + # @example + # Faraday::Connection.new(url: 'http://httpbingo.org') do |builder| + # builder.request :url_encoded # Faraday::Request::UrlEncoded + # builder.adapter :net_http # Faraday::Adapter::NetHttp + # end + class RackBuilder + # Used to detect missing arguments + NO_ARGUMENT = Object.new + + attr_accessor :handlers + + # Error raised when trying to modify the stack after calling `lock!` + class StackLocked < RuntimeError; end + + # borrowed from ActiveSupport::Dependencies::Reference & + # ActionDispatch::MiddlewareStack::Middleware + class Handler + REGISTRY = Faraday::AdapterRegistry.new + + attr_reader :name + + ruby2_keywords def initialize(klass, *args, &block) + @name = klass.to_s + REGISTRY.set(klass) if klass.respond_to?(:name) + @args = args + @block = block + end + + def klass + REGISTRY.get(@name) + end + + def inspect + @name + end + + def ==(other) + if other.is_a? Handler + name == other.name + elsif other.respond_to? :name + klass == other + else + @name == other.to_s + end + end + + def build(app = nil) + klass.new(app, *@args, &@block) + end + end + + def initialize(&block) + @adapter = nil + @handlers = [] + build(&block) + end + + def initialize_dup(original) + super + @adapter = original.adapter + @handlers = original.handlers.dup + end + + def build + raise_if_locked + block_given? ? yield(self) : request(:url_encoded) + adapter(Faraday.default_adapter, **Faraday.default_adapter_options) unless @adapter + end + + def [](idx) + @handlers[idx] + end + + # Locks the middleware stack to ensure no further modifications are made. + def lock! + @handlers.freeze + end + + def locked? + @handlers.frozen? + end + + ruby2_keywords def use(klass, *args, &block) + if klass.is_a? Symbol + use_symbol(Faraday::Middleware, klass, *args, &block) + else + raise_if_locked + raise_if_adapter(klass) + @handlers << self.class::Handler.new(klass, *args, &block) + end + end + + ruby2_keywords def request(key, *args, &block) + use_symbol(Faraday::Request, key, *args, &block) + end + + ruby2_keywords def response(key, *args, &block) + use_symbol(Faraday::Response, key, *args, &block) + end + + ruby2_keywords def adapter(klass = NO_ARGUMENT, *args, &block) + return @adapter if klass == NO_ARGUMENT || klass.nil? + + klass = Faraday::Adapter.lookup_middleware(klass) if klass.is_a?(Symbol) + @adapter = self.class::Handler.new(klass, *args, &block) + end + + ## methods to push onto the various positions in the stack: + + ruby2_keywords def insert(index, *args, &block) + raise_if_locked + index = assert_index(index) + handler = self.class::Handler.new(*args, &block) + @handlers.insert(index, handler) + end + + alias insert_before insert + + ruby2_keywords def insert_after(index, *args, &block) + index = assert_index(index) + insert(index + 1, *args, &block) + end + + ruby2_keywords def swap(index, *args, &block) + raise_if_locked + index = assert_index(index) + @handlers.delete_at(index) + insert(index, *args, &block) + end + + def delete(handler) + raise_if_locked + @handlers.delete(handler) + end + + # Processes a Request into a Response by passing it through this Builder's + # middleware stack. + # + # @param connection [Faraday::Connection] + # @param request [Faraday::Request] + # + # @return [Faraday::Response] + def build_response(connection, request) + app.call(build_env(connection, request)) + end + + # The "rack app" wrapped in middleware. All requests are sent here. + # + # The builder is responsible for creating the app object. After this, + # the builder gets locked to ensure no further modifications are made + # to the middleware stack. + # + # Returns an object that responds to `call` and returns a Response. + def app + @app ||= begin + lock! + ensure_adapter! + to_app + end + end + + def to_app + # last added handler is the deepest and thus closest to the inner app + # adapter is always the last one + @handlers.reverse.inject(@adapter.build) do |app, handler| + handler.build(app) + end + end + + def ==(other) + other.is_a?(self.class) && + @handlers == other.handlers && + @adapter == other.adapter + end + + # ENV Keys + # :http_method - a symbolized request HTTP method (:get, :post) + # :body - the request body that will eventually be converted to a string. + # :url - URI instance for the current request. + # :status - HTTP response status code + # :request_headers - hash of HTTP Headers to be sent to the server + # :response_headers - Hash of HTTP headers from the server + # :parallel_manager - sent if the connection is in parallel mode + # :request - Hash of options for configuring the request. + # :timeout - open/read timeout Integer in seconds + # :open_timeout - read timeout Integer in seconds + # :proxy - Hash of proxy options + # :uri - Proxy Server URI + # :user - Proxy server username + # :password - Proxy server password + # :ssl - Hash of options for configuring SSL requests. + def build_env(connection, request) + exclusive_url = connection.build_exclusive_url( + request.path, request.params, + request.options.params_encoder + ) + + Env.new(request.http_method, request.body, exclusive_url, + request.options, request.headers, connection.ssl, + connection.parallel_manager) + end + + private + + LOCK_ERR = "can't modify middleware stack after making a request" + MISSING_ADAPTER_ERROR = "An attempt to run a request with a Faraday::Connection without adapter has been made.\n" \ + "Please set Faraday.default_adapter or provide one when initializing the connection.\n" \ + 'For more info, check https://lostisland.github.io/faraday/usage/.' + + def raise_if_locked + raise StackLocked, LOCK_ERR if locked? + end + + def raise_if_adapter(klass) + return unless is_adapter?(klass) + + raise 'Adapter should be set using the `adapter` method, not `use`' + end + + def ensure_adapter! + raise MISSING_ADAPTER_ERROR unless @adapter + end + + def adapter_set? + !@adapter.nil? + end + + def is_adapter?(klass) # rubocop:disable Naming/PredicateName + klass <= Faraday::Adapter + end + + ruby2_keywords def use_symbol(mod, key, *args, &block) + use(mod.lookup_middleware(key), *args, &block) + end + + def assert_index(index) + idx = index.is_a?(Integer) ? index : @handlers.index(index) + raise "No such handler: #{index.inspect}" unless idx + + idx + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/request.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/request.rb new file mode 100644 index 0000000..f8f3218 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/request.rb @@ -0,0 +1,136 @@ +# frozen_string_literal: true + +module Faraday + # Used to setup URLs, params, headers, and the request body in a sane manner. + # + # @example + # @connection.post do |req| + # req.url 'http://localhost', 'a' => '1' # 'http://localhost?a=1' + # req.headers['b'] = '2' # Header + # req.params['c'] = '3' # GET Param + # req['b'] = '2' # also Header + # req.body = 'abc' + # end + # + # @!attribute http_method + # @return [Symbol] the HTTP method of the Request + # @!attribute path + # @return [URI, String] the path + # @!attribute params + # @return [Hash] query parameters + # @!attribute headers + # @return [Faraday::Utils::Headers] headers + # @!attribute body + # @return [String] body + # @!attribute options + # @return [RequestOptions] options + # + # rubocop:disable Style/StructInheritance + class Request < Struct.new(:http_method, :path, :params, :headers, :body, :options) + # rubocop:enable Style/StructInheritance + + extend MiddlewareRegistry + + # @param request_method [String] + # @yield [request] for block customization, if block given + # @yieldparam request [Request] + # @return [Request] + def self.create(request_method) + new(request_method).tap do |request| + yield(request) if block_given? + end + end + + # Replace params, preserving the existing hash type. + # + # @param hash [Hash] new params + def params=(hash) + if params + params.replace hash + else + super + end + end + + # Replace request headers, preserving the existing hash type. + # + # @param hash [Hash] new headers + def headers=(hash) + if headers + headers.replace hash + else + super + end + end + + # Update path and params. + # + # @param path [URI, String] + # @param params [Hash, nil] + # @return [void] + def url(path, params = nil) + if path.respond_to? :query + if (query = path.query) + path = path.dup + path.query = nil + end + else + anchor_index = path.index('#') + path = path.slice(0, anchor_index) unless anchor_index.nil? + path, query = path.split('?', 2) + end + self.path = path + self.params.merge_query query, options.params_encoder + self.params.update(params) if params + end + + # @param key [Object] key to look up in headers + # @return [Object] value of the given header name + def [](key) + headers[key] + end + + # @param key [Object] key of header to write + # @param value [Object] value of header + def []=(key, value) + headers[key] = value + end + + # Marshal serialization support. + # + # @return [Hash] the hash ready to be serialized in Marshal. + def marshal_dump + { + http_method: http_method, + body: body, + headers: headers, + path: path, + params: params, + options: options + } + end + + # Marshal serialization support. + # Restores the instance variables according to the +serialised+. + # @param serialised [Hash] the serialised object. + def marshal_load(serialised) + self.http_method = serialised[:http_method] + self.body = serialised[:body] + self.headers = serialised[:headers] + self.path = serialised[:path] + self.params = serialised[:params] + self.options = serialised[:options] + end + + # @return [Env] the Env for this Request + def to_env(connection) + Env.new(http_method, body, connection.build_exclusive_url(path, params), + options, headers, connection.ssl, connection.parallel_manager) + end + end +end + +require 'faraday/request/authorization' +require 'faraday/request/instrumentation' +require 'faraday/request/json' +require 'faraday/request/url_encoded' diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/request/authorization.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/request/authorization.rb new file mode 100644 index 0000000..4373243 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/request/authorization.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module Faraday + class Request + # Request middleware for the Authorization HTTP header + class Authorization < Faraday::Middleware + KEY = 'Authorization' + + # @param app [#call] + # @param type [String, Symbol] Type of Authorization + # @param params [Array] parameters to build the Authorization header. + # If the type is `:basic`, then these can be a login and password pair. + # Otherwise, a single value is expected that will be appended after the type. + # This value can be a proc or an object responding to `.call`, in which case + # it will be invoked on each request. + def initialize(app, type, *params) + @type = type + @params = params + super(app) + end + + # @param env [Faraday::Env] + def on_request(env) + return if env.request_headers[KEY] + + env.request_headers[KEY] = header_from(@type, env, *@params) + end + + private + + # @param type [String, Symbol] + # @param env [Faraday::Env] + # @param params [Array] + # @return [String] a header value + def header_from(type, env, *params) + if type.to_s.casecmp('basic').zero? && params.size == 2 + Utils.basic_header_from(*params) + elsif params.size != 1 + raise ArgumentError, "Unexpected params received (got #{params.size} instead of 1)" + else + value = params.first + if (value.is_a?(Proc) && value.arity == 1) || (value.respond_to?(:call) && value.method(:call).arity == 1) + value = value.call(env) + elsif value.is_a?(Proc) || value.respond_to?(:call) + value = value.call + end + "#{type} #{value}" + end + end + end + end +end + +Faraday::Request.register_middleware(authorization: Faraday::Request::Authorization) diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/request/instrumentation.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/request/instrumentation.rb new file mode 100644 index 0000000..c03ba1e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/request/instrumentation.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +module Faraday + class Request + # Middleware for instrumenting Requests. + class Instrumentation < Faraday::Middleware + # Options class used in Request::Instrumentation class. + class Options < Faraday::Options.new(:name, :instrumenter) + # @return [String] + def name + self[:name] ||= 'request.faraday' + end + + # @return [Class] + def instrumenter + self[:instrumenter] ||= ActiveSupport::Notifications + end + end + + # Instruments requests using Active Support. + # + # Measures time spent only for synchronous requests. + # + # @example Using ActiveSupport::Notifications to measure time spent + # for Faraday requests. + # ActiveSupport::Notifications + # .subscribe('request.faraday') do |name, starts, ends, _, env| + # url = env[:url] + # http_method = env[:method].to_s.upcase + # duration = ends - starts + # $stderr.puts '[%s] %s %s (%.3f s)' % + # [url.host, http_method, url.request_uri, duration] + # end + # @param app [#call] + # @param options [nil, Hash] Options hash + # @option options [String] :name ('request.faraday') + # Name of the instrumenter + # @option options [Class] :instrumenter (ActiveSupport::Notifications) + # Active Support instrumenter class. + def initialize(app, options = nil) + super(app) + @name, @instrumenter = Options.from(options) + .values_at(:name, :instrumenter) + end + + # @param env [Faraday::Env] + def call(env) + @instrumenter.instrument(@name, env) do + @app.call(env) + end + end + end + end +end + +Faraday::Request.register_middleware(instrumentation: Faraday::Request::Instrumentation) diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/request/json.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/request/json.rb new file mode 100644 index 0000000..fa219c4 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/request/json.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'json' + +module Faraday + class Request + # Request middleware that encodes the body as JSON. + # + # Processes only requests with matching Content-type or those without a type. + # If a request doesn't have a type but has a body, it sets the Content-type + # to JSON MIME-type. + # + # Doesn't try to encode bodies that already are in string form. + class Json < Middleware + MIME_TYPE = 'application/json' + MIME_TYPE_REGEX = %r{^application/(vnd\..+\+)?json$}.freeze + + def on_request(env) + match_content_type(env) do |data| + env[:body] = encode(data) + end + end + + private + + def encode(data) + ::JSON.generate(data) + end + + def match_content_type(env) + return unless process_request?(env) + + env[:request_headers][CONTENT_TYPE] ||= MIME_TYPE + yield env[:body] unless env[:body].respond_to?(:to_str) + end + + def process_request?(env) + type = request_type(env) + body?(env) && (type.empty? || type.match?(MIME_TYPE_REGEX)) + end + + def body?(env) + (body = env[:body]) && !(body.respond_to?(:to_str) && body.empty?) + end + + def request_type(env) + type = env[:request_headers][CONTENT_TYPE].to_s + type = type.split(';', 2).first if type.index(';') + type + end + end + end +end + +Faraday::Request.register_middleware(json: Faraday::Request::Json) diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/request/url_encoded.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/request/url_encoded.rb new file mode 100644 index 0000000..5ac7dcb --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/request/url_encoded.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +module Faraday + class Request + # Middleware for supporting urlencoded requests. + class UrlEncoded < Faraday::Middleware + unless defined?(::Faraday::Request::UrlEncoded::CONTENT_TYPE) + CONTENT_TYPE = 'Content-Type' + end + + class << self + attr_accessor :mime_type + end + self.mime_type = 'application/x-www-form-urlencoded' + + # Encodes as "application/x-www-form-urlencoded" if not already encoded or + # of another type. + # + # @param env [Faraday::Env] + def call(env) + match_content_type(env) do |data| + params = Faraday::Utils::ParamsHash[data] + env.body = params.to_query(env.params_encoder) + end + @app.call env + end + + # @param env [Faraday::Env] + # @yield [request_body] Body of the request + def match_content_type(env) + return unless process_request?(env) + + env.request_headers[CONTENT_TYPE] ||= self.class.mime_type + return if env.body.respond_to?(:to_str) || env.body.respond_to?(:read) + + yield(env.body) + end + + # @param env [Faraday::Env] + # + # @return [Boolean] True if the request has a body and its Content-Type is + # urlencoded. + def process_request?(env) + type = request_type(env) + env.body && (type.empty? || (type == self.class.mime_type)) + end + + # @param env [Faraday::Env] + # + # @return [String] + def request_type(env) + type = env.request_headers[CONTENT_TYPE].to_s + type = type.split(';', 2).first if type.index(';') + type + end + end + end +end + +Faraday::Request.register_middleware(url_encoded: Faraday::Request::UrlEncoded) diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/response.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/response.rb new file mode 100644 index 0000000..3dd1bc5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/response.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +require 'forwardable' + +module Faraday + # Response represents an HTTP response from making an HTTP request. + class Response + extend Forwardable + extend MiddlewareRegistry + + def initialize(env = nil) + @env = Env.from(env) if env + @on_complete_callbacks = [] + end + + attr_reader :env + + def status + finished? ? env.status : nil + end + + def reason_phrase + finished? ? env.reason_phrase : nil + end + + def headers + finished? ? env.response_headers : {} + end + + def_delegator :headers, :[] + + def body + finished? ? env.body : nil + end + + def finished? + !!env + end + + def on_complete(&block) + if finished? + yield(env) + else + @on_complete_callbacks << block + end + self + end + + def finish(env) + raise 'response already finished' if finished? + + @env = env.is_a?(Env) ? env : Env.from(env) + @on_complete_callbacks.each { |callback| callback.call(@env) } + self + end + + def success? + finished? && env.success? + end + + def to_hash + { + status: env.status, body: env.body, + response_headers: env.response_headers + } + end + + # because @on_complete_callbacks cannot be marshalled + def marshal_dump + finished? ? to_hash : nil + end + + def marshal_load(env) + @env = Env.from(env) + end + + # Expand the env with more properties, without overriding existing ones. + # Useful for applying request params after restoring a marshalled Response. + def apply_request(request_env) + raise "response didn't finish yet" unless finished? + + @env = Env.from(request_env).update(@env) + self + end + end +end + +require 'faraday/response/json' +require 'faraday/response/logger' +require 'faraday/response/raise_error' diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/response/json.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/response/json.rb new file mode 100644 index 0000000..399b7f3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/response/json.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require 'json' + +module Faraday + class Response + # Parse response bodies as JSON. + class Json < Middleware + def initialize(app = nil, parser_options: nil, content_type: /\bjson$/, preserve_raw: false) + super(app) + @parser_options = parser_options + @content_types = Array(content_type) + @preserve_raw = preserve_raw + end + + def on_complete(env) + process_response(env) if parse_response?(env) + end + + private + + def process_response(env) + env[:raw_body] = env[:body] if @preserve_raw + env[:body] = parse(env[:body]) + rescue StandardError, SyntaxError => e + raise Faraday::ParsingError.new(e, env[:response]) + end + + def parse(body) + ::JSON.parse(body, @parser_options || {}) unless body.strip.empty? + end + + def parse_response?(env) + process_response_type?(env) && + env[:body].respond_to?(:to_str) + end + + def process_response_type?(env) + type = response_type(env) + @content_types.empty? || @content_types.any? do |pattern| + pattern.is_a?(Regexp) ? type.match?(pattern) : type == pattern + end + end + + def response_type(env) + type = env[:response_headers][CONTENT_TYPE].to_s + type = type.split(';', 2).first if type.index(';') + type + end + end + end +end + +Faraday::Response.register_middleware(json: Faraday::Response::Json) diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/response/logger.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/response/logger.rb new file mode 100644 index 0000000..422e32b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/response/logger.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'forwardable' +require 'logger' +require 'faraday/logging/formatter' + +module Faraday + class Response + # Logger is a middleware that logs internal events in the HTTP request + # lifecycle to a given Logger object. By default, this logs to STDOUT. See + # Faraday::Logging::Formatter to see specifically what is logged. + class Logger < Middleware + def initialize(app, logger = nil, options = {}) + super(app) + logger ||= ::Logger.new($stdout) + formatter_class = options.delete(:formatter) || Logging::Formatter + @formatter = formatter_class.new(logger: logger, options: options) + yield @formatter if block_given? + end + + def call(env) + @formatter.request(env) + super + end + + def on_complete(env) + @formatter.response(env) + end + + def on_error(error) + @formatter.error(error) if @formatter.respond_to?(:error) + end + end + end +end + +Faraday::Response.register_middleware(logger: Faraday::Response::Logger) diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/response/raise_error.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/response/raise_error.rb new file mode 100644 index 0000000..e72cd40 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/response/raise_error.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +module Faraday + class Response + # RaiseError is a Faraday middleware that raises exceptions on common HTTP + # client or server error responses. + class RaiseError < Middleware + # rubocop:disable Naming/ConstantName + ClientErrorStatuses = (400...500).freeze + ServerErrorStatuses = (500...600).freeze + # rubocop:enable Naming/ConstantName + + def on_complete(env) + case env[:status] + when 400 + raise Faraday::BadRequestError, response_values(env) + when 401 + raise Faraday::UnauthorizedError, response_values(env) + when 403 + raise Faraday::ForbiddenError, response_values(env) + when 404 + raise Faraday::ResourceNotFound, response_values(env) + when 407 + # mimic the behavior that we get with proxy requests with HTTPS + msg = %(407 "Proxy Authentication Required") + raise Faraday::ProxyAuthError.new(msg, response_values(env)) + when 409 + raise Faraday::ConflictError, response_values(env) + when 422 + raise Faraday::UnprocessableEntityError, response_values(env) + when ClientErrorStatuses + raise Faraday::ClientError, response_values(env) + when ServerErrorStatuses + raise Faraday::ServerError, response_values(env) + when nil + raise Faraday::NilStatusError, response_values(env) + end + end + + def response_values(env) + { + status: env.status, + headers: env.response_headers, + body: env.body, + request: { + method: env.method, + url: env.url, + url_path: env.url.path, + params: query_params(env), + headers: env.request_headers, + body: env.request_body + } + } + end + + def query_params(env) + env.request.params_encoder ||= Faraday::Utils.default_params_encoder + env.params_encoder.decode(env.url.query) + end + end + end +end + +Faraday::Response.register_middleware(raise_error: Faraday::Response::RaiseError) diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/utils.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/utils.rb new file mode 100644 index 0000000..428dc97 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/utils.rb @@ -0,0 +1,122 @@ +# frozen_string_literal: true + +require 'base64' +require 'uri' +require 'faraday/utils/headers' +require 'faraday/utils/params_hash' + +module Faraday + # Utils contains various static helper methods. + module Utils + module_function + + def build_query(params) + FlatParamsEncoder.encode(params) + end + + def build_nested_query(params) + NestedParamsEncoder.encode(params) + end + + def default_space_encoding + @default_space_encoding ||= '+' + end + + class << self + attr_writer :default_space_encoding + end + + ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/.freeze + + def escape(str) + str.to_s.gsub(ESCAPE_RE) do |match| + "%#{match.unpack('H2' * match.bytesize).join('%').upcase}" + end.gsub(' ', default_space_encoding) + end + + def unescape(str) + CGI.unescape str.to_s + end + + DEFAULT_SEP = /[&;] */n.freeze + + # Adapted from Rack + def parse_query(query) + FlatParamsEncoder.decode(query) + end + + def parse_nested_query(query) + NestedParamsEncoder.decode(query) + end + + def default_params_encoder + @default_params_encoder ||= NestedParamsEncoder + end + + def basic_header_from(login, pass) + value = Base64.encode64("#{login}:#{pass}") + value.delete!("\n") + "Basic #{value}" + end + + class << self + attr_writer :default_params_encoder + end + + # Normalize URI() behavior across Ruby versions + # + # url - A String or URI. + # + # Returns a parsed URI. + def URI(url) # rubocop:disable Naming/MethodName + if url.respond_to?(:host) + url + elsif url.respond_to?(:to_str) + default_uri_parser.call(url) + else + raise ArgumentError, 'bad argument (expected URI object or URI string)' + end + end + + def default_uri_parser + @default_uri_parser ||= Kernel.method(:URI) + end + + def default_uri_parser=(parser) + @default_uri_parser = if parser.respond_to?(:call) || parser.nil? + parser + else + parser.method(:parse) + end + end + + # Receives a String or URI and returns just + # the path with the query string sorted. + def normalize_path(url) + url = URI(url) + (url.path.start_with?('/') ? url.path : "/#{url.path}") + + (url.query ? "?#{sort_query_params(url.query)}" : '') + end + + # Recursive hash update + def deep_merge!(target, hash) + hash.each do |key, value| + target[key] = if value.is_a?(Hash) && (target[key].is_a?(Hash) || target[key].is_a?(Options)) + deep_merge(target[key], value) + else + value + end + end + target + end + + # Recursive hash merge + def deep_merge(source, hash) + deep_merge!(source.dup, hash) + end + + def sort_query_params(query) + query.split('&').sort.join('&') + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/utils/headers.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/utils/headers.rb new file mode 100644 index 0000000..90b8be2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/utils/headers.rb @@ -0,0 +1,144 @@ +# frozen_string_literal: true + +module Faraday + module Utils + # A case-insensitive Hash that preserves the original case of a header + # when set. + # + # Adapted from Rack::Utils::HeaderHash + class Headers < ::Hash + def self.from(value) + new(value) + end + + def self.allocate + new_self = super + new_self.initialize_names + new_self + end + + def initialize(hash = nil) + super() + @names = {} + update(hash || {}) + end + + def initialize_names + @names = {} + end + + # on dup/clone, we need to duplicate @names hash + def initialize_copy(other) + super + @names = other.names.dup + end + + # need to synchronize concurrent writes to the shared KeyMap + keymap_mutex = Mutex.new + + # symbol -> string mapper + cache + KeyMap = Hash.new do |map, key| + value = if key.respond_to?(:to_str) + key + else + key.to_s.split('_') # user_agent: %w(user agent) + .each(&:capitalize!) # => %w(User Agent) + .join('-') # => "User-Agent" + end + keymap_mutex.synchronize { map[key] = value } + end + KeyMap[:etag] = 'ETag' + + def [](key) + key = KeyMap[key] + super(key) || super(@names[key.downcase]) + end + + def []=(key, val) + key = KeyMap[key] + key = (@names[key.downcase] ||= key) + # join multiple values with a comma + val = val.to_ary.join(', ') if val.respond_to?(:to_ary) + super(key, val) + end + + def fetch(key, *args, &block) + key = KeyMap[key] + key = @names.fetch(key.downcase, key) + super(key, *args, &block) + end + + def delete(key) + key = KeyMap[key] + key = @names[key.downcase] + return unless key + + @names.delete key.downcase + super(key) + end + + def include?(key) + @names.include? key.downcase + end + + alias has_key? include? + alias member? include? + alias key? include? + + def merge!(other) + other.each { |k, v| self[k] = v } + self + end + + alias update merge! + + def merge(other) + hash = dup + hash.merge! other + end + + def replace(other) + clear + @names.clear + update other + self + end + + def to_hash + {}.update(self) + end + + def parse(header_string) + return unless header_string && !header_string.empty? + + headers = header_string.split("\r\n") + + # Find the last set of response headers. + start_index = headers.rindex { |x| x.start_with?('HTTP/') } || 0 + last_response = headers.slice(start_index, headers.size) + + last_response + .tap { |a| a.shift if a.first.start_with?('HTTP/') } + .map { |h| h.split(/:\s*/, 2) } # split key and value + .reject { |p| p[0].nil? } # ignore blank lines + .each { |key, value| add_parsed(key, value) } + end + + protected + + attr_reader :names + + private + + # Join multiple values with a comma. + def add_parsed(key, value) + if key?(key) + self[key] = self[key].to_s + self[key] << ', ' << value + else + self[key] = value + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/utils/params_hash.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/utils/params_hash.rb new file mode 100644 index 0000000..0e16d93 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/utils/params_hash.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +module Faraday + module Utils + # A hash with stringified keys. + class ParamsHash < Hash + def [](key) + super(convert_key(key)) + end + + def []=(key, value) + super(convert_key(key), value) + end + + def delete(key) + super(convert_key(key)) + end + + def include?(key) + super(convert_key(key)) + end + + alias has_key? include? + alias member? include? + alias key? include? + + def update(params) + params.each do |key, value| + self[key] = value + end + self + end + alias merge! update + + def merge(params) + dup.update(params) + end + + def replace(other) + clear + update(other) + end + + def merge_query(query, encoder = nil) + return self unless query && !query.empty? + + update((encoder || Utils.default_params_encoder).decode(query)) + end + + def to_query(encoder = nil) + (encoder || Utils.default_params_encoder).encode(self) + end + + private + + def convert_key(key) + key.to_s + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/version.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/version.rb new file mode 100644 index 0000000..9479a99 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/lib/faraday/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module Faraday + VERSION = '2.7.1' +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/external_adapters/faraday_specs_setup.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/external_adapters/faraday_specs_setup.rb new file mode 100644 index 0000000..ac7f7b6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/external_adapters/faraday_specs_setup.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require 'webmock/rspec' +WebMock.disable_net_connect!(allow_localhost: true) + +require_relative '../support/helper_methods' +require_relative '../support/disabling_stub' +require_relative '../support/streaming_response_checker' +require_relative '../support/shared_examples/adapter' +require_relative '../support/shared_examples/request_method' + +RSpec.configure do |config| + config.include Faraday::HelperMethods +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/adapter/test_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/adapter/test_spec.rb new file mode 100644 index 0000000..bdeb6cd --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/adapter/test_spec.rb @@ -0,0 +1,413 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Adapter::Test do + let(:stubs) do + described_class::Stubs.new do |stub| + stub.get('http://domain.test/hello') do + [200, { 'Content-Type' => 'text/html' }, 'domain: hello'] + end + + stub.get('http://wrong.test/hello') do + [200, { 'Content-Type' => 'text/html' }, 'wrong: hello'] + end + + stub.get('http://wrong.test/bait') do + [404, { 'Content-Type' => 'text/html' }] + end + + stub.get('/hello') do + [200, { 'Content-Type' => 'text/html' }, 'hello'] + end + + stub.get('/method-echo') do |env| + [200, { 'Content-Type' => 'text/html' }, env[:method].to_s] + end + + stub.get(%r{\A/resources/\d+(?:\?|\z)}) do + [200, { 'Content-Type' => 'text/html' }, 'show'] + end + + stub.get(%r{\A/resources/(specified)\z}) do |_env, meta| + [200, { 'Content-Type' => 'text/html' }, "show #{meta[:match_data][1]}"] + end + end + end + + let(:connection) do + Faraday.new do |builder| + builder.adapter :test, stubs + end + end + + let(:response) { connection.get('/hello') } + + context 'with simple path sets status' do + subject { response.status } + + it { is_expected.to eq 200 } + end + + context 'with simple path sets headers' do + subject { response.headers['Content-Type'] } + + it { is_expected.to eq 'text/html' } + end + + context 'with simple path sets body' do + subject { response.body } + + it { is_expected.to eq 'hello' } + end + + context 'with host points to the right stub' do + subject { connection.get('http://domain.test/hello').body } + + it { is_expected.to eq 'domain: hello' } + end + + describe 'can be called several times' do + subject { connection.get('/hello').body } + + it { is_expected.to eq 'hello' } + end + + describe 'can handle regular expression path' do + subject { connection.get('/resources/1').body } + + it { is_expected.to eq 'show' } + end + + describe 'can handle single parameter block' do + subject { connection.get('/method-echo').body } + + it { is_expected.to eq 'get' } + end + + describe 'can handle regular expression path with captured result' do + subject { connection.get('/resources/specified').body } + + it { is_expected.to eq 'show specified' } + end + + context 'with get params' do + subject { connection.get('/param?a=1').body } + + before do + stubs.get('/param?a=1') { [200, {}, 'a'] } + end + + it { is_expected.to eq 'a' } + end + + describe 'ignoring unspecified get params' do + before do + stubs.get('/optional?a=1') { [200, {}, 'a'] } + end + + context 'with multiple params' do + subject { connection.get('/optional?a=1&b=1').body } + + it { is_expected.to eq 'a' } + end + + context 'with single param' do + subject { connection.get('/optional?a=1').body } + + it { is_expected.to eq 'a' } + end + + context 'without params' do + subject(:request) { connection.get('/optional') } + + it do + expect { request }.to raise_error( + Faraday::Adapter::Test::Stubs::NotFound + ) + end + end + end + + context 'with http headers' do + before do + stubs.get('/yo', 'X-HELLO' => 'hello') { [200, {}, 'a'] } + stubs.get('/yo') { [200, {}, 'b'] } + end + + context 'with header' do + subject do + connection.get('/yo') { |env| env.headers['X-HELLO'] = 'hello' }.body + end + + it { is_expected.to eq 'a' } + end + + context 'without header' do + subject do + connection.get('/yo').body + end + + it { is_expected.to eq 'b' } + end + end + + describe 'different outcomes for the same request' do + def make_request + connection.get('/foo') + end + + subject(:request) { make_request.body } + + before do + stubs.get('/foo') { [200, { 'Content-Type' => 'text/html' }, 'hello'] } + stubs.get('/foo') { [200, { 'Content-Type' => 'text/html' }, 'world'] } + end + + context 'the first request' do + it { is_expected.to eq 'hello' } + end + + context 'the second request' do + before do + make_request + end + + it { is_expected.to eq 'world' } + end + end + + describe 'yielding env to stubs' do + subject { connection.get('http://foo.com/foo?a=1').body } + + before do + stubs.get '/foo' do |env| + expect(env[:url].path).to eq '/foo' + expect(env[:url].host).to eq 'foo.com' + expect(env[:params]['a']).to eq '1' + expect(env[:request_headers]['Accept']).to eq 'text/plain' + [200, {}, 'a'] + end + + connection.headers['Accept'] = 'text/plain' + end + + it { is_expected.to eq 'a' } + end + + describe 'params parsing' do + subject { connection.get('http://foo.com/foo?a[b]=1').body } + + context 'with default encoder' do + before do + stubs.get '/foo' do |env| + expect(env[:params]['a']['b']).to eq '1' + [200, {}, 'a'] + end + end + + it { is_expected.to eq 'a' } + end + + context 'with nested encoder' do + before do + stubs.get '/foo' do |env| + expect(env[:params]['a']['b']).to eq '1' + [200, {}, 'a'] + end + + connection.options.params_encoder = Faraday::NestedParamsEncoder + end + + it { is_expected.to eq 'a' } + end + + context 'with flat encoder' do + before do + stubs.get '/foo' do |env| + expect(env[:params]['a[b]']).to eq '1' + [200, {}, 'a'] + end + + connection.options.params_encoder = Faraday::FlatParamsEncoder + end + + it { is_expected.to eq 'a' } + end + end + + describe 'raising an error if no stub was found' do + describe 'for request' do + subject(:request) { connection.get('/invalid') { [200, {}, []] } } + + it { expect { request }.to raise_error described_class::Stubs::NotFound } + end + + describe 'for specified host' do + subject(:request) { connection.get('http://domain.test/bait') } + + it { expect { request }.to raise_error described_class::Stubs::NotFound } + end + + describe 'for request without specified header' do + subject(:request) { connection.get('/yo') } + + before do + stubs.get('/yo', 'X-HELLO' => 'hello') { [200, {}, 'a'] } + end + + it { expect { request }.to raise_error described_class::Stubs::NotFound } + end + end + + describe 'for request with non default params encoder' do + let(:connection) do + Faraday.new(request: { params_encoder: Faraday::FlatParamsEncoder }) do |builder| + builder.adapter :test, stubs + end + end + let(:stubs) do + described_class::Stubs.new do |stubs| + stubs.get('/path?a=x&a=y&a=z') { [200, {}, 'a'] } + end + end + + context 'when all flat param values are correctly set' do + subject(:request) { connection.get('/path?a=x&a=y&a=z') } + + it { expect(request.status).to eq 200 } + end + + shared_examples 'raise NotFound when params do not satisfy the flat param values' do |params| + subject(:request) { connection.get('/path', params) } + + context "with #{params.inspect}" do + it { expect { request }.to raise_error described_class::Stubs::NotFound } + end + end + + it_behaves_like 'raise NotFound when params do not satisfy the flat param values', { a: %w[x] } + it_behaves_like 'raise NotFound when params do not satisfy the flat param values', { a: %w[x y] } + it_behaves_like 'raise NotFound when params do not satisfy the flat param values', { a: %w[x z y] } # NOTE: The order of the value is also compared. + it_behaves_like 'raise NotFound when params do not satisfy the flat param values', { b: %w[x y z] } + end + + describe 'strict_mode' do + let(:stubs) do + described_class::Stubs.new(strict_mode: true) do |stubs| + stubs.get('/strict?a=12&b=xy', 'Authorization' => 'Bearer m_ck', 'X-C' => 'hello') { [200, {}, 'a'] } + stubs.get('/with_user_agent?a=12&b=xy', authorization: 'Bearer m_ck', 'User-Agent' => 'My Agent') { [200, {}, 'a'] } + end + end + + context 'when params and headers are exactly set' do + subject(:request) { connection.get('/strict', { a: '12', b: 'xy' }, { authorization: 'Bearer m_ck', x_c: 'hello' }) } + + it { expect(request.status).to eq 200 } + end + + context 'when params and headers are exactly set with a custom user agent' do + subject(:request) { connection.get('/with_user_agent', { a: '12', b: 'xy' }, { authorization: 'Bearer m_ck', 'User-Agent' => 'My Agent' }) } + + it { expect(request.status).to eq 200 } + end + + shared_examples 'raise NotFound when params do not satisfy the strict check' do |params| + subject(:request) { connection.get('/strict', params, { 'Authorization' => 'Bearer m_ck', 'X-C' => 'hello' }) } + + context "with #{params.inspect}" do + it { expect { request }.to raise_error described_class::Stubs::NotFound } + end + end + + it_behaves_like 'raise NotFound when params do not satisfy the strict check', { a: '12' } + it_behaves_like 'raise NotFound when params do not satisfy the strict check', { b: 'xy' } + it_behaves_like 'raise NotFound when params do not satisfy the strict check', { a: '123', b: 'xy' } + it_behaves_like 'raise NotFound when params do not satisfy the strict check', { a: '12', b: 'xyz' } + it_behaves_like 'raise NotFound when params do not satisfy the strict check', { a: '12', b: 'xy', c: 'hello' } + it_behaves_like 'raise NotFound when params do not satisfy the strict check', { additional: 'special', a: '12', b: 'xy', c: 'hello' } + + shared_examples 'raise NotFound when headers do not satisfy the strict check' do |path, headers| + subject(:request) { connection.get(path, { a: 12, b: 'xy' }, headers) } + + context "with #{headers.inspect}" do + it { expect { request }.to raise_error described_class::Stubs::NotFound } + end + end + + it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck' } + it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { 'X-C' => 'hello' } + it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'Hi' } + it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { authorization: 'Basic m_ck', 'x-c': 'hello' } + it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'hello', x_special: 'special' } + it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck' } + it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'Unknown' } + it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'My Agent', x_special: 'special' } + + context 'when strict_mode is disabled' do + before do + stubs.strict_mode = false + end + + shared_examples 'does not raise NotFound even when params do not satisfy the strict check' do |params| + subject(:request) { connection.get('/strict', params, { 'Authorization' => 'Bearer m_ck', 'X-C' => 'hello' }) } + + context "with #{params.inspect}" do + it { expect(request.status).to eq 200 } + end + end + + it_behaves_like 'does not raise NotFound even when params do not satisfy the strict check', { a: '12', b: 'xy' } + it_behaves_like 'does not raise NotFound even when params do not satisfy the strict check', { a: '12', b: 'xy', c: 'hello' } + it_behaves_like 'does not raise NotFound even when params do not satisfy the strict check', { additional: 'special', a: '12', b: 'xy', c: 'hello' } + + shared_examples 'does not raise NotFound even when headers do not satisfy the strict check' do |path, headers| + subject(:request) { connection.get(path, { a: 12, b: 'xy' }, headers) } + + context "with #{headers.inspect}" do + it { expect(request.status).to eq 200 } + end + end + + it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'hello' } + it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'hello', x_special: 'special' } + it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'hello', user_agent: 'Special Agent' } + it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'My Agent' } + it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'My Agent', x_special: 'special' } + end + + describe 'body_match?' do + let(:stubs) do + described_class::Stubs.new do |stubs| + stubs.post('/no_check') { [200, {}, 'ok'] } + stubs.post('/with_string', 'abc') { [200, {}, 'ok'] } + stubs.post( + '/with_proc', + ->(request_body) { JSON.parse(request_body, symbolize_names: true) == { x: '!', a: [{ m: [{ a: true }], n: 123 }] } }, + { content_type: 'application/json' } + ) do + [200, {}, 'ok'] + end + end + end + + context 'when trying without any args for body' do + subject(:without_body) { connection.post('/no_check') } + + it { expect(without_body.status).to eq 200 } + end + + context 'when trying with string body stubs' do + subject(:with_string) { connection.post('/with_string', 'abc') } + + it { expect(with_string.status).to eq 200 } + end + + context 'when trying with proc body stubs' do + subject(:with_proc) do + connection.post('/with_proc', JSON.dump(a: [{ n: 123, m: [{ a: true }] }], x: '!'), { 'Content-Type' => 'application/json' }) + end + + it { expect(with_proc.status).to eq 200 } + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/adapter_registry_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/adapter_registry_spec.rb new file mode 100644 index 0000000..222e65e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/adapter_registry_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::AdapterRegistry do + describe '#initialize' do + subject(:registry) { described_class.new } + + it { expect { registry.get(:FinFangFoom) }.to raise_error(NameError) } + it { expect { registry.get('FinFangFoom') }.to raise_error(NameError) } + + it 'looks up class by string name' do + expect(registry.get('Faraday::Connection')).to eq(Faraday::Connection) + end + + it 'looks up class by symbol name' do + expect(registry.get(:Faraday)).to eq(Faraday) + end + + it 'caches lookups with implicit name' do + registry.set :symbol + expect(registry.get('symbol')).to eq(:symbol) + end + + it 'caches lookups with explicit name' do + registry.set 'string', :name + expect(registry.get(:name)).to eq('string') + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/adapter_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/adapter_spec.rb new file mode 100644 index 0000000..22ef1d1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/adapter_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Adapter do + let(:adapter) { Faraday::Adapter.new } + let(:request) { {} } + + context '#request_timeout' do + it 'gets :read timeout' do + expect(timeout(:read)).to eq(nil) + + request[:timeout] = 5 + request[:write_timeout] = 1 + + expect(timeout(:read)).to eq(5) + + request[:read_timeout] = 2 + + expect(timeout(:read)).to eq(2) + end + + it 'gets :open timeout' do + expect(timeout(:open)).to eq(nil) + + request[:timeout] = 5 + request[:write_timeout] = 1 + + expect(timeout(:open)).to eq(5) + + request[:open_timeout] = 2 + + expect(timeout(:open)).to eq(2) + end + + it 'gets :write timeout' do + expect(timeout(:write)).to eq(nil) + + request[:timeout] = 5 + request[:read_timeout] = 1 + + expect(timeout(:write)).to eq(5) + + request[:write_timeout] = 2 + + expect(timeout(:write)).to eq(2) + end + + it 'attempts unknown timeout type' do + expect { timeout(:unknown) }.to raise_error(ArgumentError) + end + + def timeout(type) + adapter.send(:request_timeout, type, request) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/connection_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/connection_spec.rb new file mode 100644 index 0000000..05d9c28 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/connection_spec.rb @@ -0,0 +1,793 @@ +# frozen_string_literal: true + +class CustomEncoder + def encode(params) + params.map { |k, v| "#{k.upcase}-#{v.to_s.upcase}" }.join(',') + end + + def decode(params) + params.split(',').to_h { |pair| pair.split('-') } + end +end + +shared_examples 'initializer with url' do + context 'with simple url' do + let(:address) { 'http://httpbingo.org' } + + it { expect(subject.host).to eq('httpbingo.org') } + it { expect(subject.port).to eq(80) } + it { expect(subject.scheme).to eq('http') } + it { expect(subject.path_prefix).to eq('/') } + it { expect(subject.params).to eq({}) } + end + + context 'with complex url' do + let(:address) { 'http://httpbingo.org:815/fish?a=1' } + + it { expect(subject.port).to eq(815) } + it { expect(subject.path_prefix).to eq('/fish') } + it { expect(subject.params).to eq('a' => '1') } + end + + context 'with IPv6 address' do + let(:address) { 'http://[::1]:85/' } + + it { expect(subject.host).to eq('[::1]') } + it { expect(subject.port).to eq(85) } + end +end + +shared_examples 'default connection options' do + after { Faraday.default_connection_options = nil } + + it 'works with implicit url' do + conn = Faraday.new 'http://httpbingo.org/foo' + expect(conn.options.timeout).to eq(10) + end + + it 'works with option url' do + conn = Faraday.new url: 'http://httpbingo.org/foo' + expect(conn.options.timeout).to eq(10) + end + + it 'works with instance connection options' do + conn = Faraday.new 'http://httpbingo.org/foo', request: { open_timeout: 1 } + expect(conn.options.timeout).to eq(10) + expect(conn.options.open_timeout).to eq(1) + end + + it 'default connection options persist with an instance overriding' do + conn = Faraday.new 'http://nigiri.com/bar' + conn.options.timeout = 1 + expect(Faraday.default_connection_options.request.timeout).to eq(10) + + other = Faraday.new url: 'https://httpbingo.org/foo' + other.options.timeout = 1 + + expect(Faraday.default_connection_options.request.timeout).to eq(10) + end + + it 'default connection uses default connection options' do + expect(Faraday.default_connection.options.timeout).to eq(10) + end +end + +RSpec.describe Faraday::Connection do + let(:conn) { Faraday::Connection.new(url, options) } + let(:url) { nil } + let(:options) { nil } + + describe '.new' do + subject { conn } + + context 'with implicit url param' do + # Faraday::Connection.new('http://httpbingo.org') + let(:url) { address } + + it_behaves_like 'initializer with url' + end + + context 'with explicit url param' do + # Faraday::Connection.new(url: 'http://httpbingo.org') + let(:url) { { url: address } } + + it_behaves_like 'initializer with url' + end + + context 'with custom builder' do + let(:custom_builder) { Faraday::RackBuilder.new } + let(:options) { { builder: custom_builder } } + + it { expect(subject.builder).to eq(custom_builder) } + end + + context 'with custom params' do + let(:options) { { params: { a: 1 } } } + + it { expect(subject.params).to eq('a' => 1) } + end + + context 'with custom params and params in url' do + let(:url) { 'http://httpbingo.org/fish?a=1&b=2' } + let(:options) { { params: { a: 3 } } } + it { expect(subject.params).to eq('a' => 3, 'b' => '2') } + end + + context 'with basic_auth in url' do + let(:url) { 'http://Aladdin:open%20sesame@httpbingo.org/fish' } + + it { expect(subject.headers['Authorization']).to eq('Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==') } + end + + context 'with custom headers' do + let(:options) { { headers: { user_agent: 'Faraday' } } } + + it { expect(subject.headers['User-agent']).to eq('Faraday') } + end + + context 'with ssl false' do + let(:options) { { ssl: { verify: false } } } + + it { expect(subject.ssl.verify?).to be_falsey } + end + + context 'with verify_hostname false' do + let(:options) { { ssl: { verify_hostname: false } } } + + it { expect(subject.ssl.verify_hostname?).to be_falsey } + end + + context 'with empty block' do + let(:conn) { Faraday::Connection.new {} } + + it { expect(conn.builder.handlers.size).to eq(0) } + end + + context 'with block' do + let(:conn) do + Faraday::Connection.new(params: { 'a' => '1' }) do |faraday| + faraday.adapter :test + faraday.url_prefix = 'http://httpbingo.org/omnom' + end + end + + it { expect(conn.builder.handlers.size).to eq(0) } + it { expect(conn.path_prefix).to eq('/omnom') } + end + end + + describe '#close' do + before { Faraday.default_adapter = :test } + after { Faraday.default_adapter = nil } + + it 'can close underlying app' do + expect(conn.app).to receive(:close) + conn.close + end + end + + describe '#build_exclusive_url' do + context 'with relative path' do + subject { conn.build_exclusive_url('sake.html') } + + it 'uses connection host as default host' do + conn.host = 'httpbingo.org' + expect(subject.host).to eq('httpbingo.org') + expect(subject.scheme).to eq('http') + end + + it do + conn.path_prefix = '/fish' + expect(subject.path).to eq('/fish/sake.html') + end + + it do + conn.path_prefix = '/' + expect(subject.path).to eq('/sake.html') + end + + it do + conn.path_prefix = 'fish' + expect(subject.path).to eq('/fish/sake.html') + end + + it do + conn.path_prefix = '/fish/' + expect(subject.path).to eq('/fish/sake.html') + end + end + + context 'with absolute path' do + subject { conn.build_exclusive_url('/sake.html') } + + after { expect(subject.path).to eq('/sake.html') } + + it { conn.path_prefix = '/fish' } + it { conn.path_prefix = '/' } + it { conn.path_prefix = 'fish' } + it { conn.path_prefix = '/fish/' } + end + + context 'with complete url' do + subject { conn.build_exclusive_url('http://httpbingo.org/sake.html?a=1') } + + it { expect(subject.scheme).to eq('http') } + it { expect(subject.host).to eq('httpbingo.org') } + it { expect(subject.port).to eq(80) } + it { expect(subject.path).to eq('/sake.html') } + it { expect(subject.query).to eq('a=1') } + end + + it 'overrides connection port for absolute url' do + conn.port = 23 + uri = conn.build_exclusive_url('http://httpbingo.org') + expect(uri.port).to eq(80) + end + + it 'does not add ending slash given nil url' do + conn.url_prefix = 'http://httpbingo.org/nigiri' + uri = conn.build_exclusive_url + expect(uri.path).to eq('/nigiri') + end + + it 'does not add ending slash given empty url' do + conn.url_prefix = 'http://httpbingo.org/nigiri' + uri = conn.build_exclusive_url('') + expect(uri.path).to eq('/nigiri') + end + + it 'does not use connection params' do + conn.url_prefix = 'http://httpbingo.org/nigiri' + conn.params = { a: 1 } + expect(conn.build_exclusive_url.to_s).to eq('http://httpbingo.org/nigiri') + end + + it 'allows to provide params argument' do + conn.url_prefix = 'http://httpbingo.org/nigiri' + conn.params = { a: 1 } + params = Faraday::Utils::ParamsHash.new + params[:a] = 2 + uri = conn.build_exclusive_url(nil, params) + expect(uri.to_s).to eq('http://httpbingo.org/nigiri?a=2') + end + + it 'handles uri instances' do + uri = conn.build_exclusive_url(URI('/sake.html')) + expect(uri.path).to eq('/sake.html') + end + + it 'always returns new URI instance' do + conn.url_prefix = 'http://httpbingo.org' + uri1 = conn.build_exclusive_url(nil) + uri2 = conn.build_exclusive_url(nil) + expect(uri1).not_to equal(uri2) + end + + context 'with url_prefixed connection' do + let(:url) { 'http://httpbingo.org/get/' } + + it 'parses url and changes scheme' do + conn.scheme = 'https' + uri = conn.build_exclusive_url('sake.html') + expect(uri.to_s).to eq('https://httpbingo.org/get/sake.html') + end + + it 'joins url to base with ending slash' do + uri = conn.build_exclusive_url('sake.html') + expect(uri.to_s).to eq('http://httpbingo.org/get/sake.html') + end + + it 'used default base with ending slash' do + uri = conn.build_exclusive_url + expect(uri.to_s).to eq('http://httpbingo.org/get/') + end + + it 'overrides base' do + uri = conn.build_exclusive_url('/sake/') + expect(uri.to_s).to eq('http://httpbingo.org/sake/') + end + end + + context 'with colon in path' do + let(:url) { 'http://service.com' } + + it 'joins url to base when used absolute path' do + conn = Faraday.new(url: url) + uri = conn.build_exclusive_url('/service:search?limit=400') + expect(uri.to_s).to eq('http://service.com/service:search?limit=400') + end + + it 'joins url to base when used relative path' do + conn = Faraday.new(url: url) + uri = conn.build_exclusive_url('service:search?limit=400') + expect(uri.to_s).to eq('http://service.com/service%3Asearch?limit=400') + end + + it 'joins url to base when used with path prefix' do + conn = Faraday.new(url: url) + conn.path_prefix = '/api' + uri = conn.build_exclusive_url('service:search?limit=400') + expect(uri.to_s).to eq('http://service.com/api/service%3Asearch?limit=400') + end + end + end + + describe '#build_url' do + let(:url) { 'http://httpbingo.org/nigiri' } + + it 'uses params' do + conn.params = { a: 1, b: 1 } + expect(conn.build_url.to_s).to eq('http://httpbingo.org/nigiri?a=1&b=1') + end + + it 'merges params' do + conn.params = { a: 1, b: 1 } + url = conn.build_url(nil, b: 2, c: 3) + expect(url.to_s).to eq('http://httpbingo.org/nigiri?a=1&b=2&c=3') + end + end + + describe '#build_request' do + let(:url) { 'https://ahttpbingo.org/sake.html' } + let(:request) { conn.build_request(:get) } + + before do + conn.headers = { 'Authorization' => 'token abc123' } + request.headers.delete('Authorization') + end + + it { expect(conn.headers.keys).to eq(['Authorization']) } + it { expect(conn.headers.include?('Authorization')).to be_truthy } + it { expect(request.headers.keys).to be_empty } + it { expect(request.headers.include?('Authorization')).to be_falsey } + end + + describe '#to_env' do + subject { conn.build_request(:get).to_env(conn).url } + + let(:url) { 'http://httpbingo.org/sake.html' } + let(:options) { { params: @params } } + + it 'parses url params into query' do + @params = { 'a[b]' => '1 + 2' } + expect(subject.query).to eq('a%5Bb%5D=1+%2B+2') + end + + it 'escapes per spec' do + @params = { 'a' => '1+2 foo~bar.-baz' } + expect(subject.query).to eq('a=1%2B2+foo~bar.-baz') + end + + it 'bracketizes nested params in query' do + @params = { 'a' => { 'b' => 'c' } } + expect(subject.query).to eq('a%5Bb%5D=c') + end + + it 'bracketizes repeated params in query' do + @params = { 'a' => [1, 2] } + expect(subject.query).to eq('a%5B%5D=1&a%5B%5D=2') + end + + it 'without braketizing repeated params in query' do + @params = { 'a' => [1, 2] } + conn.options.params_encoder = Faraday::FlatParamsEncoder + expect(subject.query).to eq('a=1&a=2') + end + end + + describe 'proxy support' do + it 'accepts string' do + with_env 'http_proxy' => 'http://env-proxy.com:80' do + conn.proxy = 'http://proxy.com' + expect(conn.proxy.host).to eq('proxy.com') + end + end + + it 'accepts uri' do + with_env 'http_proxy' => 'http://env-proxy.com:80' do + conn.proxy = URI.parse('http://proxy.com') + expect(conn.proxy.host).to eq('proxy.com') + end + end + + it 'accepts hash with string uri' do + with_env 'http_proxy' => 'http://env-proxy.com:80' do + conn.proxy = { uri: 'http://proxy.com', user: 'rick' } + expect(conn.proxy.host).to eq('proxy.com') + expect(conn.proxy.user).to eq('rick') + end + end + + it 'accepts hash' do + with_env 'http_proxy' => 'http://env-proxy.com:80' do + conn.proxy = { uri: URI.parse('http://proxy.com'), user: 'rick' } + expect(conn.proxy.host).to eq('proxy.com') + expect(conn.proxy.user).to eq('rick') + end + end + + it 'accepts http env' do + with_env 'http_proxy' => 'http://env-proxy.com:80' do + expect(conn.proxy.host).to eq('env-proxy.com') + end + end + + it 'accepts http env with auth' do + with_env 'http_proxy' => 'http://a%40b:my%20pass@proxy.com:80' do + expect(conn.proxy.user).to eq('a@b') + expect(conn.proxy.password).to eq('my pass') + end + end + + it 'accepts env without scheme' do + with_env 'http_proxy' => 'localhost:8888' do + uri = conn.proxy[:uri] + expect(uri.host).to eq('localhost') + expect(uri.port).to eq(8888) + end + end + + it 'fetches no proxy from nil env' do + with_env 'http_proxy' => nil do + expect(conn.proxy).to be_nil + end + end + + it 'fetches no proxy from blank env' do + with_env 'http_proxy' => '' do + expect(conn.proxy).to be_nil + end + end + + it 'does not accept uppercase env' do + with_env 'HTTP_PROXY' => 'http://localhost:8888/' do + expect(conn.proxy).to be_nil + end + end + + it 'allows when url in no proxy list' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do + conn = Faraday::Connection.new('http://example.com') + expect(conn.proxy).to be_nil + end + end + + it 'allows when url in no proxy list with url_prefix' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do + conn = Faraday::Connection.new + conn.url_prefix = 'http://example.com' + expect(conn.proxy).to be_nil + end + end + + it 'allows when prefixed url is not in no proxy list' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do + conn = Faraday::Connection.new('http://prefixedexample.com') + expect(conn.proxy.host).to eq('proxy.com') + end + end + + it 'allows when subdomain url is in no proxy list' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do + conn = Faraday::Connection.new('http://subdomain.example.com') + expect(conn.proxy).to be_nil + end + end + + it 'allows when url not in no proxy list' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example2.com' do + conn = Faraday::Connection.new('http://example.com') + expect(conn.proxy.host).to eq('proxy.com') + end + end + + it 'allows when ip address is not in no proxy list but url is' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'localhost' do + conn = Faraday::Connection.new('http://127.0.0.1') + expect(conn.proxy).to be_nil + end + end + + it 'allows when url is not in no proxy list but ip address is' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => '127.0.0.1' do + conn = Faraday::Connection.new('http://localhost') + expect(conn.proxy).to be_nil + end + end + + it 'allows in multi element no proxy list' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example0.com,example.com,example1.com' do + expect(Faraday::Connection.new('http://example0.com').proxy).to be_nil + expect(Faraday::Connection.new('http://example.com').proxy).to be_nil + expect(Faraday::Connection.new('http://example1.com').proxy).to be_nil + expect(Faraday::Connection.new('http://example2.com').proxy.host).to eq('proxy.com') + end + end + + it 'test proxy requires uri' do + expect { conn.proxy = { uri: :bad_uri, user: 'rick' } }.to raise_error(ArgumentError) + end + + it 'uses env http_proxy' do + with_env 'http_proxy' => 'http://proxy.com' do + conn = Faraday.new + expect(conn.instance_variable_get(:@manual_proxy)).to be_falsey + expect(conn.proxy_for_request('http://google.co.uk').host).to eq('proxy.com') + end + end + + it 'uses processes no_proxy before http_proxy' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'google.co.uk' do + conn = Faraday.new + expect(conn.instance_variable_get(:@manual_proxy)).to be_falsey + expect(conn.proxy_for_request('http://google.co.uk')).to be_nil + end + end + + it 'uses env https_proxy' do + with_env 'https_proxy' => 'https://proxy.com' do + conn = Faraday.new + expect(conn.instance_variable_get(:@manual_proxy)).to be_falsey + expect(conn.proxy_for_request('https://google.co.uk').host).to eq('proxy.com') + end + end + + it 'uses processes no_proxy before https_proxy' do + with_env 'https_proxy' => 'https://proxy.com', 'no_proxy' => 'google.co.uk' do + conn = Faraday.new + expect(conn.instance_variable_get(:@manual_proxy)).to be_falsey + expect(conn.proxy_for_request('https://google.co.uk')).to be_nil + end + end + + it 'gives priority to manually set proxy' do + with_env 'https_proxy' => 'https://proxy.com', 'no_proxy' => 'google.co.uk' do + conn = Faraday.new + conn.proxy = 'http://proxy2.com' + + expect(conn.instance_variable_get(:@manual_proxy)).to be_truthy + expect(conn.proxy_for_request('https://google.co.uk').host).to eq('proxy2.com') + end + end + + it 'ignores env proxy if set that way' do + with_env_proxy_disabled do + with_env 'http_proxy' => 'http://duncan.proxy.com:80' do + expect(conn.proxy).to be_nil + end + end + end + + context 'performing a request' do + let(:url) { 'http://example.com' } + let(:conn) do + Faraday.new do |f| + f.adapter :test do |stubs| + stubs.get(url) do + [200, {}, 'ok'] + end + end + end + end + + it 'dynamically checks proxy' do + with_env 'http_proxy' => 'http://proxy.com:80' do + expect(conn.proxy.uri.host).to eq('proxy.com') + + conn.get(url) do |req| + expect(req.options.proxy.uri.host).to eq('proxy.com') + end + end + + conn.get(url) + expect(conn.instance_variable_get(:@temp_proxy)).to be_nil + end + + it 'dynamically check no proxy' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do + expect(conn.proxy.uri.host).to eq('proxy.com') + + conn.get('http://example.com') do |req| + expect(req.options.proxy).to be_nil + end + end + end + end + end + + describe '#dup' do + subject { conn.dup } + + let(:url) { 'http://httpbingo.org/foo' } + let(:options) do + { + ssl: { verify: :none }, + headers: { 'content-type' => 'text/plain' }, + params: { 'a' => '1' }, + request: { timeout: 5 } + } + end + + it { expect(subject.build_exclusive_url).to eq(conn.build_exclusive_url) } + it { expect(subject.headers['content-type']).to eq('text/plain') } + it { expect(subject.params['a']).to eq('1') } + + context 'after manual changes' do + before do + subject.headers['content-length'] = 12 + subject.params['b'] = '2' + subject.options[:open_timeout] = 10 + end + + it { expect(subject.builder.handlers.size).to eq(1) } + it { expect(conn.builder.handlers.size).to eq(1) } + it { expect(conn.headers.key?('content-length')).to be_falsey } + it { expect(conn.params.key?('b')).to be_falsey } + it { expect(subject.options[:timeout]).to eq(5) } + it { expect(conn.options[:open_timeout]).to be_nil } + end + end + + describe '#respond_to?' do + it { expect(Faraday.respond_to?(:get)).to be_truthy } + it { expect(Faraday.respond_to?(:post)).to be_truthy } + end + + describe 'default_connection_options' do + context 'assigning a default value' do + before do + Faraday.default_connection_options = nil + Faraday.default_connection_options.request.timeout = 10 + end + + it_behaves_like 'default connection options' + end + + context 'assigning a hash' do + before { Faraday.default_connection_options = { request: { timeout: 10 } } } + + it_behaves_like 'default connection options' + end + + context 'preserving a user_agent assigned via default_conncetion_options' do + around do |example| + old = Faraday.default_connection_options + Faraday.default_connection_options = { headers: { user_agent: 'My Agent 1.2' } } + example.run + Faraday.default_connection_options = old + end + + context 'when url is a Hash' do + let(:conn) { Faraday.new(url: 'http://example.co', headers: { 'CustomHeader' => 'CustomValue' }) } + + it { expect(conn.headers).to eq('CustomHeader' => 'CustomValue', 'User-Agent' => 'My Agent 1.2') } + end + + context 'when url is a String' do + let(:conn) { Faraday.new('http://example.co', headers: { 'CustomHeader' => 'CustomValue' }) } + + it { expect(conn.headers).to eq('CustomHeader' => 'CustomValue', 'User-Agent' => 'My Agent 1.2') } + end + end + end + + describe 'request params' do + context 'with simple url' do + let(:url) { 'http://example.com' } + let(:stubs) { Faraday::Adapter::Test::Stubs.new } + + before do + conn.adapter(:test, stubs) + stubs.get('http://example.com?a=a&p=3') do + [200, {}, 'ok'] + end + end + + after { stubs.verify_stubbed_calls } + + it 'test_overrides_request_params' do + conn.get('?p=2&a=a', p: 3) + end + + it 'test_overrides_request_params_block' do + conn.get('?p=1&a=a', p: 2) do |req| + req.params[:p] = 3 + end + end + + it 'test_overrides_request_params_block_url' do + conn.get(nil, p: 2) do |req| + req.url('?p=1&a=a', 'p' => 3) + end + end + end + + context 'with url and extra params' do + let(:url) { 'http://example.com?a=1&b=2' } + let(:options) { { params: { c: 3 } } } + let(:stubs) { Faraday::Adapter::Test::Stubs.new } + + before do + conn.adapter(:test, stubs) + end + + it 'merges connection and request params' do + expected = 'http://example.com?a=1&b=2&c=3&limit=5&page=1' + stubs.get(expected) { [200, {}, 'ok'] } + conn.get('?page=1', limit: 5) + stubs.verify_stubbed_calls + end + + it 'allows to override all params' do + expected = 'http://example.com?b=b' + stubs.get(expected) { [200, {}, 'ok'] } + conn.get('?p=1&a=a', p: 2) do |req| + expect(req.params[:a]).to eq('a') + expect(req.params['c']).to eq(3) + expect(req.params['p']).to eq(2) + req.params = { b: 'b' } + expect(req.params['b']).to eq('b') + end + stubs.verify_stubbed_calls + end + + it 'allows to set params_encoder for single request' do + encoder = CustomEncoder.new + expected = 'http://example.com/?A-1,B-2,C-3,FEELING-BLUE' + stubs.get(expected) { [200, {}, 'ok'] } + + conn.get('/', a: 1, b: 2, c: 3, feeling: 'blue') do |req| + req.options.params_encoder = encoder + end + stubs.verify_stubbed_calls + end + end + + context 'with default params encoder' do + let(:stubs) { Faraday::Adapter::Test::Stubs.new } + + before do + conn.adapter(:test, stubs) + stubs.get('http://example.com?color%5B%5D=blue&color%5B%5D=red') do + [200, {}, 'ok'] + end + end + + after { stubs.verify_stubbed_calls } + + it 'supports array params in url' do + conn.get('http://example.com?color[]=blue&color[]=red') + end + + it 'supports array params in params' do + conn.get('http://example.com', color: %w[blue red]) + end + end + + context 'with flat params encoder' do + let(:options) { { request: { params_encoder: Faraday::FlatParamsEncoder } } } + let(:stubs) { Faraday::Adapter::Test::Stubs.new } + + before do + conn.adapter(:test, stubs) + stubs.get('http://example.com?color=blue&color=red') do + [200, {}, 'ok'] + end + end + + after { stubs.verify_stubbed_calls } + + it 'supports array params in params' do + conn.get('http://example.com', color: %w[blue red]) + end + + context 'with array param in url' do + let(:url) { 'http://example.com?color[]=blue&color[]=red' } + + it do + conn.get('/') + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/error_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/error_spec.rb new file mode 100644 index 0000000..bea9f0c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/error_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::ClientError do + describe '.initialize' do + subject { described_class.new(exception, response) } + let(:response) { nil } + + context 'with exception only' do + let(:exception) { RuntimeError.new('test') } + + it { expect(subject.wrapped_exception).to eq(exception) } + it { expect(subject.response).to be_nil } + it { expect(subject.message).to eq(exception.message) } + it { expect(subject.backtrace).to eq(exception.backtrace) } + it { expect(subject.inspect).to eq('#>') } + it { expect(subject.response_status).to be_nil } + end + + context 'with response hash' do + let(:exception) { { status: 400 } } + + it { expect(subject.wrapped_exception).to be_nil } + it { expect(subject.response).to eq(exception) } + it { expect(subject.message).to eq('the server responded with status 400') } + it { expect(subject.inspect).to eq('#400}>') } + it { expect(subject.response_status).to eq(400) } + end + + context 'with string' do + let(:exception) { 'custom message' } + + it { expect(subject.wrapped_exception).to be_nil } + it { expect(subject.response).to be_nil } + it { expect(subject.message).to eq('custom message') } + it { expect(subject.inspect).to eq('#>') } + it { expect(subject.response_status).to be_nil } + end + + context 'with anything else #to_s' do + let(:exception) { %w[error1 error2] } + + it { expect(subject.wrapped_exception).to be_nil } + it { expect(subject.response).to be_nil } + it { expect(subject.message).to eq('["error1", "error2"]') } + it { expect(subject.inspect).to eq('#>') } + it { expect(subject.response_status).to be_nil } + end + + context 'with exception string and response hash' do + let(:exception) { 'custom message' } + let(:response) { { status: 400 } } + + it { expect(subject.wrapped_exception).to be_nil } + it { expect(subject.response).to eq(response) } + it { expect(subject.message).to eq('custom message') } + it { expect(subject.inspect).to eq('#400}>') } + it { expect(subject.response_status).to eq(400) } + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/middleware_registry_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/middleware_registry_spec.rb new file mode 100644 index 0000000..a8fa7cc --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/middleware_registry_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::MiddlewareRegistry do + before do + stub_const('CustomMiddleware', custom_middleware_klass) + end + let(:custom_middleware_klass) { Class.new(Faraday::Middleware) } + let(:dummy) { Class.new { extend Faraday::MiddlewareRegistry } } + + after { dummy.unregister_middleware(:custom) } + + it 'allows to register with constant' do + dummy.register_middleware(custom: custom_middleware_klass) + expect(dummy.lookup_middleware(:custom)).to eq(custom_middleware_klass) + end + + it 'allows to register with symbol' do + dummy.register_middleware(custom: :CustomMiddleware) + expect(dummy.lookup_middleware(:custom)).to eq(custom_middleware_klass) + end + + it 'allows to register with string' do + dummy.register_middleware(custom: 'CustomMiddleware') + expect(dummy.lookup_middleware(:custom)).to eq(custom_middleware_klass) + end + + it 'allows to register with Proc' do + dummy.register_middleware(custom: -> { custom_middleware_klass }) + expect(dummy.lookup_middleware(:custom)).to eq(custom_middleware_klass) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/middleware_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/middleware_spec.rb new file mode 100644 index 0000000..f5cc2a1 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/middleware_spec.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Middleware do + subject { described_class.new(app) } + let(:app) { double } + + describe 'options' do + context 'when options are passed to the middleware' do + subject { described_class.new(app, options) } + let(:options) { { field: 'value' } } + + it 'accepts options when initialized' do + expect(subject.options[:field]).to eq('value') + end + end + end + + describe '#on_request' do + subject do + Class.new(described_class) do + def on_request(env) + # do nothing + end + end.new(app) + end + + it 'is called by #call' do + expect(app).to receive(:call).and_return(app) + expect(app).to receive(:on_complete) + is_expected.to receive(:call).and_call_original + is_expected.to receive(:on_request) + subject.call(double) + end + end + + describe '#on_error' do + subject do + Class.new(described_class) do + def on_error(error) + # do nothing + end + end.new(app) + end + + it 'is called by #call' do + expect(app).to receive(:call).and_raise(Faraday::ConnectionFailed) + is_expected.to receive(:call).and_call_original + is_expected.to receive(:on_error) + + expect { subject.call(double) }.to raise_error(Faraday::ConnectionFailed) + end + end + + describe '#close' do + context "with app that doesn't support \#close" do + it 'should issue warning' do + is_expected.to receive(:warn) + subject.close + end + end + + context "with app that supports \#close" do + it 'should issue warning' do + expect(app).to receive(:close) + is_expected.to_not receive(:warn) + subject.close + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/options/env_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/options/env_spec.rb new file mode 100644 index 0000000..006bd5f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/options/env_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Env do + subject(:env) { described_class.new } + + it 'allows to access members' do + expect(env.method).to be_nil + env.method = :get + expect(env.method).to eq(:get) + end + + it 'allows to access symbol non members' do + expect(env[:custom]).to be_nil + env[:custom] = :boom + expect(env[:custom]).to eq(:boom) + end + + it 'allows to access string non members' do + expect(env['custom']).to be_nil + env['custom'] = :boom + expect(env['custom']).to eq(:boom) + end + + it 'ignores false when fetching' do + ssl = Faraday::SSLOptions.new + ssl.verify = false + expect(ssl.fetch(:verify, true)).to be_falsey + end + + it 'handle verify_hostname when fetching' do + ssl = Faraday::SSLOptions.new + ssl.verify_hostname = true + expect(ssl.fetch(:verify_hostname, false)).to be_truthy + end + + it 'retains custom members' do + env[:foo] = 'custom 1' + env[:bar] = :custom2 + env2 = Faraday::Env.from(env) + env2[:baz] = 'custom 3' + + expect(env2[:foo]).to eq('custom 1') + expect(env2[:bar]).to eq(:custom2) + expect(env[:baz]).to be_nil + end + + describe '#body' do + subject(:env) { described_class.from(body: { foo: 'bar' }) } + + context 'when response is not finished yet' do + it 'returns the request body' do + expect(env.body).to eq(foo: 'bar') + end + end + + context 'when response is finished' do + before do + env.status = 200 + env.body = { bar: 'foo' } + env.response = Faraday::Response.new(env) + end + + it 'returns the response body' do + expect(env.body).to eq(bar: 'foo') + end + + it 'allows to access request_body' do + expect(env.request_body).to eq(foo: 'bar') + end + + it 'allows to access response_body' do + expect(env.response_body).to eq(bar: 'foo') + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/options/options_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/options/options_spec.rb new file mode 100644 index 0000000..9758ecc --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/options/options_spec.rb @@ -0,0 +1,297 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Options do + SubOptions = Class.new(Faraday::Options.new(:sub_a, :sub_b)) + class ParentOptions < Faraday::Options.new(:a, :b, :c) + options c: SubOptions + end + + describe '#merge' do + it 'merges options with hashes' do + options = ParentOptions.new(1) + expect(options.a).to eq(1) + expect(options.b).to be_nil + + dup = options.merge a: 2, b: 3 + expect(dup.a).to eq(2) + expect(dup.b).to eq(3) + expect(options.a).to eq(1) + expect(options.b).to be_nil + end + + it 'deeply merges two options' do + sub_opts1 = SubOptions.from(sub_a: 3) + sub_opts2 = SubOptions.from(sub_b: 4) + opt1 = ParentOptions.from(a: 1, c: sub_opts1) + opt2 = ParentOptions.from(b: 2, c: sub_opts2) + + merged = opt1.merge(opt2) + + expected_sub_opts = SubOptions.from(sub_a: 3, sub_b: 4) + expected = ParentOptions.from(a: 1, b: 2, c: expected_sub_opts) + expect(merged).to eq(expected) + end + + it 'deeply merges options with hashes' do + sub_opts1 = SubOptions.from(sub_a: 3) + sub_opts2 = { sub_b: 4 } + opt1 = ParentOptions.from(a: 1, c: sub_opts1) + opt2 = { b: 2, c: sub_opts2 } + + merged = opt1.merge(opt2) + + expected_sub_opts = SubOptions.from(sub_a: 3, sub_b: 4) + expected = ParentOptions.from(a: 1, b: 2, c: expected_sub_opts) + expect(merged).to eq(expected) + end + + it 'deeply merges options with nil' do + sub_opts = SubOptions.new(3, 4) + options = ParentOptions.new(1, 2, sub_opts) + expect(options.a).to eq(1) + expect(options.b).to eq(2) + expect(options.c.sub_a).to eq(3) + expect(options.c.sub_b).to eq(4) + + options2 = ParentOptions.from(b: 5, c: nil) + + merged = options.merge(options2) + + expect(merged.b).to eq(5) + expect(merged.c).to eq(sub_opts) + end + + it 'deeply merges options with options having nil sub-options' do + options = ParentOptions.from(a: 1) + + sub_opts = SubOptions.new(3, 4) + options2 = ParentOptions.from(b: 2, c: sub_opts) + + expect(options.a).to eq(1) + expect(options2.b).to eq(2) + expect(options2.c.sub_a).to eq(3) + expect(options2.c.sub_b).to eq(4) + + merged = options.merge(options2) + + expect(merged.c).to eq(sub_opts) + end + + describe '#dup' do + it 'duplicate options but not sub-options' do + sub_opts = SubOptions.from(sub_a: 3) + opts = ParentOptions.from(b: 1, c: sub_opts) + + duped = opts.dup + duped.b = 2 + duped.c.sub_a = 4 + + expect(opts.b).to eq(1) + expect(opts.c.sub_a).to eq(4) + end + end + + describe '#deep_dup' do + it 'duplicate options and also suboptions' do + sub_opts = SubOptions.from(sub_a: 3) + opts = ParentOptions.from(b: 1, c: sub_opts) + + duped = opts.deep_dup + duped.b = 2 + duped.c.sub_a = 4 + + expect(opts.b).to eq(1) + expect(opts.c.sub_a).to eq(3) + end + end + + describe '#clear' do + it 'clears the options' do + options = SubOptions.new(1) + expect(options.empty?).not_to be_truthy + options.clear + expect(options.empty?).to be_truthy + end + end + + describe '#empty?' do + it 'returns true only if all options are nil' do + options = SubOptions.new + expect(options.empty?).to be_truthy + options.sub_a = 1 + expect(options.empty?).not_to be_truthy + options.delete(:sub_a) + expect(options.empty?).to be_truthy + end + end + + describe '#each_key' do + it 'allows to iterate through keys' do + options = ParentOptions.new(1, 2, 3) + enum = options.each_key + expect(enum.next.to_sym).to eq(:a) + expect(enum.next.to_sym).to eq(:b) + expect(enum.next.to_sym).to eq(:c) + end + end + + describe '#key?' do + it 'returns true if the key exists and is not nil' do + options = SubOptions.new + expect(options.key?(:sub_a)).not_to be_truthy + options.sub_a = 1 + expect(options.key?(:sub_a)).to be_truthy + end + end + + describe '#each_value' do + it 'allows to iterate through values' do + options = ParentOptions.new(1, 2, 3) + enum = options.each_value + expect(enum.next).to eq(1) + expect(enum.next).to eq(2) + expect(enum.next).to eq(3) + end + end + + describe '#value?' do + it 'returns true if any key has that value' do + options = SubOptions.new + expect(options.value?(1)).not_to be_truthy + options.sub_a = 1 + expect(options.value?(1)).to be_truthy + end + end + + describe '#update' do + it 'updates options from hashes' do + options = ParentOptions.new(1) + expect(options.a).to eq(1) + expect(options.b).to be_nil + + updated = options.update a: 2, b: 3 + expect(options.a).to eq(2) + expect(options.b).to eq(3) + expect(updated).to eq(options) + end + end + + describe '#delete' do + it 'allows to remove value for key' do + options = ParentOptions.new(1) + expect(options.a).to eq(1) + expect(options.delete(:a)).to eq(1) + expect(options.a).to be_nil + end + end + + describe '#from' do + it { expect { ParentOptions.from invalid: 1 }.to raise_error(NoMethodError) } + + it 'works with options' do + options = ParentOptions.new(1) + + value = ParentOptions.from(options) + expect(value.a).to eq(1) + expect(value.b).to be_nil + end + + it 'works with options with sub object' do + sub = SubOptions.new(1) + options = ParentOptions.from a: 1, c: sub + expect(options).to be_a_kind_of(ParentOptions) + expect(options.a).to eq(1) + expect(options.b).to be_nil + expect(options.c).to be_a_kind_of(SubOptions) + expect(options.c.sub_a).to eq(1) + end + + it 'works with hash' do + options = ParentOptions.from a: 1 + expect(options).to be_a_kind_of(ParentOptions) + expect(options.a).to eq(1) + expect(options.b).to be_nil + end + + it 'works with hash with sub object' do + options = ParentOptions.from a: 1, c: { sub_a: 1 } + expect(options).to be_a_kind_of(ParentOptions) + expect(options.a).to eq(1) + expect(options.b).to be_nil + expect(options.c).to be_a_kind_of(SubOptions) + expect(options.c.sub_a).to eq(1) + end + + it 'works with deep hash' do + hash = { b: 1 } + options = ParentOptions.from a: hash + expect(options.a[:b]).to eq(1) + + hash[:b] = 2 + expect(options.a[:b]).to eq(1) + + options.a[:b] = 3 + expect(hash[:b]).to eq(2) + expect(options.a[:b]).to eq(3) + end + + it 'works with nil' do + options = ParentOptions.from(nil) + expect(options).to be_a_kind_of(ParentOptions) + expect(options.a).to be_nil + expect(options.b).to be_nil + end + + it 'respects inheritance' do + subclass = Class.new(ParentOptions) + options = subclass.from(c: { sub_a: 'hello' }) + expect(options.c).to be_a_kind_of(SubOptions) + expect(options.c.sub_a).to eq('hello') + end + end + + describe '#memoized' do + subject(:options_class) { Class.new(ParentOptions) } + it 'requires block' do + expect { options_class.memoized(:a) }.to raise_error(ArgumentError) + end + + it 'accepts block' do + options_class.memoized(:a) { :foo } + expect(options_class.new.a).to eql(:foo) + end + end + + describe '#fetch' do + subject { SubOptions.new } + + context 'when the fetched key has no value' do + it 'uses falsey default' do + expect(subject.fetch(:sub_a, false) { |_| :blah }).to be_falsey + end + + it 'accepts block' do + expect(subject.fetch(:sub_a) { |k| "yo #{k.inspect}" }).to eq('yo :sub_a') + end + + it 'needs a default if key is missing' do + expect { subject.fetch(:sub_a) }.to raise_error(Faraday::Options.fetch_error_class) + end + end + + context 'when the fetched key has a value' do + before do + subject.sub_a = 1 + end + + it 'grabs value' do + expect(subject.fetch(:sub_a, false) { |_| :blah }).to eq(1) + end + + it 'works with key' do + expect(subject.fetch(:sub_a)).to eq(1) + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/options/proxy_options_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/options/proxy_options_spec.rb new file mode 100644 index 0000000..7951554 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/options/proxy_options_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::ProxyOptions do + describe '#from' do + it 'works with string' do + options = Faraday::ProxyOptions.from 'http://user:pass@example.org' + expect(options.user).to eq('user') + expect(options.password).to eq('pass') + expect(options.uri).to be_a_kind_of(URI) + expect(options.path).to eq('') + expect(options.port).to eq(80) + expect(options.host).to eq('example.org') + expect(options.scheme).to eq('http') + expect(options.inspect).to match('#') + end + + it 'works with no auth' do + proxy = Faraday::ProxyOptions.from 'http://example.org' + expect(proxy.user).to be_nil + expect(proxy.password).to be_nil + end + end + + it 'allows hash access' do + proxy = Faraday::ProxyOptions.from 'http://a%40b:pw%20d@example.org' + expect(proxy.user).to eq('a@b') + expect(proxy[:user]).to eq('a@b') + expect(proxy.password).to eq('pw d') + expect(proxy[:password]).to eq('pw d') + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/options/request_options_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/options/request_options_spec.rb new file mode 100644 index 0000000..8c1bb99 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/options/request_options_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::RequestOptions do + subject(:options) { Faraday::RequestOptions.new } + + it 'allows to set the request proxy' do + expect(options.proxy).to be_nil + + expect { options[:proxy] = { booya: 1 } }.to raise_error(NoMethodError) + + options[:proxy] = { user: 'user' } + expect(options.proxy).to be_a_kind_of(Faraday::ProxyOptions) + expect(options.proxy.user).to eq('user') + + options.proxy = nil + expect(options.proxy).to be_nil + expect(options.inspect).to eq('#') + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/params_encoders/flat_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/params_encoders/flat_spec.rb new file mode 100644 index 0000000..115342e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/params_encoders/flat_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'rack/utils' + +RSpec.describe Faraday::FlatParamsEncoder do + it_behaves_like 'a params encoder' + + it 'decodes arrays' do + query = 'a=one&a=two&a=three' + expected = { 'a' => %w[one two three] } + expect(subject.decode(query)).to eq(expected) + end + + it 'decodes boolean values' do + query = 'a=true&b=false' + expected = { 'a' => 'true', 'b' => 'false' } + expect(subject.decode(query)).to eq(expected) + end + + it 'encodes boolean values' do + params = { a: true, b: false } + expect(subject.encode(params)).to eq('a=true&b=false') + end + + it 'encodes boolean values in array' do + params = { a: [true, false] } + expect(subject.encode(params)).to eq('a=true&a=false') + end + + it 'encodes empty array in hash' do + params = { a: [] } + expect(subject.encode(params)).to eq('a=') + end + + it 'encodes unsorted when asked' do + params = { b: false, a: true } + expect(subject.encode(params)).to eq('a=true&b=false') + Faraday::FlatParamsEncoder.sort_params = false + expect(subject.encode(params)).to eq('b=false&a=true') + Faraday::FlatParamsEncoder.sort_params = true + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/params_encoders/nested_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/params_encoders/nested_spec.rb new file mode 100644 index 0000000..4631204 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/params_encoders/nested_spec.rb @@ -0,0 +1,150 @@ +# frozen_string_literal: true + +require 'rack/utils' + +RSpec.describe Faraday::NestedParamsEncoder do + it_behaves_like 'a params encoder' + + it 'decodes arrays' do + query = 'a[1]=one&a[2]=two&a[3]=three' + expected = { 'a' => %w[one two three] } + expect(subject.decode(query)).to eq(expected) + end + + it 'decodes hashes' do + query = 'a[b1]=one&a[b2]=two&a[b][c]=foo' + expected = { 'a' => { 'b1' => 'one', 'b2' => 'two', 'b' => { 'c' => 'foo' } } } + expect(subject.decode(query)).to eq(expected) + end + + it 'decodes nested arrays rack compat' do + query = 'a[][one]=1&a[][two]=2&a[][one]=3&a[][two]=4' + expected = Rack::Utils.parse_nested_query(query) + expect(subject.decode(query)).to eq(expected) + end + + it 'decodes nested array mixed types' do + query = 'a[][one]=1&a[]=2&a[]=&a[]' + expected = Rack::Utils.parse_nested_query(query) + expect(subject.decode(query)).to eq(expected) + end + + it 'decodes nested ignores invalid array' do + query = '[][a]=1&b=2' + expected = { 'a' => '1', 'b' => '2' } + expect(subject.decode(query)).to eq(expected) + end + + it 'decodes nested ignores repeated array notation' do + query = 'a[][][]=1' + expected = { 'a' => ['1'] } + expect(subject.decode(query)).to eq(expected) + end + + it 'decodes nested ignores malformed keys' do + query = '=1&[]=2' + expected = {} + expect(subject.decode(query)).to eq(expected) + end + + it 'decodes nested subkeys dont have to be in brackets' do + query = 'a[b]c[d]e=1' + expected = { 'a' => { 'b' => { 'c' => { 'd' => { 'e' => '1' } } } } } + expect(subject.decode(query)).to eq(expected) + end + + it 'decodes nested final value overrides any type' do + query = 'a[b][c]=1&a[b]=2' + expected = { 'a' => { 'b' => '2' } } + expect(subject.decode(query)).to eq(expected) + end + + it 'encodes rack compat' do + params = { a: [{ one: '1', two: '2' }, '3', ''] } + result = Faraday::Utils.unescape(Faraday::NestedParamsEncoder.encode(params)).split('&') + expected = Rack::Utils.build_nested_query(params).split('&') + expect(result).to match_array(expected) + end + + it 'encodes empty string array value' do + expected = 'baz=&foo%5Bbar%5D=' + result = Faraday::NestedParamsEncoder.encode(foo: { bar: '' }, baz: '') + expect(result).to eq(expected) + end + + it 'encodes nil array value' do + expected = 'baz&foo%5Bbar%5D' + result = Faraday::NestedParamsEncoder.encode(foo: { bar: nil }, baz: nil) + expect(result).to eq(expected) + end + + it 'encodes empty array value' do + expected = 'baz%5B%5D&foo%5Bbar%5D%5B%5D' + result = Faraday::NestedParamsEncoder.encode(foo: { bar: [] }, baz: []) + expect(result).to eq(expected) + end + + it 'encodes boolean values' do + params = { a: true, b: false } + expect(subject.encode(params)).to eq('a=true&b=false') + end + + it 'encodes boolean values in array' do + params = { a: [true, false] } + expect(subject.encode(params)).to eq('a%5B%5D=true&a%5B%5D=false') + end + + it 'encodes unsorted when asked' do + params = { b: false, a: true } + expect(subject.encode(params)).to eq('a=true&b=false') + Faraday::NestedParamsEncoder.sort_params = false + expect(subject.encode(params)).to eq('b=false&a=true') + Faraday::NestedParamsEncoder.sort_params = true + end + + it 'encodes arrays indices when asked' do + params = { a: [0, 1, 2] } + expect(subject.encode(params)).to eq('a%5B%5D=0&a%5B%5D=1&a%5B%5D=2') + Faraday::NestedParamsEncoder.array_indices = true + expect(subject.encode(params)).to eq('a%5B0%5D=0&a%5B1%5D=1&a%5B2%5D=2') + Faraday::NestedParamsEncoder.array_indices = false + end + + shared_examples 'a wrong decoding' do + it do + expect { subject.decode(query) }.to raise_error(TypeError) do |e| + expect(e.message).to eq(error_message) + end + end + end + + context 'when expecting hash but getting string' do + let(:query) { 'a=1&a[b]=2' } + let(:error_message) { "expected Hash (got String) for param `a'" } + it_behaves_like 'a wrong decoding' + end + + context 'when expecting hash but getting array' do + let(:query) { 'a[]=1&a[b]=2' } + let(:error_message) { "expected Hash (got Array) for param `a'" } + it_behaves_like 'a wrong decoding' + end + + context 'when expecting nested hash but getting non nested' do + let(:query) { 'a[b]=1&a[b][c]=2' } + let(:error_message) { "expected Hash (got String) for param `b'" } + it_behaves_like 'a wrong decoding' + end + + context 'when expecting array but getting hash' do + let(:query) { 'a[b]=1&a[]=2' } + let(:error_message) { "expected Array (got Hash) for param `a'" } + it_behaves_like 'a wrong decoding' + end + + context 'when expecting array but getting string' do + let(:query) { 'a=1&a[]=2' } + let(:error_message) { "expected Array (got String) for param `a'" } + it_behaves_like 'a wrong decoding' + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/rack_builder_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/rack_builder_spec.rb new file mode 100644 index 0000000..89f17ca --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/rack_builder_spec.rb @@ -0,0 +1,317 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::RackBuilder do + # mock handler classes + (Handler = Struct.new(:app)).class_eval do + def call(env) + env[:request_headers]['X-Middleware'] ||= '' + env[:request_headers]['X-Middleware'] += ":#{self.class.name.split('::').last}" + app.call(env) + end + end + + class Apple < Handler + end + + class Orange < Handler + end + + class Banana < Handler + end + + subject { conn.builder } + before { Faraday.default_adapter = :test } + after { Faraday.default_adapter = nil } + + context 'with default stack' do + let(:conn) { Faraday::Connection.new } + + it { expect(subject[0]).to eq(Faraday::Request.lookup_middleware(:url_encoded)) } + it { expect(subject.adapter).to eq(Faraday::Adapter.lookup_middleware(Faraday.default_adapter)) } + end + + context 'with custom empty block' do + let(:conn) { Faraday::Connection.new {} } + + it { expect(subject[0]).to be_nil } + it { expect(subject.adapter).to eq(Faraday::Adapter.lookup_middleware(Faraday.default_adapter)) } + end + + context 'with custom adapter only' do + let(:conn) do + Faraday::Connection.new do |builder| + builder.adapter :test do |stub| + stub.get('/') { |_| [200, {}, ''] } + end + end + end + + it { expect(subject[0]).to be_nil } + it { expect(subject.adapter).to eq(Faraday::Adapter.lookup_middleware(:test)) } + end + + context 'with custom handler and adapter' do + let(:conn) do + Faraday::Connection.new do |builder| + builder.use Apple + builder.adapter :test do |stub| + stub.get('/') { |_| [200, {}, ''] } + end + end + end + + it 'locks the stack after making a request' do + expect(subject.locked?).to be_falsey + conn.get('/') + expect(subject.locked?).to be_truthy + expect { subject.use(Orange) }.to raise_error(Faraday::RackBuilder::StackLocked) + end + + it 'dup stack is unlocked' do + expect(subject.locked?).to be_falsey + subject.lock! + expect(subject.locked?).to be_truthy + dup = subject.dup + expect(dup).to eq(subject) + expect(dup.locked?).to be_falsey + end + + it 'allows to compare handlers' do + expect(subject.handlers.first).to eq(Faraday::RackBuilder::Handler.new(Apple)) + end + end + + context 'when having a single handler' do + let(:conn) { Faraday::Connection.new {} } + + before { subject.use(Apple) } + + it { expect(subject.handlers).to eq([Apple]) } + + it 'allows use' do + subject.use(Orange) + expect(subject.handlers).to eq([Apple, Orange]) + end + + it 'allows insert_before' do + subject.insert_before(Apple, Orange) + expect(subject.handlers).to eq([Orange, Apple]) + end + + it 'allows insert_after' do + subject.insert_after(Apple, Orange) + expect(subject.handlers).to eq([Apple, Orange]) + end + + it 'raises an error trying to use an unregistered symbol' do + expect { subject.use(:apple) }.to raise_error(Faraday::Error) do |err| + expect(err.message).to eq(':apple is not registered on Faraday::Middleware') + end + end + end + + context 'when having two handlers' do + let(:conn) { Faraday::Connection.new {} } + + before do + subject.use(Apple) + subject.use(Orange) + end + + it 'allows insert_before' do + subject.insert_before(Orange, Banana) + expect(subject.handlers).to eq([Apple, Banana, Orange]) + end + + it 'allows insert_after' do + subject.insert_after(Apple, Banana) + expect(subject.handlers).to eq([Apple, Banana, Orange]) + end + + it 'allows to swap handlers' do + subject.swap(Apple, Banana) + expect(subject.handlers).to eq([Banana, Orange]) + end + + it 'allows to delete a handler' do + subject.delete(Apple) + expect(subject.handlers).to eq([Orange]) + end + end + + context 'when adapter is added with named options' do + after { Faraday.default_adapter_options = {} } + let(:conn) { Faraday::Connection.new {} } + + let(:cat_adapter) do + Class.new(Faraday::Adapter) do + attr_accessor :name + + def initialize(app, name:) + super(app) + @name = name + end + end + end + + let(:cat) { subject.adapter.build } + + it 'adds a handler to construct adapter with named options' do + Faraday.default_adapter = cat_adapter + Faraday.default_adapter_options = { name: 'Chloe' } + expect { cat }.to_not output( + /warning: Using the last argument as keyword parameters is deprecated/ + ).to_stderr + expect(cat.name).to eq 'Chloe' + end + end + + context 'when middleware is added with named arguments' do + let(:conn) { Faraday::Connection.new {} } + + let(:dog_middleware) do + Class.new(Faraday::Middleware) do + attr_accessor :name + + def initialize(app, name:) + super(app) + @name = name + end + end + end + let(:dog) do + subject.handlers.find { |handler| handler == dog_middleware }.build + end + + it 'adds a handler to construct middleware with options passed to use' do + subject.use dog_middleware, name: 'Rex' + expect { dog }.to_not output( + /warning: Using the last argument as keyword parameters is deprecated/ + ).to_stderr + expect(dog.name).to eq('Rex') + end + end + + context 'when a middleware is added with named arguments' do + let(:conn) { Faraday::Connection.new {} } + + let(:cat_request) do + Class.new(Faraday::Middleware) do + attr_accessor :name + + def initialize(app, name:) + super(app) + @name = name + end + end + end + let(:cat) do + subject.handlers.find { |handler| handler == cat_request }.build + end + + it 'adds a handler to construct request adapter with options passed to request' do + Faraday::Request.register_middleware cat_request: cat_request + subject.request :cat_request, name: 'Felix' + expect { cat }.to_not output( + /warning: Using the last argument as keyword parameters is deprecated/ + ).to_stderr + expect(cat.name).to eq('Felix') + end + end + + context 'when a middleware is added with named arguments' do + let(:conn) { Faraday::Connection.new {} } + + let(:fish_response) do + Class.new(Faraday::Middleware) do + attr_accessor :name + + def initialize(app, name:) + super(app) + @name = name + end + end + end + let(:fish) do + subject.handlers.find { |handler| handler == fish_response }.build + end + + it 'adds a handler to construct response adapter with options passed to response' do + Faraday::Response.register_middleware fish_response: fish_response + subject.response :fish_response, name: 'Bubbles' + expect { fish }.to_not output( + /warning: Using the last argument as keyword parameters is deprecated/ + ).to_stderr + expect(fish.name).to eq('Bubbles') + end + end + + context 'when a plain adapter is added with named arguments' do + let(:conn) { Faraday::Connection.new {} } + + let(:rabbit_adapter) do + Class.new(Faraday::Adapter) do + attr_accessor :name + + def initialize(app, name:) + super(app) + @name = name + end + end + end + let(:rabbit) do + subject.adapter.build + end + + it 'adds a handler to construct adapter with options passed to adapter' do + Faraday::Adapter.register_middleware rabbit_adapter: rabbit_adapter + subject.adapter :rabbit_adapter, name: 'Thumper' + expect { rabbit }.to_not output( + /warning: Using the last argument as keyword parameters is deprecated/ + ).to_stderr + expect(rabbit.name).to eq('Thumper') + end + end + + context 'when handlers are directly added or updated' do + let(:conn) { Faraday::Connection.new {} } + + let(:rock_handler) do + Class.new do + attr_accessor :name + + def initialize(_app, name:) + @name = name + end + end + end + let(:rock) do + subject.handlers.find { |handler| handler == rock_handler }.build + end + + it 'adds a handler to construct adapter with options passed to insert' do + subject.insert 0, rock_handler, name: 'Stony' + expect { rock }.to_not output( + /warning: Using the last argument as keyword parameters is deprecated/ + ).to_stderr + expect(rock.name).to eq('Stony') + end + + it 'adds a handler with options passed to insert_after' do + subject.insert_after 0, rock_handler, name: 'Rocky' + expect { rock }.to_not output( + /warning: Using the last argument as keyword parameters is deprecated/ + ).to_stderr + expect(rock.name).to eq('Rocky') + end + + it 'adds a handler with options passed to swap' do + subject.insert 0, rock_handler, name: 'Flint' + subject.swap 0, rock_handler, name: 'Chert' + expect { rock }.to_not output( + /warning: Using the last argument as keyword parameters is deprecated/ + ).to_stderr + expect(rock.name).to eq('Chert') + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/request/authorization_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/request/authorization_spec.rb new file mode 100644 index 0000000..437c88a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/request/authorization_spec.rb @@ -0,0 +1,118 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Request::Authorization do + let(:conn) do + Faraday.new do |b| + b.request :authorization, auth_type, *auth_config + b.adapter :test do |stub| + stub.get('/auth-echo') do |env| + [200, {}, env[:request_headers]['Authorization']] + end + end + end + end + + shared_examples 'does not interfere with existing authentication' do + context 'and request already has an authentication header' do + let(:response) { conn.get('/auth-echo', nil, authorization: 'OAuth oauth_token') } + + it 'does not interfere with existing authorization' do + expect(response.body).to eq('OAuth oauth_token') + end + end + end + + let(:response) { conn.get('/auth-echo') } + + describe 'basic_auth' do + let(:auth_type) { :basic } + + context 'when passed correct params' do + let(:auth_config) { %w[aladdin opensesame] } + + it { expect(response.body).to eq('Basic YWxhZGRpbjpvcGVuc2VzYW1l') } + + include_examples 'does not interfere with existing authentication' + end + + context 'when passed very long values' do + let(:auth_config) { ['A' * 255, ''] } + + it { expect(response.body).to eq("Basic #{'QUFB' * 85}Og==") } + + include_examples 'does not interfere with existing authentication' + end + end + + describe 'authorization' do + let(:auth_type) { :Bearer } + + context 'when passed a string' do + let(:auth_config) { ['custom'] } + + it { expect(response.body).to eq('Bearer custom') } + + include_examples 'does not interfere with existing authentication' + end + + context 'when passed a proc' do + let(:auth_config) { [-> { 'custom_from_proc' }] } + + it { expect(response.body).to eq('Bearer custom_from_proc') } + + include_examples 'does not interfere with existing authentication' + end + + context 'when passed a callable' do + let(:callable) { double('Callable Authorizer', call: 'custom_from_callable') } + let(:auth_config) { [callable] } + + it { expect(response.body).to eq('Bearer custom_from_callable') } + + include_examples 'does not interfere with existing authentication' + end + + context 'with an argument' do + let(:response) { conn.get('/auth-echo', nil, 'middle' => 'crunchy surprise') } + + context 'when passed a proc' do + let(:auth_config) { [proc { |env| "proc #{env.request_headers['middle']}" }] } + + it { expect(response.body).to eq('Bearer proc crunchy surprise') } + + include_examples 'does not interfere with existing authentication' + end + + context 'when passed a lambda' do + let(:auth_config) { [->(env) { "lambda #{env.request_headers['middle']}" }] } + + it { expect(response.body).to eq('Bearer lambda crunchy surprise') } + + include_examples 'does not interfere with existing authentication' + end + + context 'when passed a callable with an argument' do + let(:callable) do + Class.new do + def call(env) + "callable #{env.request_headers['middle']}" + end + end.new + end + let(:auth_config) { [callable] } + + it { expect(response.body).to eq('Bearer callable crunchy surprise') } + + include_examples 'does not interfere with existing authentication' + end + end + + context 'when passed too many arguments' do + let(:auth_config) { %w[baz foo] } + + it { expect { response }.to raise_error(ArgumentError) } + + include_examples 'does not interfere with existing authentication' + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/request/instrumentation_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/request/instrumentation_spec.rb new file mode 100644 index 0000000..d207c55 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/request/instrumentation_spec.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Request::Instrumentation do + class FakeInstrumenter + attr_reader :instrumentations + + def initialize + @instrumentations = [] + end + + def instrument(name, env) + @instrumentations << [name, env] + yield + end + end + + let(:config) { {} } + let(:options) { Faraday::Request::Instrumentation::Options.from config } + let(:instrumenter) { FakeInstrumenter.new } + let(:conn) do + Faraday.new do |f| + f.request :instrumentation, config.merge(instrumenter: instrumenter) + f.adapter :test do |stub| + stub.get '/' do + [200, {}, 'ok'] + end + end + end + end + + it { expect(options.name).to eq('request.faraday') } + it 'defaults to ActiveSupport::Notifications' do + res = options.instrumenter + rescue NameError => e + expect(e.to_s).to match('ActiveSupport') + else + expect(res).to eq(ActiveSupport::Notifications) + end + + it 'instruments with default name' do + expect(instrumenter.instrumentations.size).to eq(0) + + res = conn.get '/' + expect(res.body).to eq('ok') + expect(instrumenter.instrumentations.size).to eq(1) + + name, env = instrumenter.instrumentations.first + expect(name).to eq('request.faraday') + expect(env[:url].path).to eq('/') + end + + context 'with custom name' do + let(:config) { { name: 'custom' } } + + it { expect(options.name).to eq('custom') } + it 'instruments with custom name' do + expect(instrumenter.instrumentations.size).to eq(0) + + res = conn.get '/' + expect(res.body).to eq('ok') + expect(instrumenter.instrumentations.size).to eq(1) + + name, env = instrumenter.instrumentations.first + expect(name).to eq('custom') + expect(env[:url].path).to eq('/') + end + end + + context 'with custom instrumenter' do + let(:config) { { instrumenter: :custom } } + + it { expect(options.instrumenter).to eq(:custom) } + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/request/json_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/request/json_spec.rb new file mode 100644 index 0000000..89949bc --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/request/json_spec.rb @@ -0,0 +1,111 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Request::Json do + let(:middleware) { described_class.new(->(env) { Faraday::Response.new(env) }) } + + def process(body, content_type = nil) + env = { body: body, request_headers: Faraday::Utils::Headers.new } + env[:request_headers]['content-type'] = content_type if content_type + middleware.call(Faraday::Env.from(env)).env + end + + def result_body + result[:body] + end + + def result_type + result[:request_headers]['content-type'] + end + + context 'no body' do + let(:result) { process(nil) } + + it "doesn't change body" do + expect(result_body).to be_nil + end + + it "doesn't add content type" do + expect(result_type).to be_nil + end + end + + context 'empty body' do + let(:result) { process('') } + + it "doesn't change body" do + expect(result_body).to be_empty + end + + it "doesn't add content type" do + expect(result_type).to be_nil + end + end + + context 'string body' do + let(:result) { process('{"a":1}') } + + it "doesn't change body" do + expect(result_body).to eq('{"a":1}') + end + + it 'adds content type' do + expect(result_type).to eq('application/json') + end + end + + context 'object body' do + let(:result) { process(a: 1) } + + it 'encodes body' do + expect(result_body).to eq('{"a":1}') + end + + it 'adds content type' do + expect(result_type).to eq('application/json') + end + end + + context 'empty object body' do + let(:result) { process({}) } + + it 'encodes body' do + expect(result_body).to eq('{}') + end + end + + context 'object body with json type' do + let(:result) { process({ a: 1 }, 'application/json; charset=utf-8') } + + it 'encodes body' do + expect(result_body).to eq('{"a":1}') + end + + it "doesn't change content type" do + expect(result_type).to eq('application/json; charset=utf-8') + end + end + + context 'object body with vendor json type' do + let(:result) { process({ a: 1 }, 'application/vnd.myapp.v1+json; charset=utf-8') } + + it 'encodes body' do + expect(result_body).to eq('{"a":1}') + end + + it "doesn't change content type" do + expect(result_type).to eq('application/vnd.myapp.v1+json; charset=utf-8') + end + end + + context 'object body with incompatible type' do + let(:result) { process({ a: 1 }, 'application/xml; charset=utf-8') } + + it "doesn't change body" do + expect(result_body).to eq(a: 1) + end + + it "doesn't change content type" do + expect(result_type).to eq('application/xml; charset=utf-8') + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/request/url_encoded_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/request/url_encoded_spec.rb new file mode 100644 index 0000000..bdd9e0a --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/request/url_encoded_spec.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +require 'stringio' + +RSpec.describe Faraday::Request::UrlEncoded do + let(:conn) do + Faraday.new do |b| + b.request :url_encoded + b.adapter :test do |stub| + stub.post('/echo') do |env| + posted_as = env[:request_headers]['Content-Type'] + body = env[:body] + if body.respond_to?(:read) + body = body.read + end + [200, { 'Content-Type' => posted_as }, body] + end + end + end + end + + it 'does nothing without payload' do + response = conn.post('/echo') + expect(response.headers['Content-Type']).to be_nil + expect(response.body.empty?).to be_truthy + end + + it 'ignores custom content type' do + response = conn.post('/echo', { some: 'data' }, 'content-type' => 'application/x-foo') + expect(response.headers['Content-Type']).to eq('application/x-foo') + expect(response.body).to eq(some: 'data') + end + + it 'works with no headers' do + response = conn.post('/echo', fruit: %w[apples oranges]) + expect(response.headers['Content-Type']).to eq('application/x-www-form-urlencoded') + expect(response.body).to eq('fruit%5B%5D=apples&fruit%5B%5D=oranges') + end + + it 'works with with headers' do + response = conn.post('/echo', { 'a' => 123 }, 'content-type' => 'application/x-www-form-urlencoded') + expect(response.headers['Content-Type']).to eq('application/x-www-form-urlencoded') + expect(response.body).to eq('a=123') + end + + it 'works with nested params' do + response = conn.post('/echo', user: { name: 'Mislav', web: 'mislav.net' }) + expect(response.headers['Content-Type']).to eq('application/x-www-form-urlencoded') + expected = { 'user' => { 'name' => 'Mislav', 'web' => 'mislav.net' } } + expect(Faraday::Utils.parse_nested_query(response.body)).to eq(expected) + end + + it 'works with non nested params' do + response = conn.post('/echo', dimensions: %w[date location]) do |req| + req.options.params_encoder = Faraday::FlatParamsEncoder + end + expect(response.headers['Content-Type']).to eq('application/x-www-form-urlencoded') + expected = { 'dimensions' => %w[date location] } + expect(Faraday::Utils.parse_query(response.body)).to eq(expected) + expect(response.body).to eq('dimensions=date&dimensions=location') + end + + it 'works with unicode' do + err = capture_warnings do + response = conn.post('/echo', str: 'eé cç aã aâ') + expect(response.body).to eq('str=e%C3%A9+c%C3%A7+a%C3%A3+a%C3%A2') + end + expect(err.empty?).to be_truthy + end + + it 'works with nested keys' do + response = conn.post('/echo', 'a' => { 'b' => { 'c' => ['d'] } }) + expect(response.body).to eq('a%5Bb%5D%5Bc%5D%5B%5D=d') + end + + it 'works with files' do + response = conn.post('/echo', StringIO.new('str=apple')) + expect(response.body).to eq('str=apple') + end + + context 'customising default_space_encoding' do + around do |example| + Faraday::Utils.default_space_encoding = '%20' + example.run + Faraday::Utils.default_space_encoding = nil + end + + it 'uses the custom character to encode spaces' do + response = conn.post('/echo', str: 'apple banana') + expect(response.body).to eq('str=apple%20banana') + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/request_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/request_spec.rb new file mode 100644 index 0000000..fbf85b5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/request_spec.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Request do + let(:conn) do + Faraday.new(url: 'http://httpbingo.org/api', + headers: { 'Mime-Version' => '1.0' }, + request: { oauth: { consumer_key: 'anonymous' } }) + end + let(:http_method) { :get } + let(:block) { nil } + + subject { conn.build_request(http_method, &block) } + + context 'when nothing particular is configured' do + it { expect(subject.http_method).to eq(:get) } + it { expect(subject.to_env(conn).ssl.verify).to be_falsey } + it { expect(subject.to_env(conn).ssl.verify_hostname).to be_falsey } + end + + context 'when HTTP method is post' do + let(:http_method) { :post } + + it { expect(subject.http_method).to eq(:post) } + end + + context 'when setting the url on setup with a URI' do + let(:block) { proc { |req| req.url URI.parse('foo.json?a=1') } } + + it { expect(subject.path).to eq(URI.parse('foo.json')) } + it { expect(subject.params).to eq('a' => '1') } + it { expect(subject.to_env(conn).url.to_s).to eq('http://httpbingo.org/api/foo.json?a=1') } + end + + context 'when setting the url on setup with a string path and params' do + let(:block) { proc { |req| req.url 'foo.json', 'a' => 1 } } + + it { expect(subject.path).to eq('foo.json') } + it { expect(subject.params).to eq('a' => 1) } + it { expect(subject.to_env(conn).url.to_s).to eq('http://httpbingo.org/api/foo.json?a=1') } + end + + context 'when setting the url on setup with a path including params' do + let(:block) { proc { |req| req.url 'foo.json?b=2&a=1#qqq' } } + + it { expect(subject.path).to eq('foo.json') } + it { expect(subject.params).to eq('a' => '1', 'b' => '2') } + it { expect(subject.to_env(conn).url.to_s).to eq('http://httpbingo.org/api/foo.json?a=1&b=2') } + end + + context 'when setting a header on setup with []= syntax' do + let(:block) { proc { |req| req['Server'] = 'Faraday' } } + let(:headers) { subject.to_env(conn).request_headers } + + it { expect(subject.headers['Server']).to eq('Faraday') } + it { expect(headers['mime-version']).to eq('1.0') } + it { expect(headers['server']).to eq('Faraday') } + end + + context 'when setting the body on setup' do + let(:block) { proc { |req| req.body = 'hi' } } + + it { expect(subject.body).to eq('hi') } + it { expect(subject.to_env(conn).body).to eq('hi') } + end + + context 'with global request options set' do + let(:env_request) { subject.to_env(conn).request } + + before do + conn.options.timeout = 3 + conn.options.open_timeout = 5 + conn.ssl.verify = false + conn.proxy = 'http://proxy.com' + end + + it { expect(subject.options.timeout).to eq(3) } + it { expect(subject.options.open_timeout).to eq(5) } + it { expect(env_request.timeout).to eq(3) } + it { expect(env_request.open_timeout).to eq(5) } + + context 'and per-request options set' do + let(:block) do + proc do |req| + req.options.timeout = 10 + req.options.boundary = 'boo' + req.options.oauth[:consumer_secret] = 'xyz' + req.options.context = { + foo: 'foo', + bar: 'bar' + } + end + end + + it { expect(subject.options.timeout).to eq(10) } + it { expect(subject.options.open_timeout).to eq(5) } + it { expect(env_request.timeout).to eq(10) } + it { expect(env_request.open_timeout).to eq(5) } + it { expect(env_request.boundary).to eq('boo') } + it { expect(env_request.context).to eq(foo: 'foo', bar: 'bar') } + it do + oauth_expected = { consumer_secret: 'xyz', consumer_key: 'anonymous' } + expect(env_request.oauth).to eq(oauth_expected) + end + end + end + + it 'supports marshal serialization' do + expect(Marshal.load(Marshal.dump(subject))).to eq(subject) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/response/json_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/response/json_spec.rb new file mode 100644 index 0000000..884746e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/response/json_spec.rb @@ -0,0 +1,117 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Response::Json, type: :response do + let(:options) { {} } + let(:headers) { {} } + let(:middleware) do + described_class.new(lambda { |env| + Faraday::Response.new(env) + }, **options) + end + + def process(body, content_type = 'application/json', options = {}) + env = { + body: body, request: options, + request_headers: Faraday::Utils::Headers.new, + response_headers: Faraday::Utils::Headers.new(headers) + } + env[:response_headers]['content-type'] = content_type if content_type + yield(env) if block_given? + middleware.call(Faraday::Env.from(env)) + end + + context 'no type matching' do + it "doesn't change nil body" do + expect(process(nil).body).to be_nil + end + + it 'nullifies empty body' do + expect(process('').body).to be_nil + end + + it 'parses json body' do + response = process('{"a":1}') + expect(response.body).to eq('a' => 1) + expect(response.env[:raw_body]).to be_nil + end + end + + context 'with preserving raw' do + let(:options) { { preserve_raw: true } } + + it 'parses json body' do + response = process('{"a":1}') + expect(response.body).to eq('a' => 1) + expect(response.env[:raw_body]).to eq('{"a":1}') + end + end + + context 'with default regexp type matching' do + it 'parses json body of correct type' do + response = process('{"a":1}', 'application/x-json') + expect(response.body).to eq('a' => 1) + end + + it 'ignores json body of incorrect type' do + response = process('{"a":1}', 'text/json-xml') + expect(response.body).to eq('{"a":1}') + end + end + + context 'with array type matching' do + let(:options) { { content_type: %w[a/b c/d] } } + + it 'parses json body of correct type' do + expect(process('{"a":1}', 'a/b').body).to be_a(Hash) + expect(process('{"a":1}', 'c/d').body).to be_a(Hash) + end + + it 'ignores json body of incorrect type' do + expect(process('{"a":1}', 'a/d').body).not_to be_a(Hash) + end + end + + it 'chokes on invalid json' do + expect { process('{!') }.to raise_error(Faraday::ParsingError) + end + + it 'includes the response on the ParsingError instance' do + process('{') { |env| env[:response] = Faraday::Response.new } + raise 'Parsing should have failed.' + rescue Faraday::ParsingError => e + expect(e.response).to be_a(Faraday::Response) + end + + context 'HEAD responses' do + it "nullifies the body if it's only one space" do + response = process(' ') + expect(response.body).to be_nil + end + + it "nullifies the body if it's two spaces" do + response = process(' ') + expect(response.body).to be_nil + end + end + + context 'JSON options' do + let(:body) { '{"a": 1}' } + let(:result) { { a: 1 } } + let(:options) do + { + parser_options: { + symbolize_names: true + } + } + end + + it 'passes relevant options to JSON parse' do + expect(::JSON).to receive(:parse) + .with(body, options[:parser_options]) + .and_return(result) + + response = process(body) + expect(response.body).to eq(result) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/response/logger_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/response/logger_spec.rb new file mode 100644 index 0000000..674430e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/response/logger_spec.rb @@ -0,0 +1,248 @@ +# frozen_string_literal: true + +require 'stringio' +require 'logger' + +RSpec.describe Faraday::Response::Logger do + let(:string_io) { StringIO.new } + let(:logger) { Logger.new(string_io) } + let(:logger_options) { {} } + let(:conn) do + rubbles = ['Barney', 'Betty', 'Bam Bam'] + + Faraday.new do |b| + b.response :logger, logger, logger_options do |logger| + logger.filter(/(soylent green is) (.+)/, '\1 tasty') + logger.filter(/(api_key:).*"(.+)."/, '\1[API_KEY]') + logger.filter(/(password)=(.+)/, '\1=[HIDDEN]') + end + b.adapter :test do |stubs| + stubs.get('/hello') { [200, { 'Content-Type' => 'text/html' }, 'hello'] } + stubs.post('/ohai') { [200, { 'Content-Type' => 'text/html' }, 'fred'] } + stubs.post('/ohyes') { [200, { 'Content-Type' => 'text/html' }, 'pebbles'] } + stubs.get('/rubbles') { [200, { 'Content-Type' => 'application/json' }, rubbles] } + stubs.get('/filtered_body') { [200, { 'Content-Type' => 'text/html' }, 'soylent green is people'] } + stubs.get('/filtered_headers') { [200, { 'Content-Type' => 'text/html' }, 'headers response'] } + stubs.get('/filtered_params') { [200, { 'Content-Type' => 'text/html' }, 'params response'] } + stubs.get('/filtered_url') { [200, { 'Content-Type' => 'text/html' }, 'url response'] } + end + end + end + + before do + logger.level = Logger::DEBUG + end + + it 'still returns output' do + resp = conn.get '/hello', nil, accept: 'text/html' + expect(resp.body).to eq('hello') + end + + context 'without configuration' do + let(:conn) do + Faraday.new do |b| + b.response :logger + b.adapter :test do |stubs| + stubs.get('/hello') { [200, { 'Content-Type' => 'text/html' }, 'hello'] } + end + end + end + + it 'defaults to stdout' do + expect(Logger).to receive(:new).with($stdout).and_return(Logger.new(nil)) + conn.get('/hello') + end + end + + context 'with default formatter' do + let(:formatter) { instance_double(Faraday::Logging::Formatter, request: true, response: true, filter: []) } + + before { allow(Faraday::Logging::Formatter).to receive(:new).and_return(formatter) } + + it 'delegates logging to the formatter' do + expect(formatter).to receive(:request).with(an_instance_of(Faraday::Env)) + expect(formatter).to receive(:response).with(an_instance_of(Faraday::Env)) + conn.get '/hello' + end + + context 'when no route' do + it 'delegates logging to the formatter' do + expect(formatter).to receive(:request).with(an_instance_of(Faraday::Env)) + expect(formatter).to receive(:error).with(an_instance_of(Faraday::Adapter::Test::Stubs::NotFound)) + + expect { conn.get '/noroute' }.to raise_error(Faraday::Adapter::Test::Stubs::NotFound) + end + end + end + + context 'with custom formatter' do + let(:formatter_class) do + Class.new(Faraday::Logging::Formatter) do + def request(_env) + info 'Custom log formatter request' + end + + def response(_env) + info 'Custom log formatter response' + end + end + end + + let(:logger_options) { { formatter: formatter_class } } + + it 'logs with custom formatter' do + conn.get '/hello' + + expect(string_io.string).to match('Custom log formatter request') + expect(string_io.string).to match('Custom log formatter response') + end + end + + it 'logs method and url' do + conn.get '/hello', nil, accept: 'text/html' + expect(string_io.string).to match('GET http:/hello') + end + + it 'logs status' do + conn.get '/hello', nil, accept: 'text/html' + expect(string_io.string).to match('Status 200') + end + + it 'does not log error message by default' do + expect { conn.get '/noroute' }.to raise_error(Faraday::Adapter::Test::Stubs::NotFound) + expect(string_io.string).not_to match(%(no stubbed request for get http:/noroute)) + end + + it 'logs request headers by default' do + conn.get '/hello', nil, accept: 'text/html' + expect(string_io.string).to match(%(Accept: "text/html)) + end + + it 'logs response headers by default' do + conn.get '/hello', nil, accept: 'text/html' + expect(string_io.string).to match(%(Content-Type: "text/html)) + end + + it 'does not log request body by default' do + conn.post '/ohai', 'name=Unagi', accept: 'text/html' + expect(string_io.string).not_to match(%(name=Unagi)) + end + + it 'does not log response body by default' do + conn.post '/ohai', 'name=Toro', accept: 'text/html' + expect(string_io.string).not_to match(%(fred)) + end + + it 'logs filter headers' do + conn.headers = { 'api_key' => 'ABC123' } + conn.get '/filtered_headers', nil, accept: 'text/html' + expect(string_io.string).to match(%(api_key:)) + expect(string_io.string).to match(%([API_KEY])) + expect(string_io.string).not_to match(%(ABC123)) + end + + it 'logs filter url' do + conn.get '/filtered_url?password=hunter2', nil, accept: 'text/html' + expect(string_io.string).to match(%([HIDDEN])) + expect(string_io.string).not_to match(%(hunter2)) + end + + context 'when not logging request headers' do + let(:logger_options) { { headers: { request: false } } } + + it 'does not log request headers if option is false' do + conn.get '/hello', nil, accept: 'text/html' + expect(string_io.string).not_to match(%(Accept: "text/html)) + end + end + + context 'when not logging response headers' do + let(:logger_options) { { headers: { response: false } } } + + it 'does not log response headers if option is false' do + conn.get '/hello', nil, accept: 'text/html' + expect(string_io.string).not_to match(%(Content-Type: "text/html)) + end + end + + context 'when logging request body' do + let(:logger_options) { { bodies: { request: true } } } + + it 'log only request body' do + conn.post '/ohyes', 'name=Tamago', accept: 'text/html' + expect(string_io.string).to match(%(name=Tamago)) + expect(string_io.string).not_to match(%(pebbles)) + end + end + + context 'when logging response body' do + let(:logger_options) { { bodies: { response: true } } } + + it 'log only response body' do + conn.post '/ohyes', 'name=Hamachi', accept: 'text/html' + expect(string_io.string).to match(%(pebbles)) + expect(string_io.string).not_to match(%(name=Hamachi)) + end + end + + context 'when logging request and response bodies' do + let(:logger_options) { { bodies: true } } + + it 'log request and response body' do + conn.post '/ohyes', 'name=Ebi', accept: 'text/html' + expect(string_io.string).to match(%(name=Ebi)) + expect(string_io.string).to match(%(pebbles)) + end + + it 'log response body object' do + conn.get '/rubbles', nil, accept: 'text/html' + expect(string_io.string).to match(%([\"Barney\", \"Betty\", \"Bam Bam\"]\n)) + end + + it 'logs filter body' do + conn.get '/filtered_body', nil, accept: 'text/html' + expect(string_io.string).to match(%(soylent green is)) + expect(string_io.string).to match(%(tasty)) + expect(string_io.string).not_to match(%(people)) + end + end + + context 'when logging errors' do + let(:logger_options) { { errors: true } } + + it 'logs error message' do + expect { conn.get '/noroute' }.to raise_error(Faraday::Adapter::Test::Stubs::NotFound) + expect(string_io.string).to match(%(no stubbed request for get http:/noroute)) + end + end + + context 'when using log_level' do + let(:logger_options) { { bodies: true, log_level: :debug } } + + it 'logs request/request body on the specified level (debug)' do + logger.level = Logger::DEBUG + conn.post '/ohyes', 'name=Ebi', accept: 'text/html' + expect(string_io.string).to match(%(name=Ebi)) + expect(string_io.string).to match(%(pebbles)) + end + + it 'logs headers on the debug level' do + logger.level = Logger::DEBUG + conn.get '/hello', nil, accept: 'text/html' + expect(string_io.string).to match(%(Content-Type: "text/html)) + end + + it 'does not log request/response body on the info level' do + logger.level = Logger::INFO + conn.post '/ohyes', 'name=Ebi', accept: 'text/html' + expect(string_io.string).not_to match(%(name=Ebi)) + expect(string_io.string).not_to match(%(pebbles)) + end + + it 'does not log headers on the info level' do + logger.level = Logger::INFO + conn.get '/hello', nil, accept: 'text/html' + expect(string_io.string).not_to match(%(Content-Type: "text/html)) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/response/raise_error_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/response/raise_error_spec.rb new file mode 100644 index 0000000..95b8676 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/response/raise_error_spec.rb @@ -0,0 +1,172 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Response::RaiseError do + let(:conn) do + Faraday.new do |b| + b.response :raise_error + b.adapter :test do |stub| + stub.get('ok') { [200, { 'Content-Type' => 'text/html' }, ''] } + stub.get('bad-request') { [400, { 'X-Reason' => 'because' }, 'keep looking'] } + stub.get('unauthorized') { [401, { 'X-Reason' => 'because' }, 'keep looking'] } + stub.get('forbidden') { [403, { 'X-Reason' => 'because' }, 'keep looking'] } + stub.get('not-found') { [404, { 'X-Reason' => 'because' }, 'keep looking'] } + stub.get('proxy-error') { [407, { 'X-Reason' => 'because' }, 'keep looking'] } + stub.get('conflict') { [409, { 'X-Reason' => 'because' }, 'keep looking'] } + stub.get('unprocessable-entity') { [422, { 'X-Reason' => 'because' }, 'keep looking'] } + stub.get('4xx') { [499, { 'X-Reason' => 'because' }, 'keep looking'] } + stub.get('nil-status') { [nil, { 'X-Reason' => 'nil' }, 'fail'] } + stub.get('server-error') { [500, { 'X-Error' => 'bailout' }, 'fail'] } + end + end + end + + it 'raises no exception for 200 responses' do + expect { conn.get('ok') }.not_to raise_error + end + + it 'raises Faraday::BadRequestError for 400 responses' do + expect { conn.get('bad-request') }.to raise_error(Faraday::BadRequestError) do |ex| + expect(ex.message).to eq('the server responded with status 400') + expect(ex.response[:headers]['X-Reason']).to eq('because') + expect(ex.response[:status]).to eq(400) + expect(ex.response_status).to eq(400) + expect(ex.response_body).to eq('keep looking') + expect(ex.response_headers['X-Reason']).to eq('because') + end + end + + it 'raises Faraday::UnauthorizedError for 401 responses' do + expect { conn.get('unauthorized') }.to raise_error(Faraday::UnauthorizedError) do |ex| + expect(ex.message).to eq('the server responded with status 401') + expect(ex.response[:headers]['X-Reason']).to eq('because') + expect(ex.response[:status]).to eq(401) + expect(ex.response_status).to eq(401) + expect(ex.response_body).to eq('keep looking') + expect(ex.response_headers['X-Reason']).to eq('because') + end + end + + it 'raises Faraday::ForbiddenError for 403 responses' do + expect { conn.get('forbidden') }.to raise_error(Faraday::ForbiddenError) do |ex| + expect(ex.message).to eq('the server responded with status 403') + expect(ex.response[:headers]['X-Reason']).to eq('because') + expect(ex.response[:status]).to eq(403) + expect(ex.response_status).to eq(403) + expect(ex.response_body).to eq('keep looking') + expect(ex.response_headers['X-Reason']).to eq('because') + end + end + + it 'raises Faraday::ResourceNotFound for 404 responses' do + expect { conn.get('not-found') }.to raise_error(Faraday::ResourceNotFound) do |ex| + expect(ex.message).to eq('the server responded with status 404') + expect(ex.response[:headers]['X-Reason']).to eq('because') + expect(ex.response[:status]).to eq(404) + expect(ex.response_status).to eq(404) + expect(ex.response_body).to eq('keep looking') + expect(ex.response_headers['X-Reason']).to eq('because') + end + end + + it 'raises Faraday::ProxyAuthError for 407 responses' do + expect { conn.get('proxy-error') }.to raise_error(Faraday::ProxyAuthError) do |ex| + expect(ex.message).to eq('407 "Proxy Authentication Required"') + expect(ex.response[:headers]['X-Reason']).to eq('because') + expect(ex.response[:status]).to eq(407) + expect(ex.response_status).to eq(407) + expect(ex.response_body).to eq('keep looking') + expect(ex.response_headers['X-Reason']).to eq('because') + end + end + + it 'raises Faraday::ConflictError for 409 responses' do + expect { conn.get('conflict') }.to raise_error(Faraday::ConflictError) do |ex| + expect(ex.message).to eq('the server responded with status 409') + expect(ex.response[:headers]['X-Reason']).to eq('because') + expect(ex.response[:status]).to eq(409) + expect(ex.response_status).to eq(409) + expect(ex.response_body).to eq('keep looking') + expect(ex.response_headers['X-Reason']).to eq('because') + end + end + + it 'raises Faraday::UnprocessableEntityError for 422 responses' do + expect { conn.get('unprocessable-entity') }.to raise_error(Faraday::UnprocessableEntityError) do |ex| + expect(ex.message).to eq('the server responded with status 422') + expect(ex.response[:headers]['X-Reason']).to eq('because') + expect(ex.response[:status]).to eq(422) + expect(ex.response_status).to eq(422) + expect(ex.response_body).to eq('keep looking') + expect(ex.response_headers['X-Reason']).to eq('because') + end + end + + it 'raises Faraday::NilStatusError for nil status in response' do + expect { conn.get('nil-status') }.to raise_error(Faraday::NilStatusError) do |ex| + expect(ex.message).to eq('http status could not be derived from the server response') + expect(ex.response[:headers]['X-Reason']).to eq('nil') + expect(ex.response[:status]).to be_nil + expect(ex.response_status).to be_nil + expect(ex.response_body).to eq('fail') + expect(ex.response_headers['X-Reason']).to eq('nil') + end + end + + it 'raises Faraday::ClientError for other 4xx responses' do + expect { conn.get('4xx') }.to raise_error(Faraday::ClientError) do |ex| + expect(ex.message).to eq('the server responded with status 499') + expect(ex.response[:headers]['X-Reason']).to eq('because') + expect(ex.response[:status]).to eq(499) + expect(ex.response_status).to eq(499) + expect(ex.response_body).to eq('keep looking') + expect(ex.response_headers['X-Reason']).to eq('because') + end + end + + it 'raises Faraday::ServerError for 500 responses' do + expect { conn.get('server-error') }.to raise_error(Faraday::ServerError) do |ex| + expect(ex.message).to eq('the server responded with status 500') + expect(ex.response[:headers]['X-Error']).to eq('bailout') + expect(ex.response[:status]).to eq(500) + expect(ex.response_status).to eq(500) + expect(ex.response_body).to eq('fail') + expect(ex.response_headers['X-Error']).to eq('bailout') + end + end + + describe 'request info' do + let(:conn) do + Faraday.new do |b| + b.response :raise_error + b.adapter :test do |stub| + stub.post(url, request_body, request_headers) do + [400, { 'X-Reason' => 'because' }, 'keep looking'] + end + end + end + end + let(:request_body) { JSON.generate({ 'item' => 'sth' }) } + let(:request_headers) { { 'Authorization' => 'Basic 123' } } + let(:url_path) { 'request' } + let(:query_params) { 'full=true' } + let(:url) { "#{url_path}?#{query_params}" } + + subject(:perform_request) do + conn.post url do |req| + req.headers['Authorization'] = 'Basic 123' + req.body = request_body + end + end + + it 'returns the request info in the exception' do + expect { perform_request }.to raise_error(Faraday::BadRequestError) do |ex| + expect(ex.response[:request][:method]).to eq(:post) + expect(ex.response[:request][:url]).to eq(URI("http:/#{url}")) + expect(ex.response[:request][:url_path]).to eq("/#{url_path}") + expect(ex.response[:request][:params]).to eq({ 'full' => 'true' }) + expect(ex.response[:request][:headers]).to match(a_hash_including(request_headers)) + expect(ex.response[:request][:body]).to eq(request_body) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/response_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/response_spec.rb new file mode 100644 index 0000000..1715947 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/response_spec.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Response do + subject { Faraday::Response.new(env) } + + let(:env) do + Faraday::Env.from(status: 404, body: 'yikes', + response_headers: { 'Content-Type' => 'text/plain' }) + end + + it { expect(subject.finished?).to be_truthy } + it { expect { subject.finish({}) }.to raise_error(RuntimeError) } + it { expect(subject.success?).to be_falsey } + it { expect(subject.status).to eq(404) } + it { expect(subject.body).to eq('yikes') } + it { expect(subject.headers['Content-Type']).to eq('text/plain') } + it { expect(subject['content-type']).to eq('text/plain') } + + describe '#apply_request' do + before { subject.apply_request(body: 'a=b', method: :post) } + + it { expect(subject.body).to eq('yikes') } + it { expect(subject.env[:method]).to eq(:post) } + end + + describe '#to_hash' do + let(:hash) { subject.to_hash } + + it { expect(hash).to be_a(Hash) } + it { expect(hash[:status]).to eq(subject.status) } + it { expect(hash[:response_headers]).to eq(subject.headers) } + it { expect(hash[:body]).to eq(subject.body) } + end + + describe 'marshal serialization support' do + subject { Faraday::Response.new } + let(:loaded) { Marshal.load(Marshal.dump(subject)) } + + before do + subject.on_complete {} + subject.finish(env.merge(params: 'moo')) + end + + it { expect(loaded.env[:params]).to be_nil } + it { expect(loaded.env[:body]).to eq(env[:body]) } + it { expect(loaded.env[:response_headers]).to eq(env[:response_headers]) } + it { expect(loaded.env[:status]).to eq(env[:status]) } + end + + describe '#on_complete' do + subject { Faraday::Response.new } + + it 'parse body on finish' do + subject.on_complete { |env| env[:body] = env[:body].upcase } + subject.finish(env) + + expect(subject.body).to eq('YIKES') + end + + it 'can access response body in on_complete callback' do + subject.on_complete { |env| env[:body] = subject.body.upcase } + subject.finish(env) + + expect(subject.body).to eq('YIKES') + end + + it 'can access response body in on_complete callback' do + callback_env = nil + subject.on_complete { |env| callback_env = env } + subject.finish({}) + + expect(subject.env).to eq(callback_env) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/utils/headers_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/utils/headers_spec.rb new file mode 100644 index 0000000..66751f7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/utils/headers_spec.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Utils::Headers do + subject { Faraday::Utils::Headers.new } + + context 'when Content-Type is set to application/json' do + before { subject['Content-Type'] = 'application/json' } + + it { expect(subject.keys).to eq(['Content-Type']) } + it { expect(subject['Content-Type']).to eq('application/json') } + it { expect(subject['CONTENT-TYPE']).to eq('application/json') } + it { expect(subject['content-type']).to eq('application/json') } + it { is_expected.to include('content-type') } + end + + context 'when Content-Type is set to application/xml' do + before { subject['Content-Type'] = 'application/xml' } + + it { expect(subject.keys).to eq(['Content-Type']) } + it { expect(subject['Content-Type']).to eq('application/xml') } + it { expect(subject['CONTENT-TYPE']).to eq('application/xml') } + it { expect(subject['content-type']).to eq('application/xml') } + it { is_expected.to include('content-type') } + end + + describe '#fetch' do + before { subject['Content-Type'] = 'application/json' } + + it { expect(subject.fetch('Content-Type')).to eq('application/json') } + it { expect(subject.fetch('CONTENT-TYPE')).to eq('application/json') } + it { expect(subject.fetch(:content_type)).to eq('application/json') } + it { expect(subject.fetch('invalid', 'default')).to eq('default') } + it { expect(subject.fetch('invalid', false)).to eq(false) } + it { expect(subject.fetch('invalid', nil)).to be_nil } + it { expect(subject.fetch('Invalid') { |key| "#{key} key" }).to eq('Invalid key') } + it 'calls a block when provided' do + block_called = false + expect(subject.fetch('content-type') { block_called = true }).to eq('application/json') + expect(block_called).to be_falsey + end + it 'raises an error if key not found' do + expected_error = defined?(KeyError) ? KeyError : IndexError + expect { subject.fetch('invalid') }.to raise_error(expected_error) + end + end + + describe '#delete' do + before do + subject['Content-Type'] = 'application/json' + @deleted = subject.delete('content-type') + end + + it { expect(@deleted).to eq('application/json') } + it { expect(subject.size).to eq(0) } + it { is_expected.not_to include('content-type') } + it { expect(subject.delete('content-type')).to be_nil } + end + + describe '#parse' do + context 'when response headers leave http status line out' do + let(:headers) { "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n" } + + before { subject.parse(headers) } + + it { expect(subject.keys).to eq(%w[Content-Type]) } + it { expect(subject['Content-Type']).to eq('text/html') } + it { expect(subject['content-type']).to eq('text/html') } + end + + context 'when response headers values include a colon' do + let(:headers) { "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nLocation: http://httpbingo.org/\r\n\r\n" } + + before { subject.parse(headers) } + + it { expect(subject['location']).to eq('http://httpbingo.org/') } + end + + context 'when response headers include a blank line' do + let(:headers) { "HTTP/1.1 200 OK\r\n\r\nContent-Type: text/html\r\n\r\n" } + + before { subject.parse(headers) } + + it { expect(subject['content-type']).to eq('text/html') } + end + + context 'when response headers include already stored keys' do + let(:headers) { "HTTP/1.1 200 OK\r\nX-Numbers: 123\r\n\r\n" } + + before do + h = subject + h[:x_numbers] = 8 + h.parse(headers) + end + + it do + expect(subject[:x_numbers]).to eq('8, 123') + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/utils_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/utils_spec.rb new file mode 100644 index 0000000..bf7499e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday/utils_spec.rb @@ -0,0 +1,118 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Utils do + describe 'headers parsing' do + let(:multi_response_headers) do + "HTTP/1.x 500 OK\r\nContent-Type: text/html; charset=UTF-8\r\n" \ + "HTTP/1.x 200 OK\r\nContent-Type: application/json; charset=UTF-8\r\n\r\n" + end + + it 'parse headers for aggregated responses' do + headers = Faraday::Utils::Headers.new + headers.parse(multi_response_headers) + + result = headers.to_hash + + expect(result['Content-Type']).to eq('application/json; charset=UTF-8') + end + end + + describe 'URI parsing' do + let(:url) { 'http://example.com/abc' } + + it 'escapes safe buffer' do + str = FakeSafeBuffer.new('$32,000.00') + expect(Faraday::Utils.escape(str)).to eq('%2432%2C000.00') + end + + it 'parses with default parser' do + with_default_uri_parser(nil) do + uri = normalize(url) + expect(uri.host).to eq('example.com') + end + end + + it 'parses with URI' do + with_default_uri_parser(::URI) do + uri = normalize(url) + expect(uri.host).to eq('example.com') + end + end + + it 'parses with block' do + with_default_uri_parser(->(u) { "booya#{'!' * u.size}" }) do + expect(normalize(url)).to eq('booya!!!!!!!!!!!!!!!!!!!!!!') + end + end + + it 'replaces headers hash' do + headers = Faraday::Utils::Headers.new('authorization' => 't0ps3cr3t!') + expect(headers).to have_key('authorization') + + headers.replace('content-type' => 'text/plain') + expect(headers).not_to have_key('authorization') + end + end + + describe '.deep_merge!' do + let(:connection_options) { Faraday::ConnectionOptions.new } + let(:url) do + { + url: 'http://example.com/abc', + headers: { 'Mime-Version' => '1.0' }, + request: { oauth: { consumer_key: 'anonymous' } }, + ssl: { version: '2' } + } + end + + it 'recursively merges the headers' do + connection_options.headers = { user_agent: 'My Agent 1.0' } + deep_merge = Faraday::Utils.deep_merge!(connection_options, url) + + expect(deep_merge.headers).to eq('Mime-Version' => '1.0', user_agent: 'My Agent 1.0') + end + + context 'when a target hash has an Options Struct value' do + let(:request) do + { + params_encoder: nil, + proxy: nil, + bind: nil, + timeout: nil, + open_timeout: nil, + read_timeout: nil, + write_timeout: nil, + boundary: nil, + oauth: { consumer_key: 'anonymous' }, + context: nil, + on_data: nil + } + end + let(:ssl) do + { + verify: nil, + ca_file: nil, + ca_path: nil, + verify_mode: nil, + cert_store: nil, + client_cert: nil, + client_key: nil, + certificate: nil, + private_key: nil, + verify_depth: nil, + version: '2', + min_version: nil, + max_version: nil, + verify_hostname: nil + } + end + + it 'does not overwrite an Options Struct value' do + deep_merge = Faraday::Utils.deep_merge!(connection_options, url) + + expect(deep_merge.request.to_h).to eq(request) + expect(deep_merge.ssl.to_h).to eq(ssl) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday_spec.rb new file mode 100644 index 0000000..8b603eb --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/faraday_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +RSpec.describe Faraday do + it 'has a version number' do + expect(Faraday::VERSION).not_to be nil + end + + context 'proxies to default_connection' do + let(:mock_connection) { double('Connection') } + before do + Faraday.default_connection = mock_connection + end + + it 'proxies methods that exist on the default_connection' do + expect(mock_connection).to receive(:this_should_be_proxied) + + Faraday.this_should_be_proxied + end + + it 'uses method_missing on Faraday if there is no proxyable method' do + expect { Faraday.this_method_does_not_exist }.to raise_error( + NoMethodError, + "undefined method `this_method_does_not_exist' for Faraday:Module" + ) + end + + it 'proxied methods can be accessed' do + allow(mock_connection).to receive(:this_should_be_proxied) + + expect(Faraday.method(:this_should_be_proxied)).to be_a(Method) + end + + after do + Faraday.default_connection = nil + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/spec_helper.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/spec_helper.rb new file mode 100644 index 0000000..5fcddd5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/spec_helper.rb @@ -0,0 +1,132 @@ +# frozen_string_literal: true + +# This file was generated by the `rspec --init` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause +# this file to always be loaded, without a need to explicitly require it in any +# files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration + +require 'simplecov' +require 'coveralls' +require 'webmock/rspec' +WebMock.disable_net_connect!(allow_localhost: true) + +SimpleCov.formatters = [SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter] + +SimpleCov.start do + add_filter '/spec/' + minimum_coverage 84 + minimum_coverage_by_file 26 +end + +# Ensure all /lib files are loaded +# so they will be included in the test coverage report. +Dir['./lib/**/*.rb'].sort.each { |file| require file } + +require 'faraday' +require 'pry' + +Dir['./spec/support/**/*.rb'].sort.each { |f| require f } + +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # triggering implicit auto-inclusion in groups with matching metadata. + config.shared_context_metadata_behavior = :apply_to_host_groups + + # This allows you to limit a spec run to individual examples or groups + # you care about by tagging them with `:focus` metadata. When nothing + # is tagged with `:focus`, all examples get run. RSpec also provides + # aliases for `it`, `describe`, and `context` that include `:focus` + # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + # config.filter_run_when_matching :focus + + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + # config.example_status_persistence_file_path = "spec/examples.txt" + + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ + # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode + # config.disable_monkey_patching! + + # This setting enables warnings. It's recommended, but in some cases may + # be too noisy due to issues in dependencies. + # config.warnings = true + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + # if config.files_to_run.one? + # # Use the documentation formatter for detailed output, + # # unless a formatter has already been configured + # # (e.g. via a command-line flag). + # config.default_formatter = "doc" + # end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + # config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed + + config.include Faraday::HelperMethods +end + +# Extends RSpec DocumentationFormatter to hide skipped tests. +module FormatterOverrides + def example_pending(_arg); end + + def dump_pending(_arg); end + + RSpec::Core::Formatters::DocumentationFormatter.prepend self +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/support/disabling_stub.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/support/disabling_stub.rb new file mode 100644 index 0000000..3df2f21 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/support/disabling_stub.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# Allows to disable WebMock stubs +module DisablingStub + def disable + @disabled = true + end + + def disabled? + @disabled + end + + WebMock::RequestStub.prepend self +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/support/fake_safe_buffer.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/support/fake_safe_buffer.rb new file mode 100644 index 0000000..69afd6e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/support/fake_safe_buffer.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +# emulates ActiveSupport::SafeBuffer#gsub +FakeSafeBuffer = Struct.new(:string) do + def to_s + self + end + + def gsub(regex) + string.gsub(regex) do + match, = Regexp.last_match(0), '' =~ /a/ # rubocop:disable Performance/StringInclude + yield(match) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/support/helper_methods.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/support/helper_methods.rb new file mode 100644 index 0000000..0f5d4f5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/support/helper_methods.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +module Faraday + module HelperMethods + def self.included(base) + base.extend ClassMethods + end + + module ClassMethods + def features(*features) + @features = features + end + + def on_feature(name) + yield if block_given? && feature?(name) + end + + def feature?(name) + if @features.nil? + superclass.feature?(name) if superclass.respond_to?(:feature?) + elsif @features.include?(name) + true + end + end + + def method_with_body?(method) + METHODS_WITH_BODY.include?(method.to_s) + end + end + + def ssl_mode? + ENV['SSL'] == 'yes' + end + + def normalize(url) + Faraday::Utils::URI(url) + end + + def with_default_uri_parser(parser) + old_parser = Faraday::Utils.default_uri_parser + begin + Faraday::Utils.default_uri_parser = parser + yield + ensure + Faraday::Utils.default_uri_parser = old_parser + end + end + + def with_env(new_env) + old_env = {} + + new_env.each do |key, value| + old_env[key] = ENV.fetch(key, false) + ENV[key] = value + end + + begin + yield + ensure + old_env.each do |key, value| + value == false ? ENV.delete(key) : ENV[key] = value + end + end + end + + def with_env_proxy_disabled + Faraday.ignore_env_proxy = true + + begin + yield + ensure + Faraday.ignore_env_proxy = false + end + end + + def capture_warnings + old = $stderr + $stderr = StringIO.new + begin + yield + $stderr.string + ensure + $stderr = old + end + end + + def method_with_body?(method) + self.class.method_with_body?(method) + end + + def big_string + kb = 1024 + (32..126).map(&:chr).cycle.take(50 * kb).join + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/support/shared_examples/adapter.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/support/shared_examples/adapter.rb new file mode 100644 index 0000000..6256908 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/support/shared_examples/adapter.rb @@ -0,0 +1,105 @@ +# frozen_string_literal: true + +shared_examples 'an adapter' do |**options| + before { skip } if options[:skip] + + context 'with SSL enabled' do + before { ENV['SSL'] = 'yes' } + include_examples 'adapter examples', options + end + + context 'with SSL disabled' do + before { ENV['SSL'] = 'no' } + include_examples 'adapter examples', options + end +end + +shared_examples 'adapter examples' do |**options| + include Faraday::StreamingResponseChecker + + let(:adapter) { described_class.name.split('::').last } + + let(:conn_options) { { headers: { 'X-Faraday-Adapter' => adapter } }.merge(options[:conn_options] || {}) } + + let(:adapter_options) do + return [] unless options[:adapter_options] + + if options[:adapter_options].is_a?(Array) + options[:adapter_options] + else + [options[:adapter_options]] + end + end + + let(:protocol) { ssl_mode? ? 'https' : 'http' } + let(:remote) { "#{protocol}://example.com" } + let(:stub_remote) { remote } + + let(:conn) do + conn_options[:ssl] ||= {} + conn_options[:ssl][:ca_file] ||= ENV.fetch('SSL_FILE', nil) + conn_options[:ssl][:verify_hostname] ||= ENV['SSL_VERIFY_HOSTNAME'] == 'yes' + + Faraday.new(remote, conn_options) do |conn| + conn.request :url_encoded + conn.response :raise_error + conn.adapter described_class, *adapter_options + end + end + + let!(:request_stub) { stub_request(http_method, stub_remote) } + + after do + expect(request_stub).to have_been_requested unless request_stub.disabled? + end + + describe '#delete' do + let(:http_method) { :delete } + + it_behaves_like 'a request method', :delete + end + + describe '#get' do + let(:http_method) { :get } + + it_behaves_like 'a request method', :get + end + + describe '#head' do + let(:http_method) { :head } + + it_behaves_like 'a request method', :head + end + + describe '#options' do + let(:http_method) { :options } + + it_behaves_like 'a request method', :options + end + + describe '#patch' do + let(:http_method) { :patch } + + it_behaves_like 'a request method', :patch + end + + describe '#post' do + let(:http_method) { :post } + + it_behaves_like 'a request method', :post + end + + describe '#put' do + let(:http_method) { :put } + + it_behaves_like 'a request method', :put + end + + on_feature :trace_method do + describe '#trace' do + let(:http_method) { :trace } + + it_behaves_like 'a request method', :trace + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/support/shared_examples/params_encoder.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/support/shared_examples/params_encoder.rb new file mode 100644 index 0000000..38c8567 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/support/shared_examples/params_encoder.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +shared_examples 'a params encoder' do + it 'escapes safe buffer' do + monies = FakeSafeBuffer.new('$32,000.00') + expect(subject.encode('a' => monies)).to eq('a=%2432%2C000.00') + end + + it 'raises type error for empty string' do + expect { subject.encode('') }.to raise_error(TypeError) do |error| + expect(error.message).to eq("Can't convert String into Hash.") + end + end + + it 'encodes nil' do + expect(subject.encode('a' => nil)).to eq('a') + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/support/shared_examples/request_method.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/support/shared_examples/request_method.rb new file mode 100644 index 0000000..afa3376 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/support/shared_examples/request_method.rb @@ -0,0 +1,263 @@ +# frozen_string_literal: true + +shared_examples 'proxy examples' do + it 'handles requests with proxy' do + res = conn.public_send(http_method, '/') + + expect(res.status).to eq(200) + end + + it 'handles proxy failures' do + request_stub.to_return(status: 407) + + expect { conn.public_send(http_method, '/') }.to raise_error(Faraday::ProxyAuthError) + end +end + +shared_examples 'a request method' do |http_method| + let(:query_or_body) { method_with_body?(http_method) ? :body : :query } + let(:response) { conn.public_send(http_method, '/') } + + unless http_method == :head && feature?(:skip_response_body_on_head) + it 'retrieves the response body' do + res_body = 'test' + request_stub.to_return(body: res_body) + expect(conn.public_send(http_method, '/').body).to eq(res_body) + end + end + + it 'handles headers with multiple values' do + request_stub.to_return(headers: { 'Set-Cookie' => 'name=value' }) + expect(response.headers['set-cookie']).to eq('name=value') + end + + it 'retrieves the response headers' do + request_stub.to_return(headers: { 'Content-Type' => 'text/plain' }) + expect(response.headers['Content-Type']).to match(%r{text/plain}) + expect(response.headers['content-type']).to match(%r{text/plain}) + end + + it 'sends user agent' do + request_stub.with(headers: { 'User-Agent' => 'Agent Faraday' }) + conn.public_send(http_method, '/', nil, user_agent: 'Agent Faraday') + end + + it 'represents empty body response as blank string' do + expect(response.body).to eq('') + end + + it 'handles connection error' do + request_stub.disable + expect { conn.public_send(http_method, 'http://localhost:4') }.to raise_error(Faraday::ConnectionFailed) + end + + on_feature :local_socket_binding do + it 'binds local socket' do + stub_request(http_method, 'http://example.com') + + host = '1.2.3.4' + port = 1234 + conn_options[:request] = { bind: { host: host, port: port } } + + conn.public_send(http_method, '/') + + expect(conn.options[:bind][:host]).to eq(host) + expect(conn.options[:bind][:port]).to eq(port) + end + end + + # context 'when wrong ssl certificate is provided' do + # let(:ca_file_path) { 'tmp/faraday-different-ca-cert.crt' } + # before { conn_options.merge!(ssl: { ca_file: ca_file_path }) } + # + # it do + # expect { conn.public_send(http_method, '/') }.to raise_error(Faraday::SSLError) # do |ex| + # expect(ex.message).to include?('certificate') + # end + # end + # end + + on_feature :request_body_on_query_methods do + it 'sends request body' do + request_stub.with({ body: 'test' }) + res = if query_or_body == :body + conn.public_send(http_method, '/', 'test') + else + conn.public_send(http_method, '/') do |req| + req.body = 'test' + end + end + expect(res.env.request_body).to eq('test') + end + end + + it 'sends url encoded parameters' do + payload = { name: 'zack' } + request_stub.with({ query_or_body => payload }) + res = conn.public_send(http_method, '/', payload) + if query_or_body == :query + expect(res.env.request_body).to be_nil + else + expect(res.env.request_body).to eq('name=zack') + end + end + + it 'sends url encoded nested parameters' do + payload = { name: { first: 'zack' } } + request_stub.with({ query_or_body => payload }) + conn.public_send(http_method, '/', payload) + end + + # TODO: This needs reimplementation: see https://github.com/lostisland/faraday/issues/718 + # Should raise Faraday::TimeoutError + it 'supports timeout option' do + conn_options[:request] = { timeout: 1 } + request_stub.to_timeout + exc = adapter == 'NetHttp' ? Faraday::ConnectionFailed : Faraday::TimeoutError + expect { conn.public_send(http_method, '/') }.to raise_error(exc) + end + + # TODO: This needs reimplementation: see https://github.com/lostisland/faraday/issues/718 + # Should raise Faraday::ConnectionFailed + it 'supports open_timeout option' do + conn_options[:request] = { open_timeout: 1 } + request_stub.to_timeout + exc = adapter == 'NetHttp' ? Faraday::ConnectionFailed : Faraday::TimeoutError + expect { conn.public_send(http_method, '/') }.to raise_error(exc) + end + + on_feature :reason_phrase_parse do + it 'parses the reason phrase' do + request_stub.to_return(status: [200, 'OK']) + expect(response.reason_phrase).to eq('OK') + end + end + + on_feature :compression do + # Accept-Encoding header not sent for HEAD requests as body is not expected in the response. + unless http_method == :head + it 'handles gzip compression' do + request_stub.with(headers: { 'Accept-Encoding' => /\bgzip\b/ }) + conn.public_send(http_method, '/') + end + + it 'handles deflate compression' do + request_stub.with(headers: { 'Accept-Encoding' => /\bdeflate\b/ }) + conn.public_send(http_method, '/') + end + end + end + + on_feature :streaming do + describe 'streaming' do + let(:streamed) { [] } + + context 'when response is empty' do + it 'handles streaming' do + env = nil + conn.public_send(http_method, '/') do |req| + req.options.on_data = proc do |chunk, size, block_env| + streamed << [chunk, size] + env ||= block_env + end + end + + expect(streamed).to eq([['', 0]]) + # TODO: enable this after updating all existing adapters to the new streaming API + # expect(env).to be_a(Faraday::Env) + # expect(env.status).to eq(200) + end + end + + context 'when response contains big data' do + before { request_stub.to_return(body: big_string) } + + it 'handles streaming' do + env = nil + response = conn.public_send(http_method, '/') do |req| + req.options.on_data = proc do |chunk, size, block_env| + streamed << [chunk, size] + env ||= block_env + end + end + + expect(response.body).to eq('') + check_streaming_response(streamed, chunk_size: 16 * 1024) + # TODO: enable this after updating all existing adapters to the new streaming API + # expect(env).to be_a(Faraday::Env) + # expect(env.status).to eq(200) + end + end + end + end + + on_feature :parallel do + context 'with parallel setup' do + before do + @resp1 = nil + @resp2 = nil + @payload1 = { a: '1' } + @payload2 = { b: '2' } + + request_stub + .with({ query_or_body => @payload1 }) + .to_return(body: @payload1.to_json) + + stub_request(http_method, remote) + .with({ query_or_body => @payload2 }) + .to_return(body: @payload2.to_json) + + conn.in_parallel do + @resp1 = conn.public_send(http_method, '/', @payload1) + @resp2 = conn.public_send(http_method, '/', @payload2) + + expect(conn.in_parallel?).to be_truthy + expect(@resp1.body).to be_nil + expect(@resp2.body).to be_nil + end + + expect(conn.in_parallel?).to be_falsey + end + + it 'handles parallel requests status' do + expect(@resp1&.status).to eq(200) + expect(@resp2&.status).to eq(200) + end + + unless http_method == :head && feature?(:skip_response_body_on_head) + it 'handles parallel requests body' do + expect(@resp1&.body).to eq(@payload1.to_json) + expect(@resp2&.body).to eq(@payload2.to_json) + end + end + end + end + + context 'when a proxy is provided as option' do + before do + conn_options[:proxy] = 'http://env-proxy.com:80' + end + + include_examples 'proxy examples' + end + + context 'when http_proxy env variable is set' do + let(:proxy_url) { 'http://env-proxy.com:80' } + + around do |example| + with_env 'http_proxy' => proxy_url do + example.run + end + end + + include_examples 'proxy examples' + + context 'when the env proxy is ignored' do + around do |example| + with_env_proxy_disabled(&example) + end + + include_examples 'proxy examples' + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/support/streaming_response_checker.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/support/streaming_response_checker.rb new file mode 100644 index 0000000..8ef2599 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-2.7.1/spec/support/streaming_response_checker.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Faraday + module StreamingResponseChecker + def check_streaming_response(streamed, options = {}) + opts = { + prefix: '', + streaming?: true + }.merge(options) + + expected_response = opts[:prefix] + big_string + + chunks, sizes = streamed.transpose + + # Check that the total size of the chunks (via the last size returned) + # is the same size as the expected_response + expect(sizes.last).to eq(expected_response.bytesize) + + start_index = 0 + expected_chunks = [] + chunks.each do |actual_chunk| + expected_chunk = expected_response[start_index..((start_index + actual_chunk.bytesize) - 1)] + expected_chunks << expected_chunk + start_index += expected_chunk.bytesize + end + + # it's easier to read a smaller portion, so we check that first + expect(expected_chunks[0][0..255]).to eq(chunks[0][0..255]) + + [expected_chunks, chunks].transpose.each do |expected, actual| + expect(actual).to eq(expected) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/LICENSE b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/LICENSE new file mode 100644 index 0000000..e92d6de --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/LICENSE @@ -0,0 +1,11 @@ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/README.md b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/README.md new file mode 100644 index 0000000..a52f1f3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/README.md @@ -0,0 +1,206 @@ +# Faraday Http Cache + +[![Gem Version](https://badge.fury.io/rb/faraday-http-cache.svg)](https://rubygems.org/gems/faraday-http-cache) +[![Build](https://github.com/sourcelevel/faraday-http-cache/actions/workflows/main.yml/badge.svg)](https://github.com/sourcelevel/faraday-http-cache/actions) + +A [Faraday](https://github.com/lostisland/faraday) middleware that respects HTTP cache, +by checking expiration and validation of the stored responses. + +## Installation + +Add it to your Gemfile: + +```ruby +gem 'faraday-http-cache' +``` + +## Usage and configuration + +You have to use the middleware in the Faraday instance that you want to, +along with a suitable `store` to cache the responses. You can use the new +shortcut using a symbol or passing the middleware class + +```ruby +client = Faraday.new do |builder| + builder.use :http_cache, store: Rails.cache + # or + builder.use Faraday::HttpCache, store: Rails.cache + + builder.adapter Faraday.default_adapter +end +``` + +The middleware accepts a `store` option for the cache backend responsible for recording +the API responses that should be stored. Stores should respond to `write`, `read` and `delete`, +just like an object from the `ActiveSupport::Cache` API. + +```ruby +# Connect the middleware to a Memcache instance. +store = ActiveSupport::Cache.lookup_store(:mem_cache_store, ['localhost:11211']) + +client = Faraday.new do |builder| + builder.use :http_cache, store: store + builder.adapter Faraday.default_adapter +end + +# Or use the Rails.cache instance inside your Rails app. +client = Faraday.new do |builder| + builder.use :http_cache, store: Rails.cache + builder.adapter Faraday.default_adapter +end +``` +The default store provided is a simple in memory cache that lives on the client instance. +This type of store **might not be persisted across multiple processes or connection instances** +so it is probably not suitable for most production environments. +Make sure that you configure a store that is suitable for you. + +The stdlib `JSON` module is used for serialization by default, which can struggle with unicode +characters in responses in Ruby < 3.1. For example, if your JSON returns `"name": "Raül"` then +you might see errors like: + +``` +Response could not be serialized: "\xC3" from ASCII-8BIT to UTF-8. Try using Marshal to serialize. +``` + +For full unicode support, or if you expect to be dealing with images, you can use the stdlib +[Marshal][marshal] instead. Alternatively you could use another json library like `oj` or `yajl-ruby`. + +```ruby +client = Faraday.new do |builder| + builder.use :http_cache, store: Rails.cache, serializer: Marshal + builder.adapter Faraday.default_adapter +end +``` + +### Strategies + +You can provide a `:strategy` option to the middleware to specify the strategy to use. + +```ruby +client = Faraday.new do |builder| + builder.use :http_cache, store: Rails.cache, strategy: Faraday::HttpCache::Strategies::ByVary + builder.adapter Faraday.default_adapter +end +``` + +Available strategies are: + +#### `Faraday::HttpCache::Strategies::ByUrl` + +The default strategy. +It Uses URL + HTTP method to generate cache keys and stores an array of request + response for each key. + +#### `Faraday::HttpCache::Strategies::ByVary` + +This strategy uses headers from `Vary` header to generate cache keys. +It also uses cache to store `Vary` headers mapped to the request URL. +This strategy is more suitable for caching private responses with the same URLs but different results for different users, like `https://api.github.com/user`. + +*Note:* To automatically remove stale cache keys, you might want to use the `:expires_in` option. + +```ruby +store = ActiveSupport::Cache.lookup_store(:redis_cache_store, expires_in: 1.day, url: 'redis://localhost:6379/0') +client = Faraday.new do |builder| + builder.use :http_cache, store: store, strategy: Faraday::HttpCache::Strategies::ByVary + builder.adapter Faraday.default_adapter +end +``` + +#### Custom strategies + +You can write your own strategy by subclassing `Faraday::HttpCache::Strategies::BaseStrategy` and implementing `#write`, `#read` and `#delete` methods. + +### Logging + +You can provide a `:logger` option that will receive debug information based on the middleware +operations: + +```ruby +client = Faraday.new do |builder| + builder.use :http_cache, store: Rails.cache, logger: Rails.logger + builder.adapter Faraday.default_adapter +end + +client.get('https://site/api/users') +# logs "HTTP Cache: [GET users] miss, store" +``` + +### Instrumentation + +In addition to logging you can instrument the middleware by passing in an `:instrumenter` option +such as ActiveSupport::Notifications (compatible objects are also allowed). + +The event `http_cache.faraday` will be published every time the middleware +processes a request. In the event payload, `:env` contains the response Faraday env and +`:cache_status` contains a Symbol indicating the status of the cache processing for that request: + +- `:unacceptable` means that the request did not go through the cache at all. +- `:miss` means that no cached response could be found. +- `:invalid` means that the cached response could not be validated against the server. +- `:valid` means that the cached response *could* be validated against the server. +- `:fresh` means that the cached response was still fresh and could be returned without even + calling the server. + +```ruby +client = Faraday.new do |builder| + builder.use :http_cache, store: Rails.cache, instrumenter: ActiveSupport::Notifications + builder.adapter Faraday.default_adapter +end + +# Subscribes to all events from Faraday::HttpCache. +ActiveSupport::Notifications.subscribe "http_cache.faraday" do |*args| + event = ActiveSupport::Notifications::Event.new(*args) + cache_status = event.payload[:cache_status] + statsd = Statsd.new + + case cache_status + when :fresh, :valid + statsd.increment('api-calls.cache_hits') + when :invalid, :miss + statsd.increment('api-calls.cache_misses') + when :unacceptable + statsd.increment('api-calls.cache_bypass') + end +end +``` + +## See it live + +You can clone this repository, install its dependencies with Bundler (run `bundle install`) and +execute the files under the `examples` directory to see a sample of the middleware usage. + +## What gets cached? + +The middleware will use the following headers to make caching decisions: +- Vary +- Cache-Control +- Age +- Last-Modified +- ETag +- Expires + +### Cache-Control + +The `max-age`, `must-revalidate`, `proxy-revalidate` and `s-maxage` directives are checked. + +### Shared vs. non-shared caches + +By default, the middleware acts as a "shared cache" per RFC 2616. This means it does not cache +responses with `Cache-Control: private`. This behavior can be changed by passing in the +`:shared_cache` configuration option: + +```ruby +client = Faraday.new do |builder| + builder.use :http_cache, shared_cache: false + builder.adapter Faraday.default_adapter +end + +client.get('https://site/api/some-private-resource') # => will be cached +``` + +## License + +Copyright (c) 2012-2018 Plataformatec. +Copyright (c) 2019 SourceLevel and contributors. + + [marshal]: https://www.ruby-doc.org/core-3.0/Marshal.html diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday-http-cache.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday-http-cache.rb new file mode 100644 index 0000000..93ed1d6 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday-http-cache.rb @@ -0,0 +1 @@ +require 'faraday/http_cache' diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache.rb new file mode 100644 index 0000000..d56abd7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache.rb @@ -0,0 +1,355 @@ +# frozen_string_literal: true + +require 'faraday' + +require 'faraday/http_cache/storage' +require 'faraday/http_cache/request' +require 'faraday/http_cache/response' +require 'faraday/http_cache/strategies' + +module Faraday + # Public: The middleware responsible for caching and serving responses. + # The middleware use the provided configuration options to establish on of + # 'Faraday::HttpCache::Strategies' to cache responses retrieved by the stack + # adapter. If a stored response can be served again for a subsequent + # request, the middleware will return the response instead of issuing a new + # request to it's server. This middleware should be the last attached handler + # to your stack, so it will be closest to the inner app, avoiding issues + # with other middlewares on your stack. + # + # Examples: + # + # # Using the middleware with a simple client: + # client = Faraday.new do |builder| + # builder.use :http_cache, store: my_store_backend + # builder.adapter Faraday.default_adapter + # end + # + # # Attach a Logger to the middleware. + # client = Faraday.new do |builder| + # builder.use :http_cache, logger: my_logger_instance, store: my_store_backend + # builder.adapter Faraday.default_adapter + # end + # + # # Provide an existing CacheStore (for instance, from a Rails app) + # client = Faraday.new do |builder| + # builder.use :http_cache, store: Rails.cache + # end + # + # # Use Marshal for serialization + # client = Faraday.new do |builder| + # builder.use :http_cache, store: Rails.cache, serializer: Marshal + # end + # + # # Instrument events using ActiveSupport::Notifications + # client = Faraday.new do |builder| + # builder.use :http_cache, store: Rails.cache, instrumenter: ActiveSupport::Notifications + # end + class HttpCache < Faraday::Middleware + UNSAFE_METHODS = %i[post put delete patch].freeze + + ERROR_STATUSES = (400..499).freeze + + # The name of the instrumentation event. + EVENT_NAME = 'http_cache.faraday' + + CACHE_STATUSES = [ + # The request was not cacheable. + :unacceptable, + + # The response was cached and can still be used. + :fresh, + + # The response was cached and the server has validated it with a 304 response. + :valid, + + # The response was cached but was not revalidated by the server. + :invalid, + + # No response was found in the cache. + :miss, + + # The response can't be cached. + :uncacheable, + + # The request was cached but need to be revalidated by the server. + :must_revalidate + ].freeze + + # Public: Initializes a new HttpCache middleware. + # + # app - the next endpoint on the 'Faraday' stack. + # :store - A cache store that should respond to 'read', 'write', and 'delete'. + # :serializer - A serializer that should respond to 'dump' and 'load'. + # :shared_cache - A flag to mark the middleware as a shared cache or not. + # :instrumenter - An instrumentation object that should respond to 'instrument'. + # :instrument_name - The String name of the instrument being reported on (optional). + # :logger - A logger object. + # + # Examples: + # + # # Initialize the middleware with a logger. + # Faraday::HttpCache.new(app, logger: my_logger) + # + # # Initialize the middleware with a logger and Marshal as a serializer + # Faraday::HttpCache.new(app, logger: my_logger, serializer: Marshal) + # + # # Initialize the middleware with a FileStore at the 'tmp' dir. + # store = ActiveSupport::Cache.lookup_store(:file_store, ['tmp']) + # Faraday::HttpCache.new(app, store: store) + # + # # Initialize the middleware with a MemoryStore and logger + # store = ActiveSupport::Cache.lookup_store + # Faraday::HttpCache.new(app, store: store, logger: my_logger) + def initialize(app, options = {}) + super(app) + + options = options.dup + @logger = options[:logger] + @shared_cache = options.delete(:shared_cache) { true } + @instrumenter = options.delete(:instrumenter) + @instrument_name = options.delete(:instrument_name) { EVENT_NAME } + + strategy = options.delete(:strategy) { Strategies::ByUrl } + + @strategy = strategy.new(**options) + end + + # Public: Process the request into a duplicate of this instance to + # ensure that the internal state is preserved. + def call(env) + dup.call!(env) + end + + # Internal: Process the stack request to try to serve a cache response. + # On a cacheable request, the middleware will attempt to locate a + # valid stored response to serve. On a cache miss, the middleware will + # forward the request and try to store the response for future requests. + # If the request can't be cached, the request will be delegated directly + # to the underlying app and does nothing to the response. + # The processed steps will be recorded to be logged once the whole + # process is finished. + # + # Returns a 'Faraday::Response' instance. + def call!(env) + @trace = [] + @request = create_request(env) + + response = nil + + if @request.cacheable? + response = process(env) + else + trace :unacceptable + response = @app.call(env) + end + + response.on_complete do + delete(@request, response) if should_delete?(response.status, @request.method) + log_request + response.env[:http_cache_trace] = @trace + instrument(response.env) + end + end + + protected + + # Internal: Gets the request object created from the Faraday env Hash. + attr_reader :request + + private + + # Internal: Should this cache instance act like a "shared cache" according + # to the the definition in RFC 2616? + def shared_cache? + @shared_cache + end + + # Internal: Checks if the current request method should remove any existing + # cache entries for the same resource. + # + # Returns true or false. + def should_delete?(status, method) + UNSAFE_METHODS.include?(method) && !ERROR_STATUSES.cover?(status) + end + + # Internal: Tries to locate a valid response or forwards the call to the stack. + # * If no entry is present on the storage, the 'fetch' method will forward + # the call to the remaining stack and return the new response. + # * If a fresh response is found, the middleware will abort the remaining + # stack calls and return the stored response back to the client. + # * If a response is found but isn't fresh anymore, the middleware will + # revalidate the response back to the server. + # + # env - the environment 'Hash' provided from the 'Faraday' stack. + # + # Returns the 'Faraday::Response' instance to be served. + def process(env) + entry = @strategy.read(@request) + + return fetch(env) if entry.nil? + + if entry.fresh? && !@request.no_cache? + response = entry.to_response(env) + trace :fresh + else + trace :must_revalidate + response = validate(entry, env) + end + + response + end + + # Internal: Tries to validated a stored entry back to it's origin server + # using the 'If-Modified-Since' and 'If-None-Match' headers with the + # existing 'Last-Modified' and 'ETag' headers. If the new response + # is marked as 'Not Modified', the previous stored response will be used + # and forwarded against the Faraday stack. Otherwise, the freshly new + # response will be stored (replacing the old one) and used. + # + # entry - a stale 'Faraday::HttpCache::Response' retrieved from the cache. + # env - the environment 'Hash' to perform the request. + # + # Returns the 'Faraday::HttpCache::Response' to be forwarded into the stack. + def validate(entry, env) + headers = env[:request_headers] + headers['If-Modified-Since'] = entry.last_modified if entry.last_modified + headers['If-None-Match'] = entry.etag if entry.etag + + @app.call(env).on_complete do |requested_env| + response = Response.new(requested_env) + if response.not_modified? + trace :valid + updated_response_headers = response.payload[:response_headers] + + # These headers are not allowed in 304 responses, yet some proxy + # servers add them in. Don't override the values from the original + # response. + updated_response_headers.delete('Content-Type') + updated_response_headers.delete('Content-Length') + + updated_payload = entry.payload + updated_payload[:response_headers].update(updated_response_headers) + requested_env.update(updated_payload) + response = Response.new(updated_payload) + else + trace :invalid + end + store(response) + end + end + + # Internal: Records a traced action to be used by the logger once the + # request/response phase is finished. + # + # operation - the name of the performed action, a String or Symbol. + # + # Returns nothing. + def trace(operation) + @trace << operation + end + + # Internal: Stores the response into the storage. + # If the response isn't cacheable, a trace action 'uncacheable' will be + # recorded for logging purposes. + # + # response - a 'Faraday::HttpCache::Response' instance to be stored. + # + # Returns nothing. + def store(response) + if shared_cache? ? response.cacheable_in_shared_cache? : response.cacheable_in_private_cache? + trace :store + @strategy.write(@request, response) + else + trace :uncacheable + end + end + + def delete(request, response) + headers = %w[Location Content-Location] + headers.each do |header| + url = response.headers[header] + @strategy.delete(url) if url + end + + @strategy.delete(request.url) + trace :delete + end + + # Internal: Fetches the response from the Faraday stack and stores it. + # + # env - the environment 'Hash' from the Faraday stack. + # + # Returns the fresh 'Faraday::Response' instance. + def fetch(env) + trace :miss + @app.call(env).on_complete do |fresh_env| + response = Response.new(create_response(fresh_env)) + store(response) + end + end + + # Internal: Creates a new 'Hash' containing the response information. + # + # env - the environment 'Hash' from the Faraday stack. + # + # Returns a 'Hash' containing the ':status', ':body' and 'response_headers' + # entries. + def create_response(env) + hash = env.to_hash + + { + status: hash[:status], + body: hash[:body] || hash[:response_body], + response_headers: hash[:response_headers] + } + end + + def create_request(env) + Request.from_env(env) + end + + # Internal: Logs the trace info about the incoming request + # and how the middleware handled it. + # This method does nothing if theresn't a logger present. + # + # Returns nothing. + def log_request + return unless @logger + + method = @request.method.to_s.upcase + path = @request.url.request_uri + @logger.debug { "HTTP Cache: [#{method} #{path}] #{@trace.join(', ')}" } + end + + # Internal: instruments the request processing. + # + # Returns nothing. + def instrument(env) + return unless @instrumenter + + payload = { + env: env, + cache_status: extract_status(env[:http_cache_trace]) + } + + @instrumenter.instrument(@instrument_name, payload) + # DEPRECATED: Event name from the 1.1.1 release that isn't compatible + # with the `ActiveSupport::LogSubscriber` API. + @instrumenter.instrument('process_request.http_cache.faraday', payload) + end + + # Internal: Extracts the cache status from a trace. + # + # Returns the Symbol status or nil if none was available. + def extract_status(trace) + CACHE_STATUSES.find { |status| trace.include?(status) } + end + end +end + +if Faraday.respond_to?(:register_middleware) + Faraday.register_middleware http_cache: Faraday::HttpCache +elsif Faraday::Middleware.respond_to?(:register_middleware) + Faraday::Middleware.register_middleware http_cache: Faraday::HttpCache +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/cache_control.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/cache_control.rb new file mode 100644 index 0000000..05d546c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/cache_control.rb @@ -0,0 +1,122 @@ +# frozen_string_literal: true + +module Faraday + class HttpCache < Faraday::Middleware + # Internal: A class to represent the 'Cache-Control' header options. + # This implementation is based on 'rack-cache' internals by Ryan Tomayko. + # It breaks the several directives into keys/values and stores them into + # a Hash. + class CacheControl + # Internal: Initialize a new CacheControl. + def initialize(header) + @directives = parse(header.to_s) + end + + # Internal: Checks if the 'public' directive is present. + def public? + @directives['public'] + end + + # Internal: Checks if the 'private' directive is present. + def private? + @directives['private'] + end + + # Internal: Checks if the 'no-cache' directive is present. + def no_cache? + @directives['no-cache'] + end + + # Internal: Checks if the 'no-store' directive is present. + def no_store? + @directives['no-store'] + end + + # Internal: Gets the 'max-age' directive as an Integer. + # + # Returns nil if the 'max-age' directive isn't present. + def max_age + @directives['max-age'].to_i if @directives.key?('max-age') + end + + # Internal: Gets the 'max-age' directive as an Integer. + # + # takes the age header integer value and reduces the max-age and s-maxage + # if present to account for having to remove static age header when caching responses + def normalize_max_ages(age) + if age > 0 + @directives['max-age'] = @directives['max-age'].to_i - age if @directives.key?('max-age') + @directives['s-maxage'] = @directives['s-maxage'].to_i - age if @directives.key?('s-maxage') + end + end + + # Internal: Gets the 's-maxage' directive as an Integer. + # + # Returns nil if the 's-maxage' directive isn't present. + def shared_max_age + @directives['s-maxage'].to_i if @directives.key?('s-maxage') + end + alias s_maxage shared_max_age + + # Internal: Checks if the 'must-revalidate' directive is present. + def must_revalidate? + @directives['must-revalidate'] + end + + # Internal: Checks if the 'proxy-revalidate' directive is present. + def proxy_revalidate? + @directives['proxy-revalidate'] + end + + # Internal: Gets the String representation for the cache directives. + # Directives are joined by a '=' and then combined into a single String + # separated by commas. Directives with a 'true' value will omit the '=' + # sign and their value. + # + # Returns the Cache Control string. + def to_s + booleans = [] + values = [] + + @directives.each do |key, value| + if value == true + booleans << key + elsif value + values << "#{key}=#{value}" + end + end + + (booleans.sort + values.sort).join(', ') + end + + private + + # Internal: Parses the Cache Control string to a Hash. + # Existing whitespace will be removed and the string is split on commas. + # For each part everything before a '=' will be treated as the key + # and the exceeding will be treated as the value. If only the key is + # present then the assigned value will default to true. + # + # Examples: + # parse("max-age=600") + # # => { "max-age" => "600"} + # + # parse("max-age") + # # => { "max-age" => true } + # + # Returns a Hash. + def parse(header) + directives = {} + + header.delete(' ').split(',').each do |part| + next if part.empty? + + name, value = part.split('=', 2) + directives[name.downcase] = (value || true) unless name.empty? + end + + directives + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/memory_store.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/memory_store.rb new file mode 100644 index 0000000..4864b54 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/memory_store.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Faraday + class HttpCache < Faraday::Middleware + # @private + # A Hash based store to be used by strategies + # when a `store` is not provided for the middleware setup. + class MemoryStore + def initialize + @cache = {} + end + + def read(key) + @cache[key] + end + + def delete(key) + @cache.delete(key) + end + + def write(key, value) + @cache[key] = value + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/request.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/request.rb new file mode 100644 index 0000000..4e25824 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/request.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Faraday + class HttpCache < Faraday::Middleware + # Internal: A class to represent a request + class Request + class << self + def from_env(env) + hash = env.to_hash + new(method: hash[:method], url: hash[:url], headers: hash[:request_headers].dup) + end + end + + attr_reader :method, :url, :headers + + def initialize(method:, url:, headers:) + @method = method + @url = url + @headers = headers + end + + # Internal: Validates if the current request method is valid for caching. + # + # Returns true if the method is ':get' or ':head'. + def cacheable? + return false if method != :get && method != :head + return false if cache_control.no_store? + + true + end + + def no_cache? + cache_control.no_cache? + end + + # Internal: Gets the 'CacheControl' object. + def cache_control + @cache_control ||= CacheControl.new(headers['Cache-Control']) + end + + def serializable_hash + { + method: @method, + url: @url.to_s, + headers: @headers + } + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/response.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/response.rb new file mode 100644 index 0000000..50c248d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/response.rb @@ -0,0 +1,234 @@ +# frozen_string_literal: true + +require 'time' +require 'faraday/http_cache/cache_control' + +module Faraday + class HttpCache < Faraday::Middleware + # Internal: A class to represent a response from a Faraday request. + # It decorates the response hash into a smarter object that queries + # the response headers and status informations about how the caching + # middleware should handle this specific response. + class Response + # Internal: List of status codes that can be cached: + # * 200 - 'OK' + # * 203 - 'Non-Authoritative Information' + # * 300 - 'Multiple Choices' + # * 301 - 'Moved Permanently' + # * 302 - 'Found' + # * 404 - 'Not Found' + # * 410 - 'Gone' + CACHEABLE_STATUS_CODES = [200, 203, 300, 301, 302, 307, 404, 410].freeze + + # Internal: Gets the actual response Hash (status, headers and body). + attr_reader :payload + + # Internal: Gets the 'Last-Modified' header from the headers Hash. + attr_reader :last_modified + + # Internal: Gets the 'ETag' header from the headers Hash. + attr_reader :etag + + # Internal: Initialize a new Response with the response payload from + # a Faraday request. + # + # payload - the response Hash returned by a Faraday request. + # :status - the status code from the response. + # :response_headers - a 'Hash' like object with the headers. + # :body - the response body. + def initialize(payload = {}) + @now = Time.now + @payload = payload + wrap_headers! + ensure_date_header! + + @last_modified = headers['Last-Modified'] + @etag = headers['ETag'] + end + + # Internal: Checks the response freshness based on expiration headers. + # The calculated 'ttl' should be present and bigger than 0. + # + # Returns true if the response is fresh, otherwise false. + def fresh? + !cache_control.no_cache? && ttl && ttl > 0 + end + + # Internal: Checks if the Response returned a 'Not Modified' status. + # + # Returns true if the response status code is 304. + def not_modified? + @payload[:status] == 304 + end + + # Internal: Checks if the response can be cached by the client when the + # client is acting as a shared cache per RFC 2616. This is validated by + # the 'Cache-Control' directives, the response status code and it's + # freshness or validation status. + # + # Returns false if the 'Cache-Control' says that we can't store the + # response, or it can be stored in private caches only, or if isn't fresh + # or it can't be revalidated with the origin server. Otherwise, returns + # true. + def cacheable_in_shared_cache? + cacheable?(true) + end + + # Internal: Checks if the response can be cached by the client when the + # client is acting as a private cache per RFC 2616. This is validated by + # the 'Cache-Control' directives, the response status code and it's + # freshness or validation status. + # + # Returns false if the 'Cache-Control' says that we can't store the + # response, or if isn't fresh or it can't be revalidated with the origin + # server. Otherwise, returns true. + def cacheable_in_private_cache? + cacheable?(false) + end + + # Internal: Gets the response age in seconds. + # + # Returns the 'Age' header if present, or subtracts the response 'date' + # from the current time. + def age + (headers['Age'] || (@now - date)).to_i + end + + # Internal: Calculates the 'Time to live' left on the Response. + # + # Returns the remaining seconds for the response, or nil the 'max_age' + # isn't present. + def ttl + max_age - age if max_age + end + + # Internal: Parses the 'Date' header back into a Time instance. + # + # Returns the Time object. + def date + Time.httpdate(headers['Date']) + end + + # Internal: Gets the response max age. + # The max age is extracted from one of the following: + # * The shared max age directive from the 'Cache-Control' header; + # * The max age directive from the 'Cache-Control' header; + # * The difference between the 'Expires' header and the response + # date. + # + # Returns the max age value in seconds or nil if all options above fails. + def max_age + cache_control.shared_max_age || + cache_control.max_age || + (expires && (expires - @now)) + end + + # Internal: Creates a new 'Faraday::Response', merging the stored + # response with the supplied 'env' object. + # + # Returns a new instance of a 'Faraday::Response' with the payload. + def to_response(env) + env.update(@payload) + Faraday::Response.new(env) + end + + # Internal: Exposes a representation of the current + # payload that we can serialize and cache properly. + # + # Returns a 'Hash'. + def serializable_hash + prepare_to_cache + + { + status: @payload[:status], + body: @payload[:body], + response_headers: @payload[:response_headers] + } + end + + private + + # Internal: Checks if this response can be revalidated. + # + # Returns true if the 'headers' contains a 'Last-Modified' or an 'ETag' + # entry. + def validateable? + headers.key?('Last-Modified') || headers.key?('ETag') + end + + # Internal: The logic behind cacheable_in_private_cache? and + # cacheable_in_shared_cache? The logic is the same except for the + # treatment of the private Cache-Control directive. + def cacheable?(shared_cache) + return false if (cache_control.private? && shared_cache) || cache_control.no_store? + + cacheable_status_code? && (validateable? || fresh?) + end + + # Internal: Validates the response status against the + # `CACHEABLE_STATUS_CODES' constant. + # + # Returns true if the constant includes the response status code. + def cacheable_status_code? + CACHEABLE_STATUS_CODES.include?(@payload[:status]) + end + + # Internal: Gets the 'Expires' in a Time object. + # + # Returns the Time object, or nil if the header isn't present or isn't RFC 2616 compliant. + def expires + @expires ||= headers['Expires'] && Time.httpdate(headers['Expires']) rescue nil # rubocop:disable Style/RescueModifier + end + + # Internal: Gets the 'CacheControl' object. + def cache_control + @cache_control ||= CacheControl.new(headers['Cache-Control']) + end + + # Internal: Converts the headers 'Hash' into 'Faraday::Utils::Headers'. + # Faraday actually uses a Hash subclass, `Faraday::Utils::Headers` to + # store the headers hash. When retrieving a serialized response, + # the headers object is decoded as a 'Hash' instead of the actual + # 'Faraday::Utils::Headers' object, so we need to ensure that the + # 'response_headers' is always a 'Headers' instead of a plain 'Hash'. + # + # Returns nothing. + def wrap_headers! + headers = @payload[:response_headers] + + @payload[:response_headers] = Faraday::Utils::Headers.new + @payload[:response_headers].update(headers) if headers + end + + # Internal: Try to parse the Date header, if it fails set it to @now. + # + # Returns nothing. + def ensure_date_header! + date + rescue StandardError + headers['Date'] = @now.httpdate + end + + # Internal: Gets the headers 'Hash' from the payload. + def headers + @payload[:response_headers] + end + + # Internal: Prepares the response headers to be cached. + # + # It removes the 'Age' header if present to allow cached responses + # to continue aging while cached. It also normalizes the 'max-age' + # related headers if the 'Age' header is provided to ensure accuracy + # once the 'Age' header is removed. + # + # Returns nothing. + def prepare_to_cache + if headers.key? 'Age' + cache_control.normalize_max_ages(headers['Age'].to_i) + headers.delete 'Age' + headers['Cache-Control'] = cache_control.to_s + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/storage.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/storage.rb new file mode 100644 index 0000000..f648caa --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/storage.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require 'faraday/http_cache/strategies/by_url' + +module Faraday + class HttpCache < Faraday::Middleware + # @deprecated Use Faraday::HttpCache::Strategies::ByUrl instead. + class Storage < Faraday::HttpCache::Strategies::ByUrl + def initialize(*) + Kernel.warn("Deprecated: #{self.class} is deprecated and will be removed in " \ + 'the next major release. Use Faraday::HttpCache::Strategies::ByUrl instead.') + super + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/strategies.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/strategies.rb new file mode 100644 index 0000000..27e781f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/strategies.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +require 'faraday/http_cache/strategies/by_url' +require 'faraday/http_cache/strategies/by_vary' diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/strategies/base_strategy.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/strategies/base_strategy.rb new file mode 100644 index 0000000..36568f8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/strategies/base_strategy.rb @@ -0,0 +1,94 @@ +# frozen_string_literal: true + +require 'json' +require 'logger' +require 'faraday/http_cache/memory_store' + +module Faraday + class HttpCache < Faraday::Middleware + module Strategies + # Base class for all strategies. + # @abstract + # + # @example + # + # # Creates a new strategy using a MemCached backend from ActiveSupport. + # mem_cache_store = ActiveSupport::Cache.lookup_store(:mem_cache_store, ['localhost:11211']) + # Faraday::HttpCache::Strategies::ByVary.new(store: mem_cache_store) + # + # # Reuse some other instance of an ActiveSupport::Cache::Store object. + # Faraday::HttpCache::Strategies::ByVary.new(store: Rails.cache) + # + # # Creates a new strategy using Marshal for serialization. + # Faraday::HttpCache::Strategies::ByVary.new(store: Rails.cache, serializer: Marshal) + class BaseStrategy + # Returns the underlying cache store object. + attr_reader :cache + + # @param [Hash] options the options to create a message with. + # @option options [Faraday::HttpCache::MemoryStore, nil] :store - a cache + # store object that should respond to 'read', 'write', and 'delete'. + # @option options [#dump#load] :serializer - an object that should + # respond to 'dump' and 'load'. + # @option options [Logger, nil] :logger - an object to be used to emit warnings. + def initialize(options = {}) + @cache = options[:store] || Faraday::HttpCache::MemoryStore.new + @serializer = options[:serializer] || JSON + @logger = options[:logger] || Logger.new(IO::NULL) + @cache_salt = (@serializer.is_a?(Module) ? @serializer : @serializer.class).name + assert_valid_store! + end + + # Store a response inside the cache. + # @abstract + def write(_request, _response) + raise NotImplementedError, 'Implement this method in your strategy' + end + + # Read a response from the cache. + # @abstract + def read(_request) + raise NotImplementedError, 'Implement this method in your strategy' + end + + # Delete responses from the cache by the url. + # @abstract + def delete(_url) + raise NotImplementedError, 'Implement this method in your strategy' + end + + private + + # @private + # @raise [ArgumentError] if the cache object doesn't support the expect API. + def assert_valid_store! + unless cache.respond_to?(:read) && cache.respond_to?(:write) && cache.respond_to?(:delete) + raise ArgumentError.new("#{cache.inspect} is not a valid cache store as it does not responds to 'read', 'write' or 'delete'.") + end + end + + def serialize_entry(*objects) + objects.map { |object| serialize_object(object) } + end + + def serialize_object(object) + @serializer.dump(object) + end + + def deserialize_entry(*objects) + objects.map { |object| deserialize_object(object) } + end + + def deserialize_object(object) + @serializer.load(object).each_with_object({}) do |(key, value), hash| + hash[key.to_sym] = value + end + end + + def warn(message) + @logger.warn(message) + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/strategies/by_url.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/strategies/by_url.rb new file mode 100644 index 0000000..dd60084 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/strategies/by_url.rb @@ -0,0 +1,118 @@ +# frozen_string_literal: true + +require 'digest/sha1' + +require 'faraday/http_cache/strategies/base_strategy' + +module Faraday + class HttpCache < Faraday::Middleware + module Strategies + # The original strategy by Faraday::HttpCache. + # Uses URL + HTTP method to generate cache keys. + class ByUrl < BaseStrategy + # Store a response inside the cache. + # + # @param [Faraday::HttpCache::Request] request - instance of the executed HTTP request. + # @param [Faraday::HttpCache::Response] response - instance to be stored. + # + # @return [void] + def write(request, response) + key = cache_key_for(request.url) + entry = serialize_entry(request.serializable_hash, response.serializable_hash) + entries = cache.read(key) || [] + entries = entries.dup if entries.frozen? + entries.reject! do |(cached_request, cached_response)| + response_matches?(request, deserialize_object(cached_request), deserialize_object(cached_response)) + end + + entries << entry + + cache.write(key, entries) + rescue ::Encoding::UndefinedConversionError => e + warn "Response could not be serialized: #{e.message}. Try using Marshal to serialize." + raise e + end + + # Fetch a stored response that suits the incoming HTTP request or return nil. + # + # @param [Faraday::HttpCache::Request] request - an instance of the incoming HTTP request. + # + # @return [Faraday::HttpCache::Response, nil] + def read(request) + cache_key = cache_key_for(request.url) + entries = cache.read(cache_key) + response = lookup_response(request, entries) + return nil unless response + + Faraday::HttpCache::Response.new(response) + end + + # @param [String] url – the url of a changed resource, will be used to invalidate the cache. + # + # @return [void] + def delete(url) + cache_key = cache_key_for(url) + cache.delete(cache_key) + end + + private + + # Retrieve a response Hash from the list of entries that match the given request. + # + # @param [Faraday::HttpCache::Request] request - an instance of the incoming HTTP request. + # @param [Array] entries - pairs of Hashes (request, response). + # + # @return [Hash, nil] + def lookup_response(request, entries) + if entries + entries = entries.map { |entry| deserialize_entry(*entry) } + _, response = entries.find { |req, res| response_matches?(request, req, res) } + response + end + end + + # Check if a cached response and request matches the given request. + # + # @param [Faraday::HttpCache::Request] request - an instance of the incoming HTTP request. + # @param [Hash] cached_request - a Hash of the request that was cached. + # @param [Hash] cached_response - a Hash of the response that was cached. + # + # @return [true, false] + def response_matches?(request, cached_request, cached_response) + request.method.to_s == cached_request[:method].to_s && + vary_matches?(cached_response, request, cached_request) + end + + # Check if the cached request matches the incoming + # request based on the Vary header of cached response. + # + # If Vary header is not present, the request is considered to match. + # If Vary header is '*', the request is considered to not match. + # + # @param [Faraday::HttpCache::Request] request - an instance of the incoming HTTP request. + # @param [Hash] cached_request - a Hash of the request that was cached. + # @param [Hash] cached_response - a Hash of the response that was cached. + # + # @return [true, false] + def vary_matches?(cached_response, request, cached_request) + headers = Faraday::Utils::Headers.new(cached_response[:response_headers]) + vary = headers['Vary'].to_s + + vary.empty? || (vary != '*' && vary.split(/[\s,]+/).all? do |header| + request.headers[header] == cached_request[:headers][header] + end) + end + + # Computes the cache key for a specific request, taking + # in account the current serializer to avoid cross serialization issues. + # + # @param [String] url - the request URL. + # + # @return [String] + def cache_key_for(url) + Digest::SHA1.hexdigest("#{@cache_salt}#{url}") + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/strategies/by_vary.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/strategies/by_vary.rb new file mode 100644 index 0000000..3f1e956 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/lib/faraday/http_cache/strategies/by_vary.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +require 'digest/sha1' + +require 'faraday/http_cache/strategies/base_strategy' + +module Faraday + class HttpCache < Faraday::Middleware + module Strategies + # This strategy uses headers from the Vary response header to generate cache keys. + # It also uses the index with Vary headers mapped to the request url. + # This strategy is more suitable for caching private responses with the same urls, + # like https://api.github.com/user. + # + # This strategy does not support #delete method to clear cache on unsafe methods. + class ByVary < BaseStrategy + # Store a response inside the cache. + # + # @param [Faraday::HttpCache::Request] request - instance of the executed HTTP request. + # @param [Faraday::HttpCache::Response] response - instance to be stored. + # + # @return [void] + def write(request, response) + vary_cache_key = vary_cache_key_for(request) + headers = Faraday::Utils::Headers.new(response.payload[:response_headers]) + vary = headers['Vary'].to_s + cache.write(vary_cache_key, vary) + + response_cache_key = response_cache_key_for(request, vary) + entry = serialize_object(response.serializable_hash) + cache.write(response_cache_key, entry) + rescue ::Encoding::UndefinedConversionError => e + warn "Response could not be serialized: #{e.message}. Try using Marshal to serialize." + raise e + end + + # Fetch a stored response that suits the incoming HTTP request or return nil. + # + # @param [Faraday::HttpCache::Request] request - an instance of the incoming HTTP request. + # + # @return [Faraday::HttpCache::Response, nil] + def read(request) + vary_cache_key = vary_cache_key_for(request) + vary = cache.read(vary_cache_key) + return nil if vary.nil? || vary == '*' + + cache_key = response_cache_key_for(request, vary) + response = cache.read(cache_key) + return nil if response.nil? + + Faraday::HttpCache::Response.new(deserialize_object(response)) + end + + # This strategy does not support #delete method to clear cache on unsafe methods. + # @return [void] + def delete(_url) + # do nothing since we can't find the key by url + end + + private + + # Computes the cache key for the index with Vary headers. + # + # @param [Faraday::HttpCache::Request] request - instance of the executed HTTP request. + # + # @return [String] + def vary_cache_key_for(request) + method = request.method.to_s + Digest::SHA1.hexdigest("by_vary_index#{@cache_salt}#{method}#{request.url}") + end + + # Computes the cache key for the response. + # + # @param [Faraday::HttpCache::Request] request - instance of the executed HTTP request. + # @param [String] vary - the Vary header value. + # + # @return [String] + def response_cache_key_for(request, vary) + method = request.method.to_s + headers = vary.split(/[\s,]+/).uniq.sort.map { |header| request.headers[header] } + Digest::SHA1.hexdigest("by_vary#{@cache_salt}#{method}#{request.url}#{headers.join}") + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/binary_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/binary_spec.rb new file mode 100644 index 0000000..e5a37d5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/binary_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Faraday::HttpCache do + let(:client) do + Faraday.new(url: ENV['FARADAY_SERVER']) do |stack| + stack.use :http_cache, serializer: Marshal + adapter = ENV['FARADAY_ADAPTER'] + stack.headers['X-Faraday-Adapter'] = adapter + stack.adapter adapter.to_sym + end + end + let(:data) { IO.binread File.expand_path('support/empty.png', __dir__) } + + it 'works fine with binary data' do + expect(client.get('image').body).to eq data + expect(client.get('image').body).to eq data + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/cache_control_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/cache_control_spec.rb new file mode 100644 index 0000000..f9f2088 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/cache_control_spec.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Faraday::HttpCache::CacheControl do + it 'takes a String with multiple name=value pairs' do + cache_control = Faraday::HttpCache::CacheControl.new('max-age=600, max-stale=300, min-fresh=570') + expect(cache_control.max_age).to eq(600) + end + + it 'takes a String with a single flag value' do + cache_control = Faraday::HttpCache::CacheControl.new('no-cache') + expect(cache_control).to be_no_cache + end + + it 'takes a String with a bunch of all kinds of stuff' do + cache_control = + Faraday::HttpCache::CacheControl.new('max-age=600,must-revalidate,min-fresh=3000,foo=bar,baz') + expect(cache_control.max_age).to eq(600) + expect(cache_control).to be_must_revalidate + end + + it 'strips leading and trailing spaces' do + cache_control = Faraday::HttpCache::CacheControl.new(' public, max-age = 600 ') + expect(cache_control).to be_public + expect(cache_control.max_age).to eq(600) + end + + it 'ignores blank segments' do + cache_control = Faraday::HttpCache::CacheControl.new('max-age=600,,s-maxage=300') + expect(cache_control.max_age).to eq(600) + expect(cache_control.shared_max_age).to eq(300) + end + + it 'sorts alphabetically with boolean directives before value directives' do + cache_control = Faraday::HttpCache::CacheControl.new('foo=bar, z, x, y, bling=baz, zoom=zib, b, a') + expect(cache_control.to_s).to eq('a, b, x, y, z, bling=baz, foo=bar, zoom=zib') + end + + it 'responds to #max_age with an integer when max-age directive present' do + cache_control = Faraday::HttpCache::CacheControl.new('public, max-age=600') + expect(cache_control.max_age).to eq(600) + end + + it 'responds to #max_age with nil when no max-age directive present' do + cache_control = Faraday::HttpCache::CacheControl.new('public') + expect(cache_control.max_age).to be_nil + end + + it 'responds to #shared_max_age with an integer when s-maxage directive present' do + cache_control = Faraday::HttpCache::CacheControl.new('public, s-maxage=600') + expect(cache_control.shared_max_age).to eq(600) + end + + it 'responds to #shared_max_age with nil when no s-maxage directive present' do + cache_control = Faraday::HttpCache::CacheControl.new('public') + expect(cache_control.shared_max_age).to be_nil + end + + it 'responds to #public? truthfully when public directive present' do + cache_control = Faraday::HttpCache::CacheControl.new('public') + expect(cache_control).to be_public + end + + it 'responds to #public? non-truthfully when no public directive present' do + cache_control = Faraday::HttpCache::CacheControl.new('private') + expect(cache_control).not_to be_public + end + + it 'responds to #private? truthfully when private directive present' do + cache_control = Faraday::HttpCache::CacheControl.new('private') + expect(cache_control).to be_private + end + + it 'responds to #private? non-truthfully when no private directive present' do + cache_control = Faraday::HttpCache::CacheControl.new('public') + expect(cache_control).not_to be_private + end + + it 'responds to #no_cache? truthfully when no-cache directive present' do + cache_control = Faraday::HttpCache::CacheControl.new('no-cache') + expect(cache_control).to be_no_cache + end + + it 'responds to #no_cache? non-truthfully when no no-cache directive present' do + cache_control = Faraday::HttpCache::CacheControl.new('max-age=600') + expect(cache_control).not_to be_no_cache + end + + it 'responds to #must_revalidate? truthfully when must-revalidate directive present' do + cache_control = Faraday::HttpCache::CacheControl.new('must-revalidate') + expect(cache_control).to be_must_revalidate + end + + it 'responds to #must_revalidate? non-truthfully when no must-revalidate directive present' do + cache_control = Faraday::HttpCache::CacheControl.new('max-age=600') + expect(cache_control).not_to be_must_revalidate + end + + it 'responds to #proxy_revalidate? truthfully when proxy-revalidate directive present' do + cache_control = Faraday::HttpCache::CacheControl.new('proxy-revalidate') + expect(cache_control).to be_proxy_revalidate + end + + it 'responds to #proxy_revalidate? non-truthfully when no proxy-revalidate directive present' do + cache_control = Faraday::HttpCache::CacheControl.new('max-age=600') + expect(cache_control).not_to be_no_cache + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/http_cache_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/http_cache_spec.rb new file mode 100644 index 0000000..02f7e39 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/http_cache_spec.rb @@ -0,0 +1,296 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Faraday::HttpCache do + let(:logger) { double('a Logger object', debug: nil, warn: nil) } + let(:options) { { logger: logger } } + + let(:client) do + Faraday.new(url: ENV['FARADAY_SERVER']) do |stack| + stack.use Faraday::HttpCache, options + adapter = ENV['FARADAY_ADAPTER'] + stack.headers['X-Faraday-Adapter'] = adapter + stack.headers['Content-Type'] = 'application/x-www-form-urlencoded' + stack.adapter adapter.to_sym + end + end + + before do + client.get('clear') + end + + it 'does not cache POST requests' do + client.post('post').body + expect(client.post('post').body).to eq('2') + end + + it 'logs that a POST request is unacceptable' do + expect(logger).to receive(:debug) { |&block| expect(block.call).to eq('HTTP Cache: [POST /post] unacceptable, delete') } + client.post('post').body + end + + it 'does not cache responses with , status code' do + client.get('broken') + expect(client.get('broken').body).to eq('2') + end + + it 'adds a trace of the actions performed to the env' do + response = client.post('post') + expect(response.env[:http_cache_trace]).to eq(%i[unacceptable delete]) + end + + describe 'cache invalidation' do + it 'expires POST requests' do + client.get('counter') + client.post('counter') + expect(client.get('counter').body).to eq('2') + end + + it 'logs that a POST request was deleted from the cache' do + expect(logger).to receive(:debug) { |&block| expect(block.call).to eq('HTTP Cache: [POST /counter] unacceptable, delete') } + client.post('counter') + end + + it 'does not expires POST requests that failed' do + client.get('get') + client.post('get') + expect(client.get('get').body).to eq('1') + end + + it 'expires PUT requests' do + client.get('counter') + client.put('counter') + expect(client.get('counter').body).to eq('2') + end + + it 'logs that a PUT request was deleted from the cache' do + expect(logger).to receive(:debug) { |&block| expect(block.call).to eq('HTTP Cache: [PUT /counter] unacceptable, delete') } + client.put('counter') + end + + it 'expires DELETE requests' do + client.get('counter') + client.delete('counter') + expect(client.get('counter').body).to eq('2') + end + + it 'logs that a DELETE request was deleted from the cache' do + expect(logger).to receive(:debug) { |&block| expect(block.call).to eq('HTTP Cache: [DELETE /counter] unacceptable, delete') } + client.delete('counter') + end + + it 'expires PATCH requests' do + client.get('counter') + client.patch('counter') + expect(client.get('counter').body).to eq('2') + end + + it 'logs that a PATCH request was deleted from the cache' do + expect(logger).to receive(:debug) { |&block| expect(block.call).to eq('HTTP Cache: [PATCH /counter] unacceptable, delete') } + client.patch('counter') + end + + it 'logs that a response with a bad status code is uncacheable' do + expect(logger).to receive(:debug) { |&block| expect(block.call).to eq('HTTP Cache: [GET /broken] miss, uncacheable') } + client.get('broken') + end + + it 'expires entries for the "Location" header' do + client.get('get') + client.post('delete-with-location') + expect(client.get('get').body).to eq('2') + end + + it 'expires entries for the "Content-Location" header' do + client.get('get') + client.post('delete-with-content-location') + expect(client.get('get').body).to eq('2') + end + end + + describe 'when acting as a shared cache' do + let(:options) { { logger: logger, shared_cache: true } } + + it 'does not cache requests with a private cache control' do + client.get('private') + expect(client.get('private').body).to eq('2') + end + + it 'logs that a private response is uncacheable' do + expect(logger).to receive(:debug) { |&block| expect(block.call).to eq('HTTP Cache: [GET /private] miss, uncacheable') } + client.get('private') + end + end + + describe 'when acting as a private cache' do + let(:options) { { logger: logger, shared_cache: false } } + + it 'does cache requests with a private cache control' do + client.get('private') + expect(client.get('private').body).to eq('1') + end + + it 'logs that a private response is stored' do + expect(logger).to receive(:debug) { |&block| expect(block.call).to eq('HTTP Cache: [GET /private] miss, store') } + client.get('private') + end + end + + it 'does not cache responses with a explicit no-store directive' do + client.get('dontstore') + expect(client.get('dontstore').body).to eq('2') + end + + it 'logs that a response with a no-store directive is uncacheable' do + expect(logger).to receive(:debug) { |&block| expect(block.call).to eq('HTTP Cache: [GET /dontstore] miss, uncacheable') } + client.get('dontstore') + end + + it 'does not caches multiple responses when the headers differ' do + client.get('get', nil, 'HTTP_ACCEPT' => 'text/html') + expect(client.get('get', nil, 'HTTP_ACCEPT' => 'text/html').body).to eq('1') + expect(client.get('get', nil, 'HTTP_ACCEPT' => 'application/json').body).to eq('1') + end + + it 'caches multiples responses based on the "Vary" header' do + client.get('vary', nil, 'User-Agent' => 'Agent/1.0') + expect(client.get('vary', nil, 'User-Agent' => 'Agent/1.0').body).to eq('1') + expect(client.get('vary', nil, 'User-Agent' => 'Agent/2.0').body).to eq('2') + expect(client.get('vary', nil, 'User-Agent' => 'Agent/3.0').body).to eq('3') + end + + it 'never caches responses with the wildcard "Vary" header' do + client.get('vary-wildcard') + expect(client.get('vary-wildcard').body).to eq('2') + end + + it 'caches requests with the "Expires" header' do + client.get('expires') + expect(client.get('expires').body).to eq('1') + end + + it 'logs that a request with the "Expires" is fresh and stored' do + expect(logger).to receive(:debug) { |&block| expect(block.call).to eq('HTTP Cache: [GET /expires] miss, store') } + client.get('expires') + end + + it 'caches GET responses' do + client.get('get') + expect(client.get('get').body).to eq('1') + end + + context 'when the request has a "no-cache" directive' do + it 'revalidates the cache' do + expect(client.get('etag').body).to eq('1') + expect(client.get('etag', nil, 'Cache-Control' => 'no-cache').body).to eq('1') + + expect(client.get('get', nil).body).to eq('2') + expect(client.get('etag', nil, 'Cache-Control' => 'no-cache').body).to eq('3') + end + + it 'caches the response' do + client.get('get', nil, 'Cache-Control' => 'no-cache') + expect(client.get('get', nil).body).to eq('1') + end + end + + context 'when the response has a "no-cache" directive' do + it 'always revalidate the cached response' do + client.get('no_cache') + expect(client.get('no_cache').body).to eq('2') + expect(client.get('no_cache').body).to eq('3') + end + end + + it 'logs that a GET response is stored' do + expect(logger).to receive(:debug) { |&block| expect(block.call).to eq('HTTP Cache: [GET /get] miss, store') } + client.get('get') + end + + it 'differs requests with different query strings in the log' do + expect(logger).to receive(:debug) { |&block| expect(block.call).to eq('HTTP Cache: [GET /get] miss, store') } + expect(logger).to receive(:debug) { |&block| expect(block.call).to eq('HTTP Cache: [GET /get?q=what] miss, store') } + client.get('get') + client.get('get', q: 'what') + end + + it 'logs that a stored GET response is fresh' do + client.get('get') + expect(logger).to receive(:debug) { |&block| expect(block.call).to eq('HTTP Cache: [GET /get] fresh') } + client.get('get') + end + + it 'sends the "Last-Modified" header on response validation' do + client.get('timestamped') + expect(client.get('timestamped').body).to eq('1') + end + + it 'logs that the request with "Last-Modified" was revalidated' do + client.get('timestamped') + expect(logger).to receive(:debug) { |&block| expect(block.call).to eq('HTTP Cache: [GET /timestamped] must_revalidate, valid, store') } + expect(client.get('timestamped').body).to eq('1') + end + + it 'sends the "If-None-Match" header on response validation' do + client.get('etag') + expect(client.get('etag').body).to eq('1') + end + + it 'logs that the request with "ETag" was revalidated' do + client.get('etag') + expect(logger).to receive(:debug) { |&block| expect(block.call).to eq('HTTP Cache: [GET /etag] must_revalidate, valid, store') } + expect(client.get('etag').body).to eq('1') + end + + it 'maintains the "Date" header for cached responses' do + first_date = client.get('get').headers['Date'] + second_date = client.get('get').headers['Date'] + expect(first_date).to eq(second_date) + end + + it 'preserves an old "Date" header if present' do + date = client.get('yesterday').headers['Date'] + expect(date).to match(/^\w{3}, \d{2} \w{3} \d{4} \d{2}:\d{2}:\d{2} GMT$/) + end + + it 'updates the "Cache-Control" header when a response is validated' do + first_cache_control = client.get('etag').headers['Cache-Control'] + second_cache_control = client.get('etag').headers['Cache-Control'] + expect(first_cache_control).not_to eql(second_cache_control) + end + + it 'updates the "Date" header when a response is validated' do + first_date = client.get('etag').headers['Date'] + second_date = client.get('etag').headers['Date'] + expect(first_date).not_to eql(second_date) + end + + it 'updates the "Expires" header when a response is validated' do + first_expires = client.get('etag').headers['Expires'] + second_expires = client.get('etag').headers['Expires'] + expect(first_expires).not_to eql(second_expires) + end + + it 'updates the "Vary" header when a response is validated' do + first_vary = client.get('etag').headers['Vary'] + second_vary = client.get('etag').headers['Vary'] + expect(first_vary).not_to eql(second_vary) + end + + it 'caches non-stale response with "must-revalidate" directive' do + client.get('must-revalidate') + expect(client.get('must-revalidate').body).to eq('1') + end + + describe 'Configuration options' do + let(:app) { double('it is an app!') } + + it 'uses the options to create a Cache Store' do + store = double(read: nil, write: nil) + + expect(Faraday::HttpCache::Strategies::ByUrl).to receive(:new).with(hash_including(store: store)) + Faraday::HttpCache.new(app, store: store) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/instrumentation_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/instrumentation_spec.rb new file mode 100644 index 0000000..fa3f6a8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/instrumentation_spec.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'active_support' +require 'active_support/notifications' + +describe 'Instrumentation' do + let(:backend) { Faraday::Adapter::Test::Stubs.new } + + let(:client) do + Faraday.new do |stack| + stack.use Faraday::HttpCache, instrumenter: ActiveSupport::Notifications + stack.adapter :test, backend + end + end + + let(:events) { [] } + let(:subscriber) { lambda { |*args| events << ActiveSupport::Notifications::Event.new(*args) } } + + around do |example| + ActiveSupport::Notifications.subscribed(subscriber, 'http_cache.faraday') do + example.run + end + end + + describe 'the :cache_status payload entry' do + it 'is :miss if there is no cache entry for the URL' do + backend.get('/hello') do + [200, { 'Cache-Control' => 'public, max-age=999' }, ''] + end + + client.get('/hello') + expect(events.last.payload.fetch(:cache_status)).to eq(:miss) + end + + it 'is :fresh if the cache entry has not expired' do + backend.get('/hello') do + [200, { 'Cache-Control' => 'public, max-age=999' }, ''] + end + + client.get('/hello') # miss + client.get('/hello') # fresh! + expect(events.last.payload.fetch(:cache_status)).to eq(:fresh) + end + + it 'is :valid if the cache entry can be validated against the upstream' do + backend.get('/hello') do + headers = { + 'Cache-Control' => 'public, must-revalidate, max-age=0', + 'Etag' => '123ABCD' + } + + [200, headers, ''] + end + + client.get('/hello') # miss + + backend.get('/hello') { [304, {}, ''] } + + client.get('/hello') # valid! + expect(events.last.payload.fetch(:cache_status)).to eq(:valid) + end + + it 'is :invalid if the cache entry could not be validated against the upstream' do + backend.get('/hello') do + headers = { + 'Cache-Control' => 'public, must-revalidate, max-age=0', + 'Etag' => '123ABCD' + } + + [200, headers, ''] + end + + client.get('/hello') # miss + + backend.get('/hello') { [200, {}, ''] } + + client.get('/hello') # invalid! + expect(events.last.payload.fetch(:cache_status)).to eq(:invalid) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/json_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/json_spec.rb new file mode 100644 index 0000000..c025eed --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/json_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Faraday::HttpCache do + let(:client) do + Faraday.new(url: ENV['FARADAY_SERVER']) do |stack| + stack.response :json, content_type: /\bjson$/ + stack.use :http_cache + adapter = ENV['FARADAY_ADAPTER'] + stack.headers['X-Faraday-Adapter'] = adapter + stack.adapter adapter.to_sym + end + end + + it 'works fine with other middlewares' do + client.get('clear') + expect(client.get('json').body['count']).to eq(1) + expect(client.get('json').body['count']).to eq(1) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/request_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/request_spec.rb new file mode 100644 index 0000000..0753642 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/request_spec.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Faraday::HttpCache::Request do + subject { Faraday::HttpCache::Request.new method: method, url: url, headers: headers } + let(:method) { :get } + let(:url) { URI.parse('http://example.com/path/to/somewhere') } + let(:headers) { {} } + + context 'a GET request' do + it { should be_cacheable } + end + + context 'a HEAD request' do + let(:method) { :head } + it { should be_cacheable } + end + + context 'a POST request' do + let(:method) { :post } + it { should_not be_cacheable } + end + + context 'a PUT request' do + let(:method) { :put } + it { should_not be_cacheable } + end + + context 'an OPTIONS request' do + let(:method) { :options } + it { should_not be_cacheable } + end + + context 'a DELETE request' do + let(:method) { :delete } + it { should_not be_cacheable } + end + + context 'a TRACE request' do + let(:method) { :trace } + it { should_not be_cacheable } + end + + context 'with "Cache-Control: no-store"' do + let(:headers) { { 'Cache-Control' => 'no-store' } } + it { should_not be_cacheable } + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/response_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/response_spec.rb new file mode 100644 index 0000000..6209817 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/response_spec.rb @@ -0,0 +1,246 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Faraday::HttpCache::Response do + describe 'cacheable_in_shared_cache?' do + it 'the response is not cacheable if the response is marked as private' do + headers = { 'Cache-Control' => 'private, max-age=400' } + response = Faraday::HttpCache::Response.new(status: 200, response_headers: headers) + + expect(response).not_to be_cacheable_in_shared_cache + end + + it 'the response is not cacheable if it should not be stored' do + headers = { 'Cache-Control' => 'no-store, max-age=400' } + response = Faraday::HttpCache::Response.new(status: 200, response_headers: headers) + + expect(response).not_to be_cacheable_in_shared_cache + end + + it 'the response is not cacheable when the status code is not acceptable' do + headers = { 'Cache-Control' => 'max-age=400' } + response = Faraday::HttpCache::Response.new(status: 503, response_headers: headers) + expect(response).not_to be_cacheable_in_shared_cache + end + + [200, 203, 300, 301, 302, 307, 404, 410].each do |status| + it "the response is cacheable if the status code is #{status} and the response is fresh" do + headers = { 'Cache-Control' => 'max-age=400' } + response = Faraday::HttpCache::Response.new(status: status, response_headers: headers) + + expect(response).to be_cacheable_in_shared_cache + end + end + end + + describe 'cacheable_in_private_cache?' do + it 'the response is cacheable if the response is marked as private' do + headers = { 'Cache-Control' => 'private, max-age=400' } + response = Faraday::HttpCache::Response.new(status: 200, response_headers: headers) + + expect(response).to be_cacheable_in_private_cache + end + + it 'the response is not cacheable if it should not be stored' do + headers = { 'Cache-Control' => 'no-store, max-age=400' } + response = Faraday::HttpCache::Response.new(status: 200, response_headers: headers) + + expect(response).not_to be_cacheable_in_private_cache + end + + it 'the response is not cacheable when the status code is not acceptable' do + headers = { 'Cache-Control' => 'max-age=400' } + response = Faraday::HttpCache::Response.new(status: 503, response_headers: headers) + expect(response).not_to be_cacheable_in_private_cache + end + + [200, 203, 300, 301, 302, 307, 404, 410].each do |status| + it "the response is cacheable if the status code is #{status} and the response is fresh" do + headers = { 'Cache-Control' => 'max-age=400' } + response = Faraday::HttpCache::Response.new(status: status, response_headers: headers) + + expect(response).to be_cacheable_in_private_cache + end + end + end + + describe 'freshness' do + it 'is fresh if the response still has some time to live' do + date = (Time.now - 200).httpdate + headers = { 'Cache-Control' => 'max-age=400', 'Date' => date } + response = Faraday::HttpCache::Response.new(response_headers: headers) + + expect(response).to be_fresh + end + + it 'is not fresh if the ttl has expired' do + date = (Time.now - 500).httpdate + headers = { 'Cache-Control' => 'max-age=400', 'Date' => date } + response = Faraday::HttpCache::Response.new(response_headers: headers) + + expect(response).not_to be_fresh + end + + it 'is not fresh if Cache Control has "no-cache"' do + date = (Time.now - 200).httpdate + headers = { 'Cache-Control' => 'max-age=400, no-cache', 'Date' => date } + response = Faraday::HttpCache::Response.new(response_headers: headers) + + expect(response).not_to be_fresh + end + + it 'is fresh if the response contains "must-revalidate" and is not stale' do + date = (Time.now - 200).httpdate + headers = { 'Cache-Control' => 'public, max-age=23880, must-revalidate, no-transform', 'Date' => date } + response = Faraday::HttpCache::Response.new(response_headers: headers) + + expect(response).to be_fresh + end + + it 'is not fresh if Cache Control has "must-revalidate" and is stale' do + date = (Time.now - 500).httpdate + headers = { 'Cache-Control' => 'max-age=400, must-revalidate', 'Date' => date } + response = Faraday::HttpCache::Response.new(response_headers: headers) + + expect(response).not_to be_fresh + end + end + + it 'sets the "Date" header if is not present' do + headers = { 'Date' => nil } + response = Faraday::HttpCache::Response.new(response_headers: headers) + + expect(response.date).to be + end + + it 'sets the "Date" header if is not a valid RFC 2616 compliant string' do + date = Time.now.httpdate + headers = { 'Date' => "#{date}, #{date}" } + response = Faraday::HttpCache::Response.new(response_headers: headers) + + expect(response.date).to be + end + + it 'the response is not modified if the status code is 304' do + response = Faraday::HttpCache::Response.new(status: 304) + expect(response).to be_not_modified + end + + it 'returns the "Last-Modified" header on the #last_modified method' do + headers = { 'Last-Modified' => '123' } + response = Faraday::HttpCache::Response.new(response_headers: headers) + expect(response.last_modified).to eq('123') + end + + it 'returns the "ETag" header on the #etag method' do + headers = { 'ETag' => 'tag' } + response = Faraday::HttpCache::Response.new(response_headers: headers) + expect(response.etag).to eq('tag') + end + + describe 'max age calculation' do + it 'uses the shared max age directive when present' do + headers = { 'Cache-Control' => 's-maxage=200, max-age=0' } + response = Faraday::HttpCache::Response.new(response_headers: headers) + expect(response.max_age).to be(200) + end + + it 'uses the max age directive when present' do + headers = { 'Cache-Control' => 'max-age=200' } + response = Faraday::HttpCache::Response.new(response_headers: headers) + expect(response.max_age).to be(200) + end + + it 'fallsback to the expiration date leftovers' do + headers = { 'Expires' => (Time.now + 100).httpdate, 'Date' => Time.now.httpdate } + response = Faraday::HttpCache::Response.new(response_headers: headers) + + expect(response.max_age).to be < 100 + expect(response.max_age).to be > 98 + end + + it 'returns nil when there is no information to calculate the max age' do + response = Faraday::HttpCache::Response.new + expect(response.max_age).to be_nil + end + + it 'returns nil when falling back to expiration date but it is not RFC 2616 compliant' do + headers = { 'Expires' => 'Mon, 1 Jan 2001 00:00:00 GMT' } + response = Faraday::HttpCache::Response.new(response_headers: headers) + expect(response.max_age).to be_nil + end + end + + describe 'age calculation' do + it 'uses the "Age" header if it is present' do + response = Faraday::HttpCache::Response.new(response_headers: { 'Age' => '3' }) + expect(response.age).to eq(3) + end + + it 'calculates the time from the "Date" header' do + date = (Time.now - 3).httpdate + response = Faraday::HttpCache::Response.new(response_headers: { 'Date' => date }) + expect(response.age).to eq(3) + end + + it 'returns 0 if there is no "Age" or "Date" header present' do + response = Faraday::HttpCache::Response.new(response_headers: {}) + expect(response.age).to eq(0) + end + end + + describe 'time to live calculation' do + it 'returns the time to live based on the max age limit' do + date = (Time.now - 200).httpdate + headers = { 'Cache-Control' => 'max-age=400', 'Date' => date } + response = Faraday::HttpCache::Response.new(response_headers: headers) + expect(response.ttl).to eq(200) + end + end + + describe 'response unboxing' do + subject { described_class.new(status: 200, response_headers: {}, body: 'Hi!') } + + let(:env) { { method: :get } } + let(:response) { subject.to_response(env) } + + it 'merges the supplied env object with the response data' do + expect(response.env[:method]).to be + end + + it 'returns a Faraday::Response' do + expect(response).to be_a(Faraday::Response) + end + + it 'merges the status code' do + expect(response.status).to eq(200) + end + + it 'merges the headers' do + expect(response.headers).to be_a(Faraday::Utils::Headers) + end + + it 'merges the body' do + expect(response.body).to eq('Hi!') + end + end + + describe 'remove age before caching and normalize max-age if non-zero age present' do + it 'is fresh if the response still has some time to live' do + headers = { + 'Age' => 6, + 'Cache-Control' => 'public, max-age=40', + 'Date' => (Time.now - 38).httpdate, + 'Expires' => (Time.now - 37).httpdate, + 'Last-Modified' => (Time.now - 300).httpdate + } + response = Faraday::HttpCache::Response.new(response_headers: headers) + expect(response).to be_fresh + + response.serializable_hash + expect(response.max_age).to eq(34) + expect(response).not_to be_fresh + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/spec_helper.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/spec_helper.rb new file mode 100644 index 0000000..2611303 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/spec_helper.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'uri' +require 'socket' + +require 'faraday-http-cache' + +if Gem::Version.new(Faraday::VERSION) < Gem::Version.new('1.0') + require 'faraday_middleware' +elsif ENV['FARADAY_ADAPTER'] == 'em_http' + require 'faraday/em_http' +end + +require 'active_support' +require 'active_support/cache' + +require 'support/test_app' +require 'support/test_server' + +server = TestServer.new + +ENV['FARADAY_SERVER'] = server.endpoint +ENV['FARADAY_ADAPTER'] ||= 'net_http' + +server.start + +RSpec.configure do |config| + config.run_all_when_everything_filtered = true + config.order = 'random' + + config.after(:suite) do + server.stop + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/storage_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/storage_spec.rb new file mode 100644 index 0000000..3a3b05b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/storage_spec.rb @@ -0,0 +1,161 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Faraday::HttpCache::Storage do + let(:cache_key) { '6e3b941d0f7572291c777b3e48c04b74124a55d0' } + let(:request) do + env = { method: :get, url: 'http://test/index' } + double(env.merge(serializable_hash: env)) + end + + let(:response) { double(serializable_hash: { response_headers: {} }) } + + let(:cache) { Faraday::HttpCache::MemoryStore.new } + + let(:storage) { Faraday::HttpCache::Storage.new(store: cache) } + subject { storage } + + before do + allow(Kernel).to receive(:warn).with( + 'Deprecated: Faraday::HttpCache::Storage is deprecated and will be removed '\ + 'in the next major release. Use Faraday::HttpCache::Strategies::ByUrl instead.' + ) + end + + it 'creates strategy and warns about deprecation' do + expect(Kernel).to receive(:warn).with( + 'Deprecated: Faraday::HttpCache::Storage is deprecated and will be removed '\ + 'in the next major release. Use Faraday::HttpCache::Strategies::ByUrl instead.' + ) + is_expected.to be_a_kind_of(Faraday::HttpCache::Strategies::ByUrl) + end + + describe 'Cache configuration' do + it 'uses a MemoryStore by default' do + expect(Faraday::HttpCache::MemoryStore).to receive(:new).and_call_original + Faraday::HttpCache::Storage.new + end + + it 'raises an error when the given store is not valid' do + wrong = double + + expect { + Faraday::HttpCache::Storage.new(store: wrong) + }.to raise_error(ArgumentError) + end + end + + describe 'storing responses' do + shared_examples 'A storage with serialization' do + it 'writes the response object to the underlying cache' do + entry = [serializer.dump(request.serializable_hash), serializer.dump(response.serializable_hash)] + expect(cache).to receive(:write).with(cache_key, [entry]) + subject.write(request, response) + end + end + + context 'with the JSON serializer' do + let(:serializer) { JSON } + it_behaves_like 'A storage with serialization' + + context 'when ASCII characters in response cannot be converted to UTF-8', if: Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.1') do + let(:response) do + body = String.new("\u2665").force_encoding('ASCII-8BIT') + double(:response, serializable_hash: { 'body' => body }) + end + + it 'raises and logs a warning' do + logger = double(:logger, warn: nil) + storage = Faraday::HttpCache::Storage.new(logger: logger) + + expect { + storage.write(request, response) + }.to raise_error(::Encoding::UndefinedConversionError) + expect(logger).to have_received(:warn).with( + 'Response could not be serialized: "\xE2" from ASCII-8BIT to UTF-8. Try using Marshal to serialize.' + ) + end + end + end + + context 'with the Marshal serializer' do + let(:cache_key) { '337d1e9c6c92423dd1c48a23054139058f97be40' } + let(:serializer) { Marshal } + let(:storage) { Faraday::HttpCache::Storage.new(store: cache, serializer: Marshal) } + + it_behaves_like 'A storage with serialization' + end + end + + describe 'reading responses' do + let(:storage) { Faraday::HttpCache::Storage.new(store: cache, serializer: serializer) } + + shared_examples 'A storage with serialization' do + it 'returns nil if the response is not cached' do + expect(subject.read(request)).to be_nil + end + + it 'decodes a stored response' do + subject.write(request, response) + + expect(subject.read(request)).to be_a(Faraday::HttpCache::Response) + end + end + + context 'with the JSON serializer' do + let(:serializer) { JSON } + + it_behaves_like 'A storage with serialization' + end + + context 'with the Marshal serializer' do + let(:serializer) { Marshal } + + it_behaves_like 'A storage with serialization' + end + end + + describe 'deleting responses' do + it 'removes the entries from the cache of the given URL' do + subject.write(request, response) + subject.delete(request.url) + expect(subject.read(request)).to be_nil + end + end + + describe 'remove age before caching and normalize max-age if non-zero age present' do + it 'is fresh if the response still has some time to live' do + headers = { + 'Age' => 6, + 'Cache-Control' => 'public, max-age=40', + 'Date' => (Time.now - 38).httpdate, + 'Expires' => (Time.now - 37).httpdate, + 'Last-Modified' => (Time.now - 300).httpdate + } + response = Faraday::HttpCache::Response.new(response_headers: headers) + expect(response).to be_fresh + subject.write(request, response) + + cached_response = subject.read(request) + expect(cached_response.max_age).to eq(34) + expect(cached_response).not_to be_fresh + end + + it 'is fresh until cached and that 1 second elapses then the response is no longer fresh' do + current_time = Time.now + headers = { + 'Date' => (current_time - 39).httpdate, + 'Expires' => (current_time + 40).httpdate + } + + response = Faraday::HttpCache::Response.new(response_headers: headers) + expect(response).to be_fresh + subject.write(request, response) + + sleep(1) + cached_response = subject.read(request) + expect(cached_response).not_to be_fresh + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/strategies/base_strategy_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/strategies/base_strategy_spec.rb new file mode 100644 index 0000000..4aaa9c5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/strategies/base_strategy_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Faraday::HttpCache::Strategies::BaseStrategy do + subject(:strategy) { described_class.new } + + it 'uses a MemoryStore as a default store' do + expect(Faraday::HttpCache::MemoryStore).to receive(:new).and_call_original + strategy + end + + context 'when the given store is not valid' do + let(:store) { double(:wrong_store) } + subject(:strategy) { described_class.new(store: store) } + + it 'raises an error' do + expect { strategy }.to raise_error(ArgumentError) + end + end + + it 'raises an error when abstract methods are called' do + expect { strategy.write(nil, nil) }.to raise_error(NotImplementedError) + expect { strategy.read(nil) }.to raise_error(NotImplementedError) + expect { strategy.delete(nil) }.to raise_error(NotImplementedError) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/strategies/by_url_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/strategies/by_url_spec.rb new file mode 100644 index 0000000..301a2e3 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/strategies/by_url_spec.rb @@ -0,0 +1,145 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Faraday::HttpCache::Strategies::ByUrl do + let(:cache_key) { '6e3b941d0f7572291c777b3e48c04b74124a55d0' } + let(:request) do + env = { method: :get, url: 'http://test/index' } + double(env.merge(serializable_hash: env)) + end + + let(:response) { double(serializable_hash: { response_headers: {} }) } + + let(:cache) { Faraday::HttpCache::MemoryStore.new } + + let(:strategy) { described_class.new(store: cache) } + subject { strategy } + + describe 'Cache configuration' do + it 'uses a MemoryStore by default' do + expect(Faraday::HttpCache::MemoryStore).to receive(:new).and_call_original + described_class.new + end + + it 'raises an error when the given store is not valid' do + wrong = double + + expect { + described_class.new(store: wrong) + }.to raise_error(ArgumentError) + end + end + + describe 'storing responses' do + shared_examples 'A strategy with serialization' do + it 'writes the response object to the underlying cache' do + entry = [serializer.dump(request.serializable_hash), serializer.dump(response.serializable_hash)] + expect(cache).to receive(:write).with(cache_key, [entry]) + subject.write(request, response) + end + end + + context 'with the JSON serializer' do + let(:serializer) { JSON } + it_behaves_like 'A strategy with serialization' + + context 'when ASCII characters in response cannot be converted to UTF-8', if: Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.1') do + let(:response) do + body = String.new("\u2665").force_encoding('ASCII-8BIT') + double(:response, serializable_hash: { 'body' => body }) + end + + it 'raises and logs a warning' do + logger = double(:logger, warn: nil) + strategy = described_class.new(logger: logger) + + expect { + strategy.write(request, response) + }.to raise_error(::Encoding::UndefinedConversionError) + expect(logger).to have_received(:warn).with( + 'Response could not be serialized: "\xE2" from ASCII-8BIT to UTF-8. Try using Marshal to serialize.' + ) + end + end + end + + context 'with the Marshal serializer' do + let(:cache_key) { '337d1e9c6c92423dd1c48a23054139058f97be40' } + let(:serializer) { Marshal } + let(:strategy) { described_class.new(store: cache, serializer: Marshal) } + + it_behaves_like 'A strategy with serialization' + end + end + + describe 'reading responses' do + let(:strategy) { described_class.new(store: cache, serializer: serializer) } + + shared_examples 'A strategy with serialization' do + it 'returns nil if the response is not cached' do + expect(subject.read(request)).to be_nil + end + + it 'decodes a stored response' do + subject.write(request, response) + + expect(subject.read(request)).to be_a(Faraday::HttpCache::Response) + end + end + + context 'with the JSON serializer' do + let(:serializer) { JSON } + + it_behaves_like 'A strategy with serialization' + end + + context 'with the Marshal serializer' do + let(:serializer) { Marshal } + + it_behaves_like 'A strategy with serialization' + end + end + + describe 'deleting responses' do + it 'removes the entries from the cache of the given URL' do + subject.write(request, response) + subject.delete(request.url) + expect(subject.read(request)).to be_nil + end + end + + describe 'remove age before caching and normalize max-age if non-zero age present' do + it 'is fresh if the response still has some time to live' do + headers = { + 'Age' => 6, + 'Cache-Control' => 'public, max-age=40', + 'Date' => (Time.now - 38).httpdate, + 'Expires' => (Time.now - 37).httpdate, + 'Last-Modified' => (Time.now - 300).httpdate + } + response = Faraday::HttpCache::Response.new(response_headers: headers) + expect(response).to be_fresh + subject.write(request, response) + + cached_response = subject.read(request) + expect(cached_response.max_age).to eq(34) + expect(cached_response).not_to be_fresh + end + + it 'is fresh until cached and that 1 second elapses then the response is no longer fresh' do + headers = { + 'Date' => (Time.now - 39).httpdate, + 'Expires' => (Time.now + 40).httpdate + } + + response = Faraday::HttpCache::Response.new(response_headers: headers) + expect(response).to be_fresh + subject.write(request, response) + + sleep(1) + cached_response = subject.read(request) + expect(cached_response).not_to be_fresh + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/strategies/by_vary_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/strategies/by_vary_spec.rb new file mode 100644 index 0000000..54a61a8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/strategies/by_vary_spec.rb @@ -0,0 +1,145 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Faraday::HttpCache::Strategies::ByVary do + let(:vary_index_cache_key) { '64896419583e8022efeb21d0ece6e266c0e58b59' } + let(:cache_key) { '978047698d156fe8642a86dbfaacc675917c9a22' } + let(:vary) { 'Accept, Accept-Encoding, X-Requested-With' } + let(:headers) { {'Accept' => 'text/html', 'Accept-Encoding' => 'gzip, deflate, br' } } + let(:request) do + env = {method: :get, url: 'http://test/index', headers: headers} + double(env.merge(serializable_hash: env)) + end + + let(:response_payload) { {response_headers: {'Vary' => vary}} } + + let(:response) do + instance_double(Faraday::HttpCache::Response, payload: response_payload, serializable_hash: response_payload) + end + + let(:cache) { Faraday::HttpCache::MemoryStore.new } + + let(:strategy) { described_class.new(store: cache) } + subject { strategy } + + describe 'storing responses' do + shared_examples 'A strategy with serialization' do + it 'writes the response object to the underlying cache' do + entry = serializer.dump(response.serializable_hash) + expect(cache).to receive(:write).with(vary_index_cache_key, vary) + expect(cache).to receive(:write).with(cache_key, entry) + subject.write(request, response) + end + end + + context 'with the JSON serializer' do + let(:serializer) { JSON } + it_behaves_like 'A strategy with serialization' + + context 'when ASCII characters in response cannot be converted to UTF-8', if: Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.1') do + let(:response_payload) do + body = String.new("\u2665").force_encoding('ASCII-8BIT') + super().merge('body' => body) + end + + it 'raises and logs a warning' do + logger = double(:logger, warn: nil) + strategy = described_class.new(logger: logger) + + expect { + strategy.write(request, response) + }.to raise_error(::Encoding::UndefinedConversionError) + expect(logger).to have_received(:warn).with( + 'Response could not be serialized: "\xE2" from ASCII-8BIT to UTF-8. Try using Marshal to serialize.' + ) + end + end + + context 'with reordered and doubled values in the vary' do + let(:vary) { 'X-Requested-With,Accept,Accept-Encoding,Accept' } + + it_behaves_like 'A strategy with serialization' + end + end + + context 'with the Marshal serializer' do + let(:vary_index_cache_key) { '6a7cb42440c10ef6edeb1826086a4d90b04103f0' } + let(:cache_key) { 'c9edbf280da95d4cac5acda8b8109c0aba2a469a' } + let(:serializer) { Marshal } + let(:strategy) { described_class.new(store: cache, serializer: Marshal) } + + it_behaves_like 'A strategy with serialization' + end + end + + describe 'reading responses' do + let(:strategy) { described_class.new(store: cache, serializer: serializer) } + + shared_examples 'A strategy with serialization' do + it 'returns nil if the response is not cached' do + expect(subject.read(request)).to be_nil + end + + it 'decodes a stored response' do + subject.write(request, response) + + expect(subject.read(request)).to be_a(Faraday::HttpCache::Response) + end + end + + context 'with the JSON serializer' do + let(:serializer) { JSON } + + it_behaves_like 'A strategy with serialization' + end + + context 'with the Marshal serializer' do + let(:serializer) { Marshal } + + it_behaves_like 'A strategy with serialization' + end + end + + describe 'deleting responses' do + it 'ignores delete method' do + subject.write(request, response) + subject.delete(request.url) + expect(subject.read(request)).not_to be_nil + end + end + + describe 'remove age before caching and normalize max-age if non-zero age present' do + it 'is fresh if the response still has some time to live' do + headers = { + 'Age' => 6, + 'Cache-Control' => 'public, max-age=40', + 'Date' => (Time.now - 38).httpdate, + 'Expires' => (Time.now - 37).httpdate, + 'Last-Modified' => (Time.now - 300).httpdate + } + response = Faraday::HttpCache::Response.new(response_headers: headers) + expect(response).to be_fresh + subject.write(request, response) + + cached_response = subject.read(request) + expect(cached_response.max_age).to eq(34) + expect(cached_response).not_to be_fresh + end + + it 'is fresh until cached and that 1 second elapses then the response is no longer fresh' do + headers = { + 'Date' => (Time.now - 39).httpdate, + 'Expires' => (Time.now + 40).httpdate + } + + response = Faraday::HttpCache::Response.new(response_headers: headers) + expect(response).to be_fresh + subject.write(request, response) + + sleep(1) + cached_response = subject.read(request) + expect(cached_response).not_to be_fresh + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/support/empty.png b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/support/empty.png new file mode 100644 index 0000000..eb2c44c Binary files /dev/null and b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/support/empty.png differ diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/support/test_app.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/support/test_app.rb new file mode 100644 index 0000000..cde97d9 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/support/test_app.rb @@ -0,0 +1,134 @@ +# frozen_string_literal: true + +require 'sinatra/base' +require 'json' + +class TestApp < Sinatra::Base + set :environment, :test + set :server, 'webrick' + disable :protection + + set :counter, 0 + set :requests, 0 + set :yesterday, (Date.today - 1).httpdate + + get '/ping' do + 'PONG' + end + + get '/clear' do + settings.counter = 0 + settings.requests = 0 + status 204 + end + + get '/json' do + json = JSON.dump(count: increment_counter.to_i) + [200, { 'Cache-Control' => 'max-age=400', 'Content-Type' => 'application/json' }, json] + end + + get '/image' do + image = File.expand_path('empty.png', __dir__) + data = IO.binread(image) + [200, { 'Cache-Control' => 'max-age=400', 'Content-Type' => 'image/png' }, data] + end + + post '/post' do + [200, { 'Cache-Control' => 'max-age=400' }, increment_counter] + end + + get '/broken' do + [500, { 'Cache-Control' => 'max-age=400' }, increment_counter] + end + + get '/counter' do + [200, { 'Cache-Control' => 'max-age=200' }, increment_counter] + end + + post '/counter' do + end + + put '/counter' do + end + + delete '/counter' do + end + + patch '/counter' do + end + + get '/get' do + [200, { 'Cache-Control' => 'max-age=200' }, increment_counter] + end + + post '/delete-with-location' do + [200, { 'Location' => "#{request.base_url}/get" }, ''] + end + + post '/delete-with-content-location' do + [200, { 'Content-Location' => "#{request.base_url}/get" }, ''] + end + + post '/get' do + halt 405 + end + + get '/private' do + [200, { 'Cache-Control' => 'private, max-age=100' }, increment_counter] + end + + get '/dontstore' do + [200, { 'Cache-Control' => 'no-store' }, increment_counter] + end + + get '/expires' do + [200, { 'Expires' => (Time.now + 10).httpdate }, increment_counter] + end + + get '/yesterday' do + [200, { 'Date' => settings.yesterday, 'Expires' => settings.yesterday }, increment_counter] + end + + get '/must-revalidate' do + [200, { 'Date' => Time.now.httpdate, 'Cache-Control' => 'public, max-age=23880, must-revalidate, no-transform' }, increment_counter] + end + + get '/timestamped' do + settings.counter += 1 + header = settings.counter > 2 ? '1' : '2' + + if env['HTTP_IF_MODIFIED_SINCE'] == header + [304, {}, ''] + else + [200, { 'Last-Modified' => header }, increment_counter] + end + end + + get '/etag' do + settings.counter += 1 + tag = settings.counter > 2 ? '1' : '2' + + if env['HTTP_IF_NONE_MATCH'] == tag + [304, { 'ETag' => tag, 'Cache-Control' => 'max-age=200', 'Date' => Time.now.httpdate, 'Expires' => (Time.now + 200).httpdate, 'Vary' => '*' }, ''] + else + [200, { 'ETag' => tag, 'Cache-Control' => 'max-age=0', 'Date' => settings.yesterday, 'Expires' => Time.now.httpdate, 'Vary' => 'Accept' }, increment_counter] + end + end + + get '/no_cache' do + [200, { 'Cache-Control' => 'max-age=200, no-cache', 'ETag' => settings.counter.to_s }, increment_counter] + end + + get '/vary' do + [200, { 'Cache-Control' => 'max-age=50', 'Vary' => 'User-Agent' }, increment_counter] + end + + get '/vary-wildcard' do + [200, { 'Cache-Control' => 'max-age=50', 'Vary' => '*' }, increment_counter] + end + + # Increments the 'requests' counter to act as a newly processed response. + def increment_counter + (settings.requests += 1).to_s + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/support/test_server.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/support/test_server.rb new file mode 100644 index 0000000..ccb1377 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/support/test_server.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +require 'net/http' + +class TestServer + attr_reader :endpoint + + def initialize + @host = 'localhost' + @port = find_port + @endpoint = "http://#{@host}:#{@port}" + end + + def start + @pid = run! + wait + end + + def stop + `kill -9 #{@pid}` + end + + private + + def run! + fork do + require 'webrick' + log = File.open('log/test.log', 'w+') + log.sync = true + webrick_opts = { + Port: @port, + Logger: WEBrick::Log.new(log), + AccessLog: [[log, '[%{X-Faraday-Adapter}i] %m %U -> %s %b']] + } + Rack::Handler::WEBrick.run(TestApp, **webrick_opts) + end + end + + def wait + conn = Net::HTTP.new @host, @port + conn.open_timeout = conn.read_timeout = 0.1 + + responsive = ->(path) { + begin + res = conn.start { conn.get(path) } + res.is_a?(Net::HTTPSuccess) + rescue Errno::ECONNREFUSED, Errno::EBADF, Timeout::Error, Net::HTTPBadResponse + false + end + } + + server_pings = 0 + loop do + break if responsive.call('/ping') + + server_pings += 1 + sleep 0.05 + abort 'test server did not managed to start' if server_pings >= 50 + end + end + + def find_port + server = TCPServer.new(@host, 0) + server.addr[1] + ensure + server&.close + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/validation_spec.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/validation_spec.rb new file mode 100644 index 0000000..368f02f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-http-cache-2.4.1/spec/validation_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Faraday::HttpCache do + let(:backend) { Faraday::Adapter::Test::Stubs.new } + + let(:client) do + Faraday.new(url: ENV['FARADAY_SERVER']) do |stack| + stack.use Faraday::HttpCache + stack.adapter :test, backend + end + end + + it 'maintains the "Content-Type" header for cached responses' do + backend.get('/test') { [200, { 'ETag' => '123ABC', 'Content-Type' => 'x' }, ''] } + first_content_type = client.get('/test').headers['Content-Type'] + + # The Content-Type header of the validation response should be ignored. + backend.get('/test') { [304, { 'Content-Type' => 'y' }, ''] } + second_content_type = client.get('/test').headers['Content-Type'] + + expect(first_content_type).to eq('x') + expect(second_content_type).to eq('x') + end + + it 'maintains the "Content-Length" header for cached responses' do + backend.get('/test') { [200, { 'ETag' => '123ABC', 'Content-Length' => 1 }, ''] } + first_content_length = client.get('/test').headers['Content-Length'] + + # The Content-Length header of the validation response should be ignored. + backend.get('/test') { [304, { 'Content-Length' => 2 }, ''] } + second_content_length = client.get('/test').headers['Content-Length'] + + expect(first_content_length).to eq(1) + expect(second_content_length).to eq(1) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-net_http-3.0.2/LICENSE.md b/vendor/bundle/ruby/2.7.0/gems/faraday-net_http-3.0.2/LICENSE.md new file mode 100644 index 0000000..b7aabc5 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-net_http-3.0.2/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020 Jan van der Pas + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-net_http-3.0.2/README.md b/vendor/bundle/ruby/2.7.0/gems/faraday-net_http-3.0.2/README.md new file mode 100644 index 0000000..6d510f4 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-net_http-3.0.2/README.md @@ -0,0 +1,57 @@ +# Faraday Net::HTTP adapter + +This gem is a [Faraday][faraday] adapter for the [Net::HTTP][net-http] library. Faraday is an HTTP client library that provides a common interface over many adapters. Every adapter is defined into it's own gem. This gem defines the adapter for `Net::HTTP` the HTTP library that's included into the standard library of Ruby. + +## Installation + +Add this line to your application's Gemfile: + +```ruby +gem 'faraday-net_http' +``` + +And then execute: + + $ bundle install + +Or install it yourself as: + + $ gem install faraday-net_http + +## Usage + +```ruby +conn = Faraday.new(...) do |f| + f.adapter :net_http do |http| + # yields Net::HTTP + http.verify_callback = lambda do |preverify, cert_store| + # do something here... + end + end +end +``` + +## Development + +After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. + +To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](rubygems). + +## Contributing + +Bug reports and pull requests are welcome on [GitHub][repo]. + +## License + +The gem is available as open source under the terms of the [license][license]. + +## Code of Conduct + +Everyone interacting in the Faraday Net::HTTP adapter project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct][code-of-conduct]. + +[faraday]: https://github.com/lostisland/faraday +[net-http]: https://ruby-doc.org/stdlib-2.7.0/libdoc/net/http/rdoc/Net/HTTP.html +[rubygems]: https://rubygems.org +[repo]: https://github.com/lostisland/faraday-net_http +[license]: https://github.com/lostisland/faraday-net_http/blob/main/LICENSE.md +[code-of-conduct]: https://github.com/lostisland/faraday-net_http/blob/main/CODE_OF_CONDUCT.md diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-net_http-3.0.2/lib/faraday/adapter/net_http.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-net_http-3.0.2/lib/faraday/adapter/net_http.rb new file mode 100644 index 0000000..f4c4e3f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-net_http-3.0.2/lib/faraday/adapter/net_http.rb @@ -0,0 +1,208 @@ +# frozen_string_literal: true + +begin + require 'net/https' +rescue LoadError + warn 'Warning: no such file to load -- net/https. ' \ + 'Make sure openssl is installed if you want ssl support' + require 'net/http' +end +require 'zlib' + +module Faraday + class Adapter + class NetHttp < Faraday::Adapter + exceptions = [ + IOError, + Errno::EADDRNOTAVAIL, + Errno::EALREADY, + Errno::ECONNABORTED, + Errno::ECONNREFUSED, + Errno::ECONNRESET, + Errno::EHOSTUNREACH, + Errno::EINVAL, + Errno::ENETUNREACH, + Errno::EPIPE, + Net::HTTPBadResponse, + Net::HTTPHeaderSyntaxError, + Net::ProtocolError, + SocketError, + Zlib::GzipFile::Error + ] + + exceptions << ::OpenSSL::SSL::SSLError if defined?(::OpenSSL::SSL::SSLError) + exceptions << ::Net::OpenTimeout if defined?(::Net::OpenTimeout) + + NET_HTTP_EXCEPTIONS = exceptions.freeze + + def initialize(app = nil, opts = {}, &block) + @ssl_cert_store = nil + super(app, opts, &block) + end + + def build_connection(env) + net_http_connection(env).tap do |http| + http.use_ssl = env[:url].scheme == 'https' if http.respond_to?(:use_ssl=) + configure_ssl(http, env[:ssl]) + configure_request(http, env[:request]) + end + end + + def net_http_connection(env) + proxy = env[:request][:proxy] + port = env[:url].port || (env[:url].scheme == 'https' ? 443 : 80) + if proxy + Net::HTTP.new(env[:url].hostname, port, + proxy[:uri].hostname, proxy[:uri].port, + proxy[:user], proxy[:password]) + else + Net::HTTP.new(env[:url].hostname, port, nil) + end + end + + def call(env) + super + connection(env) do |http| + perform_request(http, env) + rescue *NET_HTTP_EXCEPTIONS => e + raise Faraday::SSLError, e if defined?(OpenSSL) && e.is_a?(OpenSSL::SSL::SSLError) + + raise Faraday::ConnectionFailed, e + end + @app.call env + rescue Timeout::Error, Errno::ETIMEDOUT => e + raise Faraday::TimeoutError, e + end + + private + + def create_request(env) + request = Net::HTTPGenericRequest.new \ + env[:method].to_s.upcase, # request method + !!env[:body], # is there request body + env[:method] != :head, # is there response body + env[:url].request_uri, # request uri path + env[:request_headers] # request headers + + if env[:body].respond_to?(:read) + request.body_stream = env[:body] + else + request.body = env[:body] + end + request + end + + def perform_request(http, env) + if env.stream_response? + http_response = env.stream_response do |&on_data| + request_with_wrapped_block(http, env, &on_data) + end + http_response.body = nil + else + http_response = request_with_wrapped_block(http, env) + end + env.response_body = encoded_body(http_response) + env.response.finish(env) + http_response + end + + def request_with_wrapped_block(http, env) + # Must use Net::HTTP#start and pass it a block otherwise the server's + # TCP socket does not close correctly. + http.start do |opened_http| + opened_http.request create_request(env) do |response| + save_http_response(env, response) + + if block_given? + response.read_body do |chunk| + yield(chunk) + end + end + end + end + end + + def save_http_response(env, http_response) + save_response( + env, http_response.code.to_i, nil, nil, http_response.message, finished: false + ) do |response_headers| + http_response.each_header do |key, value| + response_headers[key] = value + end + end + end + + def configure_ssl(http, ssl) + return unless ssl + + http.verify_mode = ssl_verify_mode(ssl) + http.cert_store = ssl_cert_store(ssl) + + http.cert = ssl[:client_cert] if ssl[:client_cert] + http.key = ssl[:client_key] if ssl[:client_key] + http.ca_file = ssl[:ca_file] if ssl[:ca_file] + http.ca_path = ssl[:ca_path] if ssl[:ca_path] + http.verify_depth = ssl[:verify_depth] if ssl[:verify_depth] + http.ssl_version = ssl[:version] if ssl[:version] + http.min_version = ssl[:min_version] if ssl[:min_version] + http.max_version = ssl[:max_version] if ssl[:max_version] + http.verify_hostname = ssl[:verify_hostname] if verify_hostname_enabled?(http, ssl) + end + + def configure_request(http, req) + if (sec = request_timeout(:read, req)) + http.read_timeout = sec + end + + if (sec = http.respond_to?(:write_timeout=) && + request_timeout(:write, req)) + http.write_timeout = sec + end + + if (sec = request_timeout(:open, req)) + http.open_timeout = sec + end + + # Only set if Net::Http supports it, since Ruby 2.5. + http.max_retries = 0 if http.respond_to?(:max_retries=) + + @config_block&.call(http) + end + + def ssl_cert_store(ssl) + return ssl[:cert_store] if ssl[:cert_store] + + @ssl_cert_store ||= begin + # Use the default cert store by default, i.e. system ca certs + OpenSSL::X509::Store.new.tap(&:set_default_paths) + end + end + + def ssl_verify_mode(ssl) + ssl[:verify_mode] || begin + if ssl.fetch(:verify, true) + OpenSSL::SSL::VERIFY_PEER + else + OpenSSL::SSL::VERIFY_NONE + end + end + end + + def encoded_body(http_response) + body = http_response.body || +'' + /\bcharset=([^;]+)/.match(http_response['Content-Type']) do |match| + content_charset = ::Encoding.find(match[1].strip) + body = body.dup if body.frozen? + body.force_encoding(content_charset) + rescue ArgumentError + nil + end + body + end + + def verify_hostname_enabled?(http, ssl) + http.respond_to?(:verify_hostname=) && ssl.key?(:verify_hostname) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-net_http-3.0.2/lib/faraday/net_http.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-net_http-3.0.2/lib/faraday/net_http.rb new file mode 100644 index 0000000..e4048e8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-net_http-3.0.2/lib/faraday/net_http.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'faraday/adapter/net_http' +require 'faraday/net_http/version' + +module Faraday + module NetHttp + Faraday::Adapter.register_middleware(net_http: Faraday::Adapter::NetHttp) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-net_http-3.0.2/lib/faraday/net_http/version.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-net_http-3.0.2/lib/faraday/net_http/version.rb new file mode 100644 index 0000000..1c1a01e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-net_http-3.0.2/lib/faraday/net_http/version.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module Faraday + module NetHttp + VERSION = '3.0.2' + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-retry-2.0.0/CHANGELOG.md b/vendor/bundle/ruby/2.7.0/gems/faraday-retry-2.0.0/CHANGELOG.md new file mode 100644 index 0000000..9cddf44 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-retry-2.0.0/CHANGELOG.md @@ -0,0 +1,24 @@ +# Changelog + +## v2.0.0 (2022-06-08) + +### Changed + +* `retry_block` now takes keyword arguments instead of positional (backwards incompatible) +* `retry_block`'s `retry_count` argument now counts up from 0, instead of old `retries_remaining` + +### Added + +* Support for the `RateLimit-Reset` header. [PR #9](https://github.com/lostisland/faraday-retry/pull/9). Thanks, [@maxprokopiev]! +* `retry_block` has additional `will_retry_in` argument with upcoming delay before retry in seconds. + +## v1.0 + +Initial release. +This release consists of the same middleware that was previously bundled with Faraday but removed in Faraday v2.0, plus: + +### Fixed + +* Retry middleware `retry_block` is not called if retry will not happen due to `max_interval`, https://github.com/lostisland/faraday/pull/1350 + +[@maxprokopiev]: https://github.com/maxprokopiev diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-retry-2.0.0/LICENSE.md b/vendor/bundle/ruby/2.7.0/gems/faraday-retry-2.0.0/LICENSE.md new file mode 100644 index 0000000..389e644 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-retry-2.0.0/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2021 Mattia Giuffrida + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-retry-2.0.0/README.md b/vendor/bundle/ruby/2.7.0/gems/faraday-retry-2.0.0/README.md new file mode 100644 index 0000000..f861c2d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-retry-2.0.0/README.md @@ -0,0 +1,169 @@ +# Faraday Retry + +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/lostisland/faraday-retry/CI)](https://github.com/lostisland/faraday-retry/actions?query=branch%3Amain) +[![Gem](https://img.shields.io/gem/v/faraday-retry.svg?style=flat-square)](https://rubygems.org/gems/faraday-retry) +[![License](https://img.shields.io/github/license/lostisland/faraday-retry.svg?style=flat-square)](LICENSE.md) + +The `Retry` middleware automatically retries requests that fail due to intermittent client +or server errors (such as network hiccups). +By default, it retries 2 times and handles only timeout exceptions. +It can be configured with an arbitrary number of retries, a list of exceptions to handle, +a retry interval, a percentage of randomness to add to the retry interval, and a backoff factor. +The middleware can also handle the [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) +header automatically when configured with the right status codes (see below for an example). + +## Installation + +Add this line to your application's Gemfile: + +```ruby +gem 'faraday-retry' +``` + +And then execute: + +```shell +bundle install +``` + +Or install it yourself as: + +```shell +gem install faraday-retry +``` + +## Usage + +This example will result in a first interval that is random between 0.05 and 0.075 +and a second interval that is random between 0.1 and 0.125. + +```ruby +require 'faraday' +require 'faraday/retry' + +retry_options = { + max: 2, + interval: 0.05, + interval_randomness: 0.5, + backoff_factor: 2 +} + +conn = Faraday.new(...) do |f| + f.request :retry, retry_options + #... +end + +conn.get('/') +``` + +### Control when the middleware will retry requests + +By default, the `Retry` middleware will only retry idempotent methods and the most common network-related exceptions. +You can change this behaviour by providing the right option when adding the middleware to your connection. + +#### Specify which methods will be retried + +You can provide a `methods` option with a list of HTTP methods. +This will replace the default list of HTTP methods: `delete`, `get`, `head`, `options`, `put`. + +```ruby +retry_options = { + methods: %i[get post] +} +``` + +#### Specify which exceptions should trigger a retry + +You can provide an `exceptions` option with a list of exceptions that will replace +the default list of network-related exceptions: `Errno::ETIMEDOUT`, `Timeout::Error`, `Faraday::TimeoutError`. +This can be particularly useful when combined with the [RaiseError][raise_error] middleware. + +```ruby +retry_options = { + exceptions: [Faraday::ResourceNotFound, Faraday::UnauthorizedError] +} +``` + +#### Specify on which response statuses to retry + +By default the `Retry` middleware will only retry the request if one of the expected exceptions arise. +However, you can specify a list of HTTP statuses you'd like to be retried. When you do so, the middleware will +check the response `status` code and will retry the request if included in the list. + +```ruby +retry_options = { + retry_statuses: [401, 409] +} +``` + +#### Automatically handle the `Retry-After` and `RateLimit-Reset` headers + +Some APIs, like the [Slack API](https://api.slack.com/docs/rate-limits), will inform you when you reach their API limits by replying with a response status code of `429` +and a response header of `Retry-After` containing a time in seconds. You should then only retry querying after the amount of time provided by the `Retry-After` header, +otherwise you won't get a response. Other APIs communicate their rate limits via the [RateLimit-xxx](https://tools.ietf.org/id/draft-polli-ratelimit-headers-00.html#rfc.section.3.3) headers +where `RateLimit-Reset` behaves similarly to the `Retry-After`. + +You can automatically handle both headers and have Faraday pause and retry for the right amount of time by including the `429` status code in the retry statuses list: + +```ruby +retry_options = { + retry_statuses: [429] +} +``` + +#### Specify a custom retry logic + +You can also specify a custom retry logic with the `retry_if` option. +This option accepts a block that will receive the `env` object and the exception raised +and should decide if the code should retry still the action or not independent of the retry count. +This would be useful if the exception produced is non-recoverable or if the the HTTP method called is not idempotent. + +**NOTE:** this option will only be used for methods that are not included in the `methods` option. +If you want this to apply to all HTTP methods, pass `methods: []` as an additional option. + +```ruby +# Retries the request if response contains { success: false } +retry_options = { + retry_if: -> (env, _exc) { env.body[:success] == 'false' } +} +``` + +### Call a block on every retry + +You can specify a proc object through the `retry_block` option that will be called before every +retry, before There are many different applications for this feature, spacing from instrumentation to monitoring. + + +The block is passed keyword arguments with contextual information: Request environment, middleware options, current number of retries, exception, and amount of time we will wait before retrying. (retry_block is called before the wait time happens) + + +For example, you might want to keep track of the response statuses: + +```ruby +response_statuses = [] +retry_options = { + retry_block: -> (env:, options:, retries_remaining:, exception:, will_retry_in:) { response_statuses << env.status } +} +``` + +## Development + +After checking out the repo, run `bin/setup` to install dependencies. + +Then, run `bin/test` to run the tests. + +To install this gem onto your local machine, run `rake build`. + +### Releasing a new version + +To release a new version, make a commit with a message such as "Bumped to 0.0.2", and change the _Unreleased_ heading in `CHANGELOG.md` to a heading like "0.0.2 (2022-01-01)", and then use GitHub Releases to author a release. A GitHub Actions workflow then publishes a new gem to [RubyGems.org](https://rubygems.org/gems/faraday-retry). + +## Contributing + +Bug reports and pull requests are welcome on [GitHub](https://github.com/lostisland/faraday-retry). + +## License + +The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). + +[raise_error]: https://lostisland.github.io/faraday/middleware/raise-error diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-retry-2.0.0/lib/faraday/retriable_response.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-retry-2.0.0/lib/faraday/retriable_response.rb new file mode 100644 index 0000000..115e2c2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-retry-2.0.0/lib/faraday/retriable_response.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# Faraday namespace. +module Faraday + # Exception used to control the Retry middleware. + class RetriableResponse < Error + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-retry-2.0.0/lib/faraday/retry.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-retry-2.0.0/lib/faraday/retry.rb new file mode 100644 index 0000000..659663c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-retry-2.0.0/lib/faraday/retry.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'faraday' +require_relative 'retriable_response' +require_relative 'retry/middleware' +require_relative 'retry/version' + +module Faraday + # Middleware main module. + module Retry + Faraday::Request.register_middleware(retry: Faraday::Retry::Middleware) + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-retry-2.0.0/lib/faraday/retry/middleware.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-retry-2.0.0/lib/faraday/retry/middleware.rb new file mode 100644 index 0000000..cfad003 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-retry-2.0.0/lib/faraday/retry/middleware.rb @@ -0,0 +1,254 @@ +# frozen_string_literal: true + +module Faraday + module Retry + # This class provides the main implementation for your middleware. + # Your middleware can implement any of the following methods: + # * on_request - called when the request is being prepared + # * on_complete - called when the response is being processed + # + # Optionally, you can also override the following methods from Faraday::Middleware + # * initialize(app, options = {}) - the initializer method + # * call(env) - the main middleware invocation method. + # This already calls on_request and on_complete, so you normally don't need to override it. + # You may need to in case you need to "wrap" the request or need more control + # (see "retry" middleware: https://github.com/lostisland/faraday/blob/main/lib/faraday/request/retry.rb#L142). + # IMPORTANT: Remember to call `@app.call(env)` or `super` to not interrupt the middleware chain! + class Middleware < Faraday::Middleware + DEFAULT_EXCEPTIONS = [ + Errno::ETIMEDOUT, 'Timeout::Error', + Faraday::TimeoutError, Faraday::RetriableResponse + ].freeze + IDEMPOTENT_METHODS = %i[delete get head options put].freeze + + # Options contains the configurable parameters for the Retry middleware. + class Options < Faraday::Options.new(:max, :interval, :max_interval, + :interval_randomness, + :backoff_factor, :exceptions, + :methods, :retry_if, :retry_block, + :retry_statuses) + + DEFAULT_CHECK = ->(_env, _exception) { false } + + def self.from(value) + if value.is_a?(Integer) + new(value) + else + super(value) + end + end + + def max + (self[:max] ||= 2).to_i + end + + def interval + (self[:interval] ||= 0).to_f + end + + def max_interval + (self[:max_interval] ||= Float::MAX).to_f + end + + def interval_randomness + (self[:interval_randomness] ||= 0).to_f + end + + def backoff_factor + (self[:backoff_factor] ||= 1).to_f + end + + def exceptions + Array(self[:exceptions] ||= DEFAULT_EXCEPTIONS) + end + + def methods + Array(self[:methods] ||= IDEMPOTENT_METHODS) + end + + def retry_if + self[:retry_if] ||= DEFAULT_CHECK + end + + def retry_block + self[:retry_block] ||= proc {} + end + + def retry_statuses + Array(self[:retry_statuses] ||= []) + end + end + + # @param app [#call] + # @param options [Hash] + # @option options [Integer] :max (2) Maximum number of retries + # @option options [Integer] :interval (0) Pause in seconds between retries + # @option options [Integer] :interval_randomness (0) The maximum random + # interval amount expressed as a float between + # 0 and 1 to use in addition to the interval. + # @option options [Integer] :max_interval (Float::MAX) An upper limit + # for the interval + # @option options [Integer] :backoff_factor (1) The amount to multiply + # each successive retry's interval amount by in order to provide backoff + # @option options [Array] :exceptions ([ Errno::ETIMEDOUT, + # 'Timeout::Error', Faraday::TimeoutError, Faraday::RetriableResponse]) + # The list of exceptions to handle. Exceptions can be given as + # Class, Module, or String. + # @option options [Array] :methods (the idempotent HTTP methods + # in IDEMPOTENT_METHODS) A list of HTTP methods to retry without + # calling retry_if. Pass an empty Array to call retry_if + # for all exceptions. + # @option options [Block] :retry_if (false) block that will receive + # the env object and the exception raised + # and should decide if the code should retry still the action or + # not independent of the retry count. This would be useful + # if the exception produced is non-recoverable or if the + # the HTTP method called is not idempotent. + # @option options [Block] :retry_block block that is executed before + # every retry. The block will be yielded keyword arguments: + # * env [Faraday::Env]: Request environment + # * options [Faraday::Options]: middleware options + # * retry_count [Integer]: how many retries have already occured (starts at 0) + # * exception [Exception]: exception that triggered the retry, + # will be the synthetic `Faraday::RetriableResponse` if the + # retry was triggered by something other than an exception. + # * will_retry_in [Float]: retry_block is called *before* the retry + # delay, actual retry will happen in will_retry_in number of + # seconds. + # @option options [Array] :retry_statuses Array of Integer HTTP status + # codes or a single Integer value that determines whether to raise + # a Faraday::RetriableResponse exception based on the HTTP status code + # of an HTTP response. + def initialize(app, options = nil) + super(app) + @options = Options.from(options) + @errmatch = build_exception_matcher(@options.exceptions) + end + + def calculate_sleep_amount(retries, env) + retry_after = [calculate_retry_after(env), calculate_rate_limit_reset(env)].compact.max + retry_interval = calculate_retry_interval(retries) + + return if retry_after && retry_after > @options.max_interval + + if retry_after && retry_after >= retry_interval + retry_after + else + retry_interval + end + end + + # @param env [Faraday::Env] + def call(env) + retries = @options.max + request_body = env[:body] + begin + # after failure env[:body] is set to the response body + env[:body] = request_body + @app.call(env).tap do |resp| + raise Faraday::RetriableResponse.new(nil, resp) if @options.retry_statuses.include?(resp.status) + end + rescue @errmatch => e + if retries.positive? && retry_request?(env, e) + retries -= 1 + rewind_files(request_body) + if (sleep_amount = calculate_sleep_amount(retries + 1, env)) + @options.retry_block.call( + env: env, + options: @options, + retry_count: @options.max - (retries + 1), + exception: e, + will_retry_in: sleep_amount + ) + sleep sleep_amount + retry + end + end + + raise unless e.is_a?(Faraday::RetriableResponse) + + e.response + end + end + + # An exception matcher for the rescue clause can usually be any object + # that responds to `===`, but for Ruby 1.8 it has to be a Class or Module. + # + # @param exceptions [Array] + # @api private + # @return [Module] an exception matcher + def build_exception_matcher(exceptions) + matcher = Module.new + ( + class << matcher + self + end).class_eval do + define_method(:===) do |error| + exceptions.any? do |ex| + if ex.is_a? Module + error.is_a? ex + else + Object.const_defined?(ex.to_s) && error.is_a?(Object.const_get(ex.to_s)) + end + end + end + end + matcher + end + + private + + def retry_request?(env, exception) + @options.methods.include?(env[:method]) || + @options.retry_if.call(env, exception) + end + + def rewind_files(body) + return unless defined?(UploadIO) + return unless body.is_a?(Hash) + + body.each do |_, value| + value.rewind if value.is_a?(UploadIO) + end + end + + # RFC for RateLimit Header Fields for HTTP: + # https://tools.ietf.org/id/draft-polli-ratelimit-headers-00.html#rfc.section.3.3 + def calculate_rate_limit_reset(env) + parse_retry_header(env, 'RateLimit-Reset') + end + + # MDN spec for Retry-After header: + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After + def calculate_retry_after(env) + parse_retry_header(env, 'Retry-After') + end + + def calculate_retry_interval(retries) + retry_index = @options.max - retries + current_interval = @options.interval * + (@options.backoff_factor**retry_index) + current_interval = [current_interval, @options.max_interval].min + random_interval = rand * @options.interval_randomness.to_f * + @options.interval + + current_interval + random_interval + end + + def parse_retry_header(env, header) + response_headers = env[:response_headers] + return unless response_headers + + retry_after_value = env[:response_headers][header] + + # Try to parse date from the header value + begin + datetime = DateTime.rfc2822(retry_after_value) + datetime.to_time - Time.now.utc + rescue ArgumentError + retry_after_value.to_f + end + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/faraday-retry-2.0.0/lib/faraday/retry/version.rb b/vendor/bundle/ruby/2.7.0/gems/faraday-retry-2.0.0/lib/faraday/retry/version.rb new file mode 100644 index 0000000..8eba371 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/faraday-retry-2.0.0/lib/faraday/retry/version.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module Faraday + module Retry + VERSION = '2.0.0' + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/fiber-local-1.0.0/lib/fiber/local.rb b/vendor/bundle/ruby/2.7.0/gems/fiber-local-1.0.0/lib/fiber/local.rb new file mode 100644 index 0000000..25dbbb8 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/fiber-local-1.0.0/lib/fiber/local.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +# Copyright, 2020, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require_relative "local/version" + +class Fiber + module Local + # Instantiate a new thread-local object. + # By default, invokes {new} to generate the instance. + # @returns [Object] + def local + self.new + end + + # Get the current thread-local instance. Create it if required. + # @returns [Object] The thread-local instance. + def instance + thread = Thread.current + name = self.name + + if instance = thread[self.name] + return instance + end + + unless instance = thread.thread_variable_get(name) + if instance = self.local + thread.thread_variable_set(name, instance) + end + end + + thread[self.name] = instance + + return instance + end + + # Assigns to the fiber-local instance. + # @parameter instance [Object] The object that will become the thread-local instance. + def instance= instance + thread = Thread.current + thread[self.name] = instance + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/fiber-local-1.0.0/lib/fiber/local/version.rb b/vendor/bundle/ruby/2.7.0/gems/fiber-local-1.0.0/lib/fiber/local/version.rb new file mode 100644 index 0000000..cfc1ebb --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/fiber-local-1.0.0/lib/fiber/local/version.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +# Copyright, 2020, by Samuel G. D. Williams. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +class Fiber + module Local + VERSION = "1.0.0" + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/LICENSE b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/LICENSE new file mode 100644 index 0000000..5b4ce7e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/LICENSE @@ -0,0 +1,9 @@ +The MIT License (MIT) +Copyright (c) 2016-2019 Petr Korolev + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/README.md b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/README.md new file mode 100644 index 0000000..d6a6e3e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/README.md @@ -0,0 +1,332 @@ +[![Gem Version](https://badge.fury.io/rb/github_changelog_generator.svg)](http://badge.fury.io/rb/github_changelog_generator) +[![CircleCI](https://circleci.com/gh/github-changelog-generator/github-changelog-generator.svg?style=svg)](https://circleci.com/gh/github-changelog-generator/github-changelog-generator) +[![Inline docs](http://inch-ci.org/github/github-changelog-generator/github-changelog-generator.svg)](http://inch-ci.org/github/github-changelog-generator/github-changelog-generator) +[![Join the chat at https://gitter.im/github-changelog-generator/chat](https://badges.gitter.im/github-changelog-generator/chat.svg)](https://gitter.im/github-changelog-generator/chat?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +# github-changelog-generator ![GitHub Logo](../master/images/logo.jpg) + + + + + + + - [Changelog generation has never been so easy](#changelog-generation-has-never-been-so-easy) + - [*What’s the point of a changelog?*](#whats-the-point-of-a-changelog) + - [*Why should I care?*](#why-should-i-care) +- [Installation](#installation) +- [Running with Docker](#running-with-docker) +- [Output example](#output-example) +- [Usage](#usage) + - [Params](#params) + - [Params File](#params-file) + - [GitHub token](#github-token) +- [Migrating from a manual changelog](#migrating-from-a-manual-changelog) + - [Rake task](#rake-task) +- [Features and advantages of this project](#features-and-advantages-of-this-project) + - [Using the summary section feature](#using-the-summary-section-feature) + - [Alternatives](#alternatives) + - [Projects using this library](#projects-using-this-library) +- [Am I missing some essential feature?](#am-i-missing-some-essential-feature) +- [FAQ](#faq) +- [Contributing](#contributing) +- [License](#license) + + + + +### Changelog generation has never been so easy + +**Fully automated changelog generation** - This gem generates a changelog file based on **tags**, **issues** and merged **pull requests** (and splits them into separate lists according to labels) from :octocat: GitHub. + +Since you don't have to fill your `CHANGELOG.md` manually now: just run the script, relax and take a cup of :coffee: before your next release! :tada: + +### *What’s the point of a changelog?* + +To make it easier for users and contributors to see precisely what notable changes have been made between each release (or version) of the project. + +### *Why should I care?* + +Because software tools are for _people_. "Changelogs make it easier for users and +contributors to see precisely what notable changes have been made between each +release (or version) of the project." + +:arrow_right: *[https://keepachangelog.com](https://keepachangelog.com)* + +## Installation + +GitHub Changelog Generator is a [Ruby](https://www.ruby-lang.org/) +program, distributed as a RubyGem. The Ruby language homepage has an [Installation page](https://www.ruby-lang.org/en/documentation/installation/). + +Install the gem like: + + $ gem install github_changelog_generator + +Depending on your system, you _may_ need to run the shell as an Administrator (Windows), +or use `sudo gem install github_changelog_generator` (Linux). + + +## Usage + + +### Running with CLI: + + github_changelog_generator -u github_project_namespace -p github_project + +(where the project namespace is _likely_ your username if it's a project you own, but it could also be the namespace of the project) + + +### Running with Docker + +Using [Docker](https://www.docker.com/products/docker-desktop) is an alternative to installing Ruby and the gem. + +Example invocation: + + $ docker run -it --rm -v "$(pwd)":/usr/local/src/your-app githubchangeloggenerator/github-changelog-generator + + + +- For GitHub Enterprise repos, specify *both* `--github-site` and `--github-api` options: + + $ github_changelog_generator --github-site="https://github.yoursite.com" \ + --github-api="https://github.yoursite.com/api/v3/" + + +This generates a `CHANGELOG.md`, with pretty Markdown formatting. + + +## Output example + +- Look at **[CHANGELOG.md](https://github.com/github-changelog-generator/Github-Changelog-Generator/blob/master/CHANGELOG.md)** for this project +- [ActionSheetPicker-3.0/CHANGELOG.md](https://github.com/skywinder/ActionSheetPicker-3.0/blob/develop/CHANGELOG.md) was generated by command: + + $ github_changelog_generator -u github-changelog-generator -p ActionSheetPicker-3.0 + +- In general, it looks like this: + +> ## [1.2.5](https://github.com/github-changelog-generator/Github-Changelog-Generator/tree/1.2.5) (2015-01-15) +> +> [Full Changelog](https://github.com/github-changelog-generator/Github-Changelog-Generator/compare/1.2.4...1.2.5) +> +> **Implemented enhancements:** +> +> - Use milestone to specify in which version bug was fixed [\#22](https://github.com/github-changelog-generator/Github-Changelog-Generator/issues/22) +> +> **Fixed bugs:** +> +> - Error when trying to generate log for repo without tags [\#32](https://github.com/github-changelog-generator/Github-Changelog-Generator/issues/32) +> +> **Merged pull requests:** +> +> - PrettyPrint class is included using lowercase 'pp' [\#43](https://github.com/github-changelog-generator/Github-Changelog-Generator/pull/43) ([schwing](https://github.com/schwing)) +> +> - support enterprise github via command line options [\#42](https://github.com/github-changelog-generator/Github-Changelog-Generator/pull/42) ([glenlovett](https://github.com/glenlovett)) + +### Params + +Print help for all command-line options to learn more details: + + $ github_changelog_generator --help + +For more details about params, read the Wiki page: [**Advanced changelog generation examples**](https://github.com/github-changelog-generator/github-changelog-generator/wiki/Advanced-change-log-generation-examples) + +### Params File + +In your project root, you can put a params file named `.github_changelog_generator` to override default params: + +Example: + +``` +unreleased=false +future-release=5.0.0 +since-tag=1.0.0 +``` + +### GitHub token + +GitHub only allows **50 unauthenticated requests per hour**. + +Therefore, it's recommended to run this script with authentication by using a **token**. + +Here's how: + +- [Generate a token here](https://github.com/settings/tokens/new?description=GitHub%20Changelog%20Generator%20token) - you only need "repo" scope for private repositories +- Either: + - Run the script with `--token `; **OR** + - Set the `CHANGELOG_GITHUB_TOKEN` environment variable to your 40 digit token + +You can set an environment variable by running the following command at the prompt, or by adding it to your shell profile (e.g., `.env`, `~/.bash_profile`, `~/.zshrc`, etc): + + export CHANGELOG_GITHUB_TOKEN="«your-40-digit-github-token»" + +So, if you get a message like this: + +``` markdown +API rate limit exceeded for github_username. +See: https://developer.github.com/v3/#rate-limiting +``` + +It's time to create this token! (Or, wait an hour for GitHub to reset your unauthenticated request limit.) + +## Migrating from a manual changelog + +Knowing how dedicated you are to your project, you probably haven't been waiting for `github-changelog-generator` to keep a changelog. +But you probably don't want your project's open issues and PRs for all past features listed in your historic changelog, either. + +That's where `--base ` comes in handy! +This option lets append your old manual changelog to the end of the generated entries. + +If you have a `HISTORY.md` file in your project, it will automatically be picked as the static historical changelog and appended. + +### Rake task + +You love `rake`? We do, too! So, we've made it even easier for you: +we've provided a `rake` task library for your changelog generation. + +Configure the task in your `Rakefile`: + +```ruby +require 'github_changelog_generator/task' + +GitHubChangelogGenerator::RakeTask.new :changelog do |config| + config.user = 'username' + config.project = 'project-name' + config.since_tag = '0.1.14' + config.future_release = '0.2.0' +end +``` + +All command-line options can be passed to the `rake` task as `config` +parameters. And since you're naming the `rake` task yourself, you can create +as many as you want. + +You can look for params names from the [parser source code (#setup_parser)](https://github.com/github-changelog-generator/github-changelog-generator/blob/master/lib/github_changelog_generator/parser.rb). For example, to translate the bugs label to Portuguese, instead of setting `config.bugs_label`, you have to set `config.bug_prefix`, and so on. + +## Features and advantages of this project + +- Generate canonical, neat changelog file, with default sections that follow [basic changelog guidelines](http://keepachangelog.com) :gem: +- Optionally generate **Unreleased** changes (closed issues that have not released yet) :dizzy: +- **GitHub Enterprise support** via command line options! :factory: +- Flexible format **customization**: + - **Customize** issues that **should be added** to changelog :eight_spoked_asterisk: + - **Custom date formats** supported (but keep [ISO 8601](http://xkcd.com/1179/) in mind!) :date: + - Manually specify the version that fixed an issue (for cases when the issue's Closed date doesn't match) by giving the issue's `milestone` the same name as the tag of version :pushpin: + - Automatically **exclude specific issues** that are irrelevant to your changelog (by default, any issue labeled `question`, `duplicate`, `invalid`, or `wontfix`) :scissors: +- **Distinguish** issues **by labels**. :mag_right: + - Merged pull requests (all merged pull-requests) :twisted_rightwards_arrows: + - Bug fixes (issues labeled `bug`) :beetle: + - Enhancements (issues labeled `enhancement`) :star2: + - Issues (closed issues with no labels) :non-potable_water: + +- Manually include or exclude issues by labels :wrench: +- Customize lots more! Tweak the changelog to fit your preferences :tophat: +(*See `github_changelog_generator --help` for details)* + +### Using the summary section feature + +For each version, you can add a _release summary_ with text, images, gif animations, +etc, and show new features and notes clearly to the user. This is done using GitHub metadata. + +**Example**: adding the release summary for v1.0.0: + +1. Create a new GitHub Issue +2. In the Issue's _Description_ field, add your release summary content +``` +![image](https://user-images.githubusercontent.com/12690315/45935880-006a8200-bfeb-11e8-958e-ff742ae66b96.png) + +Hello, World! :tada: +``` +3. Set the Issue Label `release-summary` and add it to the GitHub Milestone `v1.0.0` +4. Close the Issue and execute `github-changelog-generator` +5. The result looks like this: +> ## [v1.0.0](https://github.com/github-changelog-generator/github-changelog-generator/tree/1.0.0) (2014-11-07) +> [Full Changelog](https://github.com/github-changelog-generator/github-changelog-generator/compare/0.1.0...1.0.0) +> +> ![image](https://user-images.githubusercontent.com/12690315/45935880-006a8200-bfeb-11e8-958e-ff742ae66b96.png) +> +> Hello, World! :tada: +> +> **Implemented enhancements:** +> - Add some features + +### Alternatives + +Here is a [wikipage list of alternatives](https://github.com/github-changelog-generator/Github-Changelog-Generator/wiki/Alternatives) that I found. But none satisfied my requirements. + +*If you know other projects, feel free to edit this Wiki page!* + + +### Projects using this library + +Here's a [wikipage list of projects](https://github.com/github-changelog-generator/Github-Changelog-Generator/wiki/Projects-using-Github-Changelog-Generator). + +If you've used this project in a live app, please let me know! Nothing makes me happier than seeing someone else take my work and go wild with it. + +*If you are using `github_changelog_generator` to generate your project's changelog, or know of other projects using it, please [add it to this list](https://github.com/github-changelog-generator/github-changelog-generator/wiki/Projects-using-Github-Changelog-Generator).* + +## Am I missing some essential feature? + +- **Nothing is impossible!** + +- Open an [issue](https://github.com/github-changelog-generator/Github-Changelog-Generator/issues/new) and let's make the generator better together! + +- *Bug reports, feature requests, patches, and well-wishes are always welcome.* :heavy_exclamation_mark: + +## FAQ + +- ***I already use GitHub Releases. Why do I need this?*** + +GitHub Releases is a very good thing. And it's very good practice to maintain it. (Not a lot of people are using it yet!) :congratulations: + +*BTW: I would like to support GitHub Releases in [next releases](https://github.com/github-changelog-generator/github-changelog-generator/issues/56) ;)* + +I'm not trying to compare the quality of handwritten and auto-generated logs. That said.... + +An auto-generated changelog really helps, even if you manually fill in the release notes! + + +- ***My Ruby version is very old, can I use this?*** + +When your Ruby is old, and you don't want to upgrade, and you want to +control which libraries you use, you can use Bundler. + +In a Gemfile, perhaps in a non-deployed `:development` group, add this +gem: + +```ruby +group :development do + gem 'github_changelog_generator', require: false +end +``` + +Then you can keep back dependencies like rack, which currently is only +compatible with Ruby >= 2.2.2. So, use an older version for your app by +adding a line like this to the Gemfile: + +``` +gem 'rack', '~> 1.6' +``` + +This way, you can keep on using github_changelog_generator, even if you +can't get the latest version of Ruby installed. + +- ***Windows: 1.14.x wants to create a file on an invalid path. Why?*** + +Windows: [v1.14.0 introduced a bug where it attempts to create /tmp/github_changelog-logger.log... which isn't a valid path on Windows and thus fails](https://github.com/github-changelog-generator/github-changelog-generator/issues/458) + +Workaround: Create a `C:\tmp`. + +## Contributing + +Would you like to contribute to this project? [CONTRIBUTING.md] has all the details on how to do that. + +[CONTRIBUTING.md]: CONTRIBUTING.md + +## Contact us +[Join the chat at gitter : github-changelog-generator](https://gitter.im/github-changelog-generator/chat?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +## License + +GitHub Changelog Generator is released under the [MIT License](http://www.opensource.org/licenses/MIT). diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/Rakefile b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/Rakefile new file mode 100644 index 0000000..b784c42 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/Rakefile @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require "bundler" +require "bundler/gem_tasks" +require "rubocop/rake_task" +require "rspec/core/rake_task" +require "pathname" +require "fileutils" +require "overcommit" + +RuboCop::RakeTask.new +RSpec::Core::RakeTask.new + +desc "When releasing the gem, re-fetch latest cacert.pem from curl.haxx.se. Developer task." +task :update_ssl_ca_file do + `pushd lib/github_changelog_generator/ssl_certs && curl --remote-name --time-cond cacert.pem https://curl.se/ca/cacert.pem && popd` +end + +task default: %i[rubocop spec] diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/bin/git-generate-changelog b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/bin/git-generate-changelog new file mode 100755 index 0000000..68dfad2 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/bin/git-generate-changelog @@ -0,0 +1,5 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require_relative "../lib/github_changelog_generator" +GitHubChangelogGenerator::ChangelogGenerator.new.run diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/bin/github_changelog_generator b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/bin/github_changelog_generator new file mode 100755 index 0000000..15a8d5e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/bin/github_changelog_generator @@ -0,0 +1,5 @@ +#! /usr/bin/env ruby +# frozen_string_literal: true + +require_relative "../lib/github_changelog_generator" +GitHubChangelogGenerator::ChangelogGenerator.new.run diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator.rb b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator.rb new file mode 100755 index 0000000..e514541 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator.rb @@ -0,0 +1,47 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "octokit" +require "faraday-http-cache" +require "logger" +require "active_support" +require "active_support/core_ext/object/blank" +require "json" +require "multi_json" +require "benchmark" + +require "github_changelog_generator/helper" +require "github_changelog_generator/options" +require "github_changelog_generator/parser" +require "github_changelog_generator/parser_file" +require "github_changelog_generator/generator/generator" +require "github_changelog_generator/version" +require "github_changelog_generator/reader" + +# The main module, where placed all classes (now, at least) +module GitHubChangelogGenerator + # Main class and entry point for this script. + class ChangelogGenerator + # Class, responsible for whole changelog generation cycle + # @return initialised instance of ChangelogGenerator + def initialize + @options = Parser.parse_options + @generator = Generator.new @options + end + + # The entry point of this script to generate changelog + # @raise (ChangelogGeneratorError) Is thrown when one of specified tags was not found in list of tags. + def run + log = @generator.compound_changelog + + if @options.write_to_file? + output_filename = @options[:output].to_s + File.open(output_filename, "wb") { |file| file.write(log) } + puts "Done!" + puts "Generated log placed in #{Dir.pwd}/#{output_filename}" + else + puts log + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/argv_parser.rb b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/argv_parser.rb new file mode 100755 index 0000000..e62bae7 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/argv_parser.rb @@ -0,0 +1,225 @@ +# frozen_string_literal: true + +require "optparse" +require "github_changelog_generator/version" + +module GitHubChangelogGenerator + class ArgvParser + attr_reader :options + + def initialize(options = {}) + @options = options + end + + def parse!(argv) + parser.parse(argv) + rescue OptionParser::ParseError => e + warn [e, parser].join("\n") + Kernel.abort + end + + def parser + @parser ||= OptionParser.new do |opts| # rubocop:disable Metrics/BlockLength + opts.banner = "Usage: github_changelog_generator --user USER --project PROJECT [options]" + opts.on("-u", "--user USER", "Username of the owner of the target GitHub repo OR the namespace of target Github repo if owned by an organization.") do |last| + options[:user] = last + end + opts.on("-p", "--project PROJECT", "Name of project on GitHub.") do |last| + options[:project] = last + end + opts.on("-t", "--token TOKEN", "To make more than 50 requests per hour your GitHub token is required. You can generate it at: https://github.com/settings/tokens/new") do |last| + options[:token] = last + end + opts.on("-f", "--date-format FORMAT", "Date format. Default is %Y-%m-%d.") do |last| + options[:date_format] = last + end + opts.on("-o", "--output NAME", "Output file. To print to STDOUT instead, use blank as path. Default is CHANGELOG.md") do |last| + options[:output] = last + end + opts.on("-b", "--base NAME", "Optional base file to append generated changes to. Default is HISTORY.md") do |last| + options[:base] = last + end + opts.on("--summary-label LABEL", "Set up custom label for the release summary section. Default is \"\".") do |v| + options[:summary_prefix] = v + end + opts.on("--breaking-label LABEL", "Set up custom label for the breaking changes section. Default is \"**Breaking changes:**\".") do |v| + options[:breaking_prefix] = v + end + opts.on("--enhancement-label LABEL", "Set up custom label for enhancements section. Default is \"**Implemented enhancements:**\".") do |v| + options[:enhancement_prefix] = v + end + opts.on("--bugs-label LABEL", "Set up custom label for bug-fixes section. Default is \"**Fixed bugs:**\".") do |v| + options[:bug_prefix] = v + end + opts.on("--deprecated-label LABEL", "Set up custom label for the deprecated changes section. Default is \"**Deprecated:**\".") do |v| + options[:deprecated_prefix] = v + end + opts.on("--removed-label LABEL", "Set up custom label for the removed changes section. Default is \"**Removed:**\".") do |v| + options[:removed_prefix] = v + end + opts.on("--security-label LABEL", "Set up custom label for the security changes section. Default is \"**Security fixes:**\".") do |v| + options[:security_prefix] = v + end + opts.on("--issues-label LABEL", "Set up custom label for closed-issues section. Default is \"**Closed issues:**\".") do |v| + options[:issue_prefix] = v + end + opts.on("--header-label LABEL", "Set up custom header label. Default is \"# Changelog\".") do |v| + options[:header] = v + end + opts.on("--configure-sections HASH, STRING", "Define your own set of sections which overrides all default sections.") do |v| + options[:configure_sections] = v + end + opts.on("--add-sections HASH, STRING", "Add new sections but keep the default sections.") do |v| + options[:add_sections] = v + end + opts.on("--front-matter JSON", "Add YAML front matter. Formatted as JSON because it's easier to add on the command line.") do |v| + require "yaml" + options[:frontmatter] = "#{JSON.parse(v).to_yaml}---\n" + end + opts.on("--pr-label LABEL", "Set up custom label for pull requests section. Default is \"**Merged pull requests:**\".") do |v| + options[:merge_prefix] = v + end + opts.on("--[no-]issues", "Include closed issues in changelog. Default is true.") do |v| + options[:issues] = v + end + opts.on("--[no-]issues-wo-labels", "Include closed issues without labels in changelog. Default is true.") do |v| + options[:add_issues_wo_labels] = v + end + opts.on("--[no-]pr-wo-labels", "Include pull requests without labels in changelog. Default is true.") do |v| + options[:add_pr_wo_labels] = v + end + opts.on("--[no-]pull-requests", "Include pull-requests in changelog. Default is true.") do |v| + options[:pulls] = v + end + opts.on("--[no-]filter-by-milestone", "Use milestone to detect when issue was resolved. Default is true.") do |last| + options[:filter_issues_by_milestone] = last + end + opts.on("--[no-]issues-of-open-milestones", "Include issues of open milestones. Default is true.") do |v| + options[:issues_of_open_milestones] = v + end + opts.on("--[no-]author", "Add author of pull request at the end. Default is true.") do |author| + options[:author] = author + end + opts.on("--usernames-as-github-logins", "Use GitHub tags instead of Markdown links for the author of an issue or pull-request.") do |v| + options[:usernames_as_github_logins] = v + end + opts.on("--unreleased-only", "Generate log from unreleased closed issues only.") do |v| + options[:unreleased_only] = v + end + opts.on("--[no-]unreleased", "Add to log unreleased closed issues. Default is true.") do |v| + options[:unreleased] = v + end + opts.on("--unreleased-label LABEL", "Set up custom label for unreleased closed issues section. Default is \"**Unreleased:**\".") do |v| + options[:unreleased_label] = v + end + opts.on("--[no-]compare-link", "Include compare link (Full Changelog) between older version and newer version. Default is true.") do |v| + options[:compare_link] = v + end + opts.on("--include-labels x,y,z", Array, "Of the labeled issues, only include the ones with the specified labels.") do |list| + options[:include_labels] = list + end + opts.on("--exclude-labels x,y,z", Array, "Issues with the specified labels will be excluded from changelog. Default is 'duplicate,question,invalid,wontfix'.") do |list| + options[:exclude_labels] = list + end + opts.on("--summary-labels x,y,z", Array, 'Issues with these labels will be added to a new section, called "Release Summary". The section display only body of issues. Default is \'release-summary,summary\'.') do |list| + options[:summary_labels] = list + end + opts.on("--breaking-labels x,y,z", Array, 'Issues with these labels will be added to a new section, called "Breaking changes". Default is \'backwards-incompatible,breaking\'.') do |list| + options[:breaking_labels] = list + end + opts.on("--enhancement-labels x,y,z", Array, 'Issues with the specified labels will be added to "Implemented enhancements" section. Default is \'enhancement,Enhancement\'.') do |list| + options[:enhancement_labels] = list + end + opts.on("--bug-labels x,y,z", Array, 'Issues with the specified labels will be added to "Fixed bugs" section. Default is \'bug,Bug\'.') do |list| + options[:bug_labels] = list + end + opts.on("--deprecated-labels x,y,z", Array, 'Issues with the specified labels will be added to a section called "Deprecated". Default is \'deprecated,Deprecated\'.') do |list| + options[:deprecated_labels] = list + end + opts.on("--removed-labels x,y,z", Array, 'Issues with the specified labels will be added to a section called "Removed". Default is \'removed,Removed\'.') do |list| + options[:removed_labels] = list + end + opts.on("--security-labels x,y,z", Array, 'Issues with the specified labels will be added to a section called "Security fixes". Default is \'security,Security\'.') do |list| + options[:security_labels] = list + end + opts.on("--issue-line-labels x,y,z", Array, 'The specified labels will be shown in brackets next to each matching issue. Use "ALL" to show all labels. Default is [].') do |list| + options[:issue_line_labels] = list + end + opts.on("--include-tags-regex REGEX", "Apply a regular expression on tag names so that they can be included, for example: --include-tags-regex \".*\+\d{1,}\".") do |last| + options[:include_tags_regex] = last + end + opts.on("--exclude-tags x,y,z", Array, "Changelog will exclude specified tags") do |list| + options[:exclude_tags] = list + end + opts.on("--exclude-tags-regex REGEX", "Apply a regular expression on tag names so that they can be excluded, for example: --exclude-tags-regex \".*\+\d{1,}\".") do |last| + options[:exclude_tags_regex] = last + end + opts.on("--since-tag x", "Changelog will start after specified tag.") do |v| + options[:since_tag] = v + end + opts.on("--due-tag x", "Changelog will end before specified tag.") do |v| + options[:due_tag] = v + end + opts.on("--since-commit x", "Fetch only commits after this time. eg. \"2017-01-01 10:00:00\"") do |v| + options[:since_commit] = v + end + opts.on("--max-issues NUMBER", Integer, "Maximum number of issues to fetch from GitHub. Default is unlimited.") do |max| + options[:max_issues] = max + end + opts.on("--release-url URL", "The URL to point to for release links, in printf format (with the tag as variable).") do |url| + options[:release_url] = url + end + opts.on("--github-site URL", "The Enterprise GitHub site where your project is hosted.") do |last| + options[:github_site] = last + end + opts.on("--github-api URL", "The enterprise endpoint to use for your GitHub API.") do |last| + options[:github_endpoint] = last + end + opts.on("--simple-list", "Create a simple list from issues and pull requests. Default is false.") do |v| + options[:simple_list] = v + end + opts.on("--future-release RELEASE-VERSION", "Put the unreleased changes in the specified release number.") do |future_release| + options[:future_release] = future_release + end + opts.on("--release-branch RELEASE-BRANCH", "Limit pull requests to the release branch, such as master or release.") do |release_branch| + options[:release_branch] = release_branch + end + opts.on("--[no-]http-cache", "Use HTTP Cache to cache GitHub API requests (useful for large repos). Default is true.") do |http_cache| + options[:http_cache] = http_cache + end + opts.on("--cache-file CACHE-FILE", "Filename to use for cache. Default is github-changelog-http-cache in a temporary directory.") do |cache_file| + options[:cache_file] = cache_file + end + opts.on("--cache-log CACHE-LOG", "Filename to use for cache log. Default is github-changelog-logger.log in a temporary directory.") do |cache_log| + options[:cache_log] = cache_log + end + opts.on("--config-file CONFIG-FILE", "Path to configuration file. Default is .github_changelog_generator.") do |config_file| + options[:config_file] = config_file + end + opts.on("--ssl-ca-file PATH", "Path to cacert.pem file. Default is a bundled lib/github_changelog_generator/ssl_certs/cacert.pem. Respects SSL_CA_PATH.") do |ssl_ca_file| + options[:ssl_ca_file] = ssl_ca_file + end + opts.on("--require x,y,z", Array, "Path to Ruby file(s) to require before generating changelog.") do |paths| + options[:require] = paths + end + opts.on("--[no-]verbose", "Run verbosely. Default is true.") do |v| + options[:verbose] = v + end + opts.on("-v", "--version", "Print version number.") do |_v| + puts "Version: #{GitHubChangelogGenerator::VERSION}" + exit + end + opts.on("-h", "--help", "Displays Help.") do + puts opts + exit + end + end + end + + class << self + def banner + new.parser.banner + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/generator/entry.rb b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/generator/entry.rb new file mode 100644 index 0000000..4c7a5aa --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/generator/entry.rb @@ -0,0 +1,218 @@ +# frozen_string_literal: true + +require "github_changelog_generator/generator/section" + +module GitHubChangelogGenerator + # This class generates the content for a single changelog entry. An entry is + # generally either for a specific tagged release or the collection of + # unreleased changes. + # + # An entry is comprised of header text followed by a series of sections + # relating to the entry. + # + # @see GitHubChangelogGenerator::Generator + # @see GitHubChangelogGenerator::Section + class Entry + attr_reader :content + + def initialize(options = Options.new({})) + @content = "" + @options = Options.new(options) + end + + # Generates log entry with header and body + # + # @param [Array] pull_requests List or PR's in new section + # @param [Array] issues List of issues in new section + # @param [String] newer_tag_name Name of the newer tag. Could be nil for `Unreleased` section. + # @param [String] newer_tag_link Name of the newer tag. Could be "HEAD" for `Unreleased` section. + # @param [Time] newer_tag_time Time of the newer tag + # @param [Hash, nil] older_tag_name Older tag, used for the links. Could be nil for last tag. + # @return [String] Ready and parsed section content. + def generate_entry_for_tag(pull_requests, issues, newer_tag_name, newer_tag_link, newer_tag_time, older_tag_name) # rubocop:disable Metrics/ParameterLists + github_site = @options[:github_site] || "https://github.com" + project_url = "#{github_site}/#{@options[:user]}/#{@options[:project]}" + + create_sections + + @content = generate_header(newer_tag_name, newer_tag_link, newer_tag_time, older_tag_name, project_url) + @content += generate_body(pull_requests, issues) + @content + end + + def line_labels_for(issue) + labels = if @options[:issue_line_labels] == ["ALL"] + issue["labels"] + else + issue["labels"].select { |label| @options[:issue_line_labels].include?(label["name"]) } + end + labels.map { |label| " \[[#{label['name']}](#{label['url'].sub('api.github.com/repos', 'github.com')})\]" }.join("") + end + + private + + # Creates section objects for this entry. + # @return [Nil] + def create_sections + @sections = if @options.configure_sections? + parse_sections(@options[:configure_sections]) + elsif @options.add_sections? + default_sections.concat parse_sections(@options[:add_sections]) + else + default_sections + end + nil + end + + # Turns the argument from the commandline of --configure-sections or + # --add-sections into an array of Section objects. + # + # @param [String, Hash] sections_desc Either string or hash describing sections + # @return [Array] Parsed section objects. + def parse_sections(sections_desc) + require "json" + + sections_desc = sections_desc.to_json if sections_desc.class == Hash + + begin + sections_json = JSON.parse(sections_desc) + rescue JSON::ParserError => e + raise "There was a problem parsing your JSON string for sections: #{e}" + end + + sections_json.collect do |name, v| + Section.new(name: name.to_s, prefix: v["prefix"], labels: v["labels"], body_only: v["body_only"], options: @options) + end + end + + # Generates header text for an entry. + # + # @param [String] newer_tag_name The name of a newer tag + # @param [String] newer_tag_link Used for URL generation. Could be same as #newer_tag_name or some specific value, like HEAD + # @param [Time] newer_tag_time Time when the newer tag was created + # @param [String] older_tag_name The name of an older tag; used for URLs. + # @param [String] project_url URL for the current project. + # @return [String] Header text content. + def generate_header(newer_tag_name, newer_tag_link, newer_tag_time, older_tag_name, project_url) + header = "" + + # Generate date string: + time_string = newer_tag_time.strftime(@options[:date_format]) + + # Generate tag name and link + release_url = if @options[:release_url] + format(@options[:release_url], newer_tag_link) + else + "#{project_url}/tree/#{newer_tag_link}" + end + header += if newer_tag_name.equal?(@options[:unreleased_label]) + "## [#{newer_tag_name}](#{release_url})\n\n" + else + "## [#{newer_tag_name}](#{release_url}) (#{time_string})\n\n" + end + + if @options[:compare_link] && older_tag_name + # Generate compare link + header += "[Full Changelog](#{project_url}/compare/#{older_tag_name}...#{newer_tag_link})\n\n" + end + + header + end + + # Generates complete body text for a tag (without a header) + # + # @param [Array] pull_requests + # @param [Array] issues + # @return [String] Content generated from sections of sorted issues & PRs. + def generate_body(pull_requests, issues) + sort_into_sections(pull_requests, issues) + @sections.map(&:generate_content).join + end + + # Default sections to used when --configure-sections is not set. + # + # @return [Array] Section objects. + def default_sections + [ + Section.new(name: "summary", prefix: @options[:summary_prefix], labels: @options[:summary_labels], options: @options, body_only: true), + Section.new(name: "breaking", prefix: @options[:breaking_prefix], labels: @options[:breaking_labels], options: @options), + Section.new(name: "enhancements", prefix: @options[:enhancement_prefix], labels: @options[:enhancement_labels], options: @options), + Section.new(name: "bugs", prefix: @options[:bug_prefix], labels: @options[:bug_labels], options: @options), + Section.new(name: "deprecated", prefix: @options[:deprecated_prefix], labels: @options[:deprecated_labels], options: @options), + Section.new(name: "removed", prefix: @options[:removed_prefix], labels: @options[:removed_labels], options: @options), + Section.new(name: "security", prefix: @options[:security_prefix], labels: @options[:security_labels], options: @options) + ] + end + + # Sorts issues and PRs into entry sections by labels and lack of labels. + # + # @param [Array] pull_requests + # @param [Array] issues + # @return [Nil] + def sort_into_sections(pull_requests, issues) + if @options[:issues] + unmapped_issues = sort_labeled_issues(issues) + add_unmapped_section(unmapped_issues) + end + if @options[:pulls] + unmapped_pull_requests = sort_labeled_issues(pull_requests) + add_unmapped_section(unmapped_pull_requests) + end + nil + end + + # Iterates through sections and sorts labeled issues into them based on + # the label mapping. Returns any unmapped or unlabeled issues. + # + # @param [Array] issues Issues or pull requests. + # @return [Array] Issues that were not mapped into any sections. + def sort_labeled_issues(issues) + sorted_issues = [] + issues.each do |issue| + label_names = issue["labels"].collect { |l| l["name"] } + + # Add PRs in the order of the @sections array. This will either be the + # default sections followed by any --add-sections sections in + # user-defined order, or --configure-sections in user-defined order. + # Ignore the order of the issue labels from github which cannot be + # controled by the user. + @sections.each do |section| + unless (section.labels & label_names).empty? + section.issues << issue + sorted_issues << issue + break + end + end + end + issues - sorted_issues + end + + # Creates a section for issues/PRs with no labels or no mapped labels. + # + # @param [Array] issues + # @return [Nil] + def add_unmapped_section(issues) + unless issues.empty? + # Distinguish between issues and pull requests + if issues.first.key?("pull_request") + name = "merged" + prefix = @options[:merge_prefix] + add_wo_labels = @options[:add_pr_wo_labels] + else + name = "issues" + prefix = @options[:issue_prefix] + add_wo_labels = @options[:add_issues_wo_labels] + end + add_issues = if add_wo_labels + issues + else + # Only add unmapped issues + issues.select { |issue| issue["labels"].any? } + end + merged = Section.new(name: name, prefix: prefix, labels: [], issues: add_issues, options: @options) unless add_issues.empty? + @sections << merged + end + nil + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/generator/generator.rb b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/generator/generator.rb new file mode 100644 index 0000000..143d60e --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/generator/generator.rb @@ -0,0 +1,177 @@ +# frozen_string_literal: true + +require "github_changelog_generator/octo_fetcher" +require "github_changelog_generator/generator/generator_fetcher" +require "github_changelog_generator/generator/generator_processor" +require "github_changelog_generator/generator/generator_tags" +require "github_changelog_generator/generator/entry" +require "github_changelog_generator/generator/section" + +module GitHubChangelogGenerator + # Default error for ChangelogGenerator + class ChangelogGeneratorError < StandardError + end + + # This class is the high-level code for gathering issues and PRs for a github + # repository and generating a CHANGELOG.md file. A changelog is made up of a + # series of "entries" of all tagged releases, plus an extra entry for the + # unreleased changes. Entries are made up of various organizational + # "sections," and sections contain the github issues and PRs. + # + # So the changelog contains entries, entries contain sections, and sections + # contain issues and PRs. + # + # @see GitHubChangelogGenerator::Entry + # @see GitHubChangelogGenerator::Section + class Generator + attr_accessor :options, :filtered_tags, :tag_section_mapping, :sorted_tags + + CREDIT_LINE = <<~CREDIT + \\* *This Changelog was automatically generated \ + by [github_changelog_generator]\ + (https://github.com/github-changelog-generator/github-changelog-generator)* + CREDIT + + # A Generator responsible for all logic, related with changelog generation from ready-to-parse issues + # + # Example: + # generator = GitHubChangelogGenerator::Generator.new + # content = generator.compound_changelog + def initialize(options = {}) + @options = options + @tag_times_hash = {} + @fetcher = GitHubChangelogGenerator::OctoFetcher.new(options) + @sections = [] + end + + # Main function to start changelog generation + # + # @return [String] Generated changelog file + def compound_changelog + @options.load_custom_ruby_files + + Sync do + fetch_and_filter_tags + fetch_issues_and_pr + + log = if @options[:unreleased_only] + generate_entry_between_tags(@filtered_tags[0], nil) + else + generate_entries_for_all_tags + end + log += File.read(@options[:base]) if File.file?(@options[:base]) + log = remove_old_fixed_string(log) + log = insert_fixed_string(log) + @log = log + end + end + + private + + # Generate log only between 2 specified tags + # @param [String] older_tag all issues before this tag date will be excluded. May be nil, if it's first tag + # @param [String] newer_tag all issue after this tag will be excluded. May be nil for unreleased section + def generate_entry_between_tags(older_tag, newer_tag) + filtered_issues, filtered_pull_requests = filter_issues_for_tags(newer_tag, older_tag) + + if newer_tag.nil? && filtered_issues.empty? && filtered_pull_requests.empty? + # do not generate empty unreleased section + return "" + end + + newer_tag_link, newer_tag_name, newer_tag_time = detect_link_tag_time(newer_tag) + + # If the older tag is nil, go back in time from the latest tag and find + # the SHA for the first commit. + older_tag_name = + if older_tag.nil? + @fetcher.oldest_commit["sha"] + else + older_tag["name"] + end + + Entry.new(options).generate_entry_for_tag(filtered_pull_requests, filtered_issues, newer_tag_name, newer_tag_link, newer_tag_time, older_tag_name) + end + + # Filters issues and pull requests based on, respectively, `actual_date` + # and `merged_at` timestamp fields. `actual_date` is the detected form of + # `closed_at` based on merge event SHA commit times. + # + # @return [Array] filtered issues and pull requests + def filter_issues_for_tags(newer_tag, older_tag) + filtered_pull_requests = filter_by_tag(@pull_requests, newer_tag) + filtered_issues = delete_by_time(@issues, "actual_date", older_tag, newer_tag) + + newer_tag_name = newer_tag.nil? ? nil : newer_tag["name"] + + if options[:filter_issues_by_milestone] + # delete excess irrelevant issues (according milestones). Issue #22. + filtered_issues = filter_by_milestone(filtered_issues, newer_tag_name, @issues) + filtered_pull_requests = filter_by_milestone(filtered_pull_requests, newer_tag_name, @pull_requests) + end + [filtered_issues, filtered_pull_requests] + end + + # The full cycle of generation for whole project + # @return [String] All entries in the changelog + def generate_entries_for_all_tags + puts "Generating entry..." if options[:verbose] + + entries = generate_unreleased_entry + + @tag_section_mapping.each_pair do |_tag_section, left_right_tags| + older_tag, newer_tag = left_right_tags + entries += generate_entry_between_tags(older_tag, newer_tag) + end + + entries + end + + def generate_unreleased_entry + entry = "" + if options[:unreleased] + start_tag = @filtered_tags[0] || @sorted_tags.last + unreleased_entry = generate_entry_between_tags(start_tag, nil) + entry += unreleased_entry if unreleased_entry + end + entry + end + + # Fetches @pull_requests and @issues and filters them based on options. + # + # @return [Nil] No return. + def fetch_issues_and_pr + issues, pull_requests = @fetcher.fetch_closed_issues_and_pr + + @pull_requests = options[:pulls] ? get_filtered_pull_requests(pull_requests) : [] + + @issues = options[:issues] ? get_filtered_issues(issues) : [] + + fetch_events_for_issues_and_pr + detect_actual_closed_dates(@issues + @pull_requests) + add_first_occurring_tag_to_prs(@sorted_tags, @pull_requests) + nil + end + + # Remove the previously assigned fixed message. + # @param log [String] Old lines are fixed + def remove_old_fixed_string(log) + log.gsub!(/#{Regexp.escape(@options[:frontmatter])}/, "") if @options[:frontmatter] + log.gsub!(/#{Regexp.escape(@options[:header])}\n{,2}/, "") + log.gsub!(/\n{,2}#{Regexp.escape(CREDIT_LINE)}/, "") # Remove old credit lines + log + end + + # Add template messages to given string. Previously added + # messages of the same wording are removed. + # @param log [String] + def insert_fixed_string(log) + ins = "" + ins += @options[:frontmatter] if @options[:frontmatter] + ins += "#{@options[:header]}\n\n" + log.insert(0, ins) + log += "\n\n#{CREDIT_LINE}" + log + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/generator/generator_fetcher.rb b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/generator/generator_fetcher.rb new file mode 100644 index 0000000..63a9fc0 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/generator/generator_fetcher.rb @@ -0,0 +1,203 @@ +# frozen_string_literal: true + +module GitHubChangelogGenerator + class Generator + # Fetch event for issues and pull requests + # @return [Array] array of fetched issues + def fetch_events_for_issues_and_pr + print "Fetching events for issues and PR: 0/#{@issues.count + @pull_requests.count}\r" if options[:verbose] + + # Async fetching events: + @fetcher.fetch_events_async(@issues + @pull_requests) + end + + # Async fetching of all tags dates + def fetch_tags_dates(tags) + print "Fetching tag dates...\r" if options[:verbose] + i = 0 + tags.each do |tag| + get_time_of_tag(tag) + i += 1 + end + puts "Fetching tags dates: #{i}/#{tags.count}" if options[:verbose] + end + + # Find correct closed dates, if issues was closed by commits + def detect_actual_closed_dates(issues) + print "Fetching closed dates for issues...\r" if options[:verbose] + + i = 0 + issues.each do |issue| + find_closed_date_by_commit(issue) + i += 1 + end + puts "Fetching closed dates for issues: #{i}/#{issues.count}" if options[:verbose] + end + + # Adds a key "first_occurring_tag" to each PR with a value of the oldest + # tag that a PR's merge commit occurs in in the git history. This should + # indicate the release of each PR by git's history regardless of dates and + # divergent branches. + # + # @param [Array] tags The tags sorted by time, newest to oldest. + # @param [Array] prs The PRs to discover the tags of. + # @return [Nil] No return; PRs are updated in-place. + def add_first_occurring_tag_to_prs(tags, prs) + total = prs.count + + prs_left = associate_tagged_prs(tags, prs, total) + prs_left = associate_release_branch_prs(prs_left, total) + prs_left = associate_rebase_comment_prs(tags, prs_left, total) if prs_left.any? + # PRs in prs_left will be untagged, not in release branch, and not + # rebased. They should not be included in the changelog as they probably + # have been merged to a branch other than the release branch. + @pull_requests -= prs_left + Helper.log.info "Associating PRs with tags: #{total}/#{total}" + end + + # Associate merged PRs by the merge SHA contained in each tag. If the + # merge_commit_sha is not found in any tag's history, skip association. + # + # @param [Array] tags The tags sorted by time, newest to oldest. + # @param [Array] prs The PRs to associate. + # @return [Array] PRs without their merge_commit_sha in a tag. + def associate_tagged_prs(tags, prs, total) + @fetcher.fetch_tag_shas(tags) + + i = 0 + prs.reject do |pr| + found = false + # XXX Wish I could use merge_commit_sha, but gcg doesn't currently + # fetch that. See + # https://developer.github.com/v3/pulls/#get-a-single-pull-request vs. + # https://developer.github.com/v3/pulls/#list-pull-requests + if pr["events"] && (event = pr["events"].find { |e| e["event"] == "merged" }) + # Iterate tags.reverse (oldest to newest) to find first tag of each PR. + if (oldest_tag = tags.reverse.find { |tag| tag["shas_in_tag"].include?(event["commit_id"]) }) + pr["first_occurring_tag"] = oldest_tag["name"] + found = true + i += 1 + print("Associating PRs with tags: #{i}/#{total}\r") if @options[:verbose] + end + else + # Either there were no events or no merged event. Github's api can be + # weird like that apparently. Check for a rebased comment before erroring. + no_events_pr = associate_rebase_comment_prs(tags, [pr], total) + raise StandardError, "No merge sha found for PR #{pr['number']} via the GitHub API" unless no_events_pr.empty? + + found = true + i += 1 + print("Associating PRs with tags: #{i}/#{total}\r") if @options[:verbose] + end + found + end + end + + # Associate merged PRs by the HEAD of the release branch. If no + # --release-branch was specified, then the github default branch is used. + # + # @param [Array] prs_left PRs not associated with any tag. + # @param [Integer] total The total number of PRs to associate; used for verbose printing. + # @return [Array] PRs without their merge_commit_sha in the branch. + def associate_release_branch_prs(prs_left, total) + if prs_left.any? + i = total - prs_left.count + prs_left.reject do |pr| + found = false + if pr["events"] && (event = pr["events"].find { |e| e["event"] == "merged" }) && sha_in_release_branch?(event["commit_id"]) + found = true + i += 1 + print("Associating PRs with tags: #{i}/#{total}\r") if @options[:verbose] + end + found + end + else + prs_left + end + end + + # Associate merged PRs by the SHA detected in github comments of the form + # "rebased commit: ". For use when the merge_commit_sha is not in the + # actual git history due to rebase. + # + # @param [Array] tags The tags sorted by time, newest to oldest. + # @param [Array] prs_left The PRs not yet associated with any tag or branch. + # @return [Array] PRs without rebase comments. + def associate_rebase_comment_prs(tags, prs_left, total) + i = total - prs_left.count + # Any remaining PRs were not found in the list of tags by their merge + # commit and not found in any specified release branch. Fallback to + # rebased commit comment. + @fetcher.fetch_comments_async(prs_left) + prs_left.reject do |pr| + found = false + if pr["comments"] && (rebased_comment = pr["comments"].reverse.find { |c| c["body"].match(%r{rebased commit: ([0-9a-f]{40})}i) }) + rebased_sha = rebased_comment["body"].match(%r{rebased commit: ([0-9a-f]{40})}i)[1] + if (oldest_tag = tags.reverse.find { |tag| tag["shas_in_tag"].include?(rebased_sha) }) + pr["first_occurring_tag"] = oldest_tag["name"] + found = true + i += 1 + elsif sha_in_release_branch?(rebased_sha) + found = true + i += 1 + else + raise StandardError, "PR #{pr['number']} has a rebased SHA comment but that SHA was not found in the release branch or any tags" + end + print("Associating PRs with tags: #{i}/#{total}\r") if @options[:verbose] + else + puts "Warning: PR #{pr['number']} merge commit was not found in the release branch or tagged git history and no rebased SHA comment was found" + end + found + end + end + + # Fill :actual_date parameter of specified issue by closed date of the commit, if it was closed by commit. + # @param [Hash] issue + def find_closed_date_by_commit(issue) + unless issue["events"].nil? + # if it's PR -> then find "merged event", in case of usual issue -> fond closed date + compare_string = issue["merged_at"].nil? ? "closed" : "merged" + # reverse! - to find latest closed event. (event goes in date order) + issue["events"].reverse!.each do |event| + if event["event"].eql? compare_string + set_date_from_event(event, issue) + break + end + end + end + # TODO: assert issues, that remain without 'actual_date' hash for some reason. + end + + # Set closed date from this issue + # + # @param [Hash] event + # @param [Hash] issue + def set_date_from_event(event, issue) + if event["commit_id"].nil? + issue["actual_date"] = issue["closed_at"] + else + begin + commit = @fetcher.fetch_commit(event["commit_id"]) + issue["actual_date"] = commit["commit"]["author"]["date"] + + # issue['actual_date'] = commit['author']['date'] + rescue StandardError + puts "Warning: Can't fetch commit #{event['commit_id']}. It is probably referenced from another repo." + issue["actual_date"] = issue["closed_at"] + end + end + end + + private + + # Detect if a sha occurs in the --release-branch. Uses the github repo + # default branch if not specified. + # + # @param [String] sha SHA to check. + # @return [Boolean] True if SHA is in the branch git history. + def sha_in_release_branch?(sha) + branch = @options[:release_branch] || @fetcher.default_branch + @fetcher.commits_in_branch(branch).include?(sha) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/generator/generator_processor.rb b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/generator/generator_processor.rb new file mode 100644 index 0000000..a3bb319 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/generator/generator_processor.rb @@ -0,0 +1,239 @@ +# frozen_string_literal: true + +module GitHubChangelogGenerator + class Generator + # delete all issues with labels from options[:exclude_labels] array + # @param [Array] issues + # @return [Array] filtered array + def exclude_issues_by_labels(issues) + return issues if !options[:exclude_labels] || options[:exclude_labels].empty? + + issues.reject do |issue| + labels = issue["labels"].map { |l| l["name"] } + (labels & options[:exclude_labels]).any? + end + end + + # Only include issues without labels if options[:add_issues_wo_labels] + # @param [Array] issues + # @return [Array] filtered array + def exclude_issues_without_labels(issues) + return issues if issues.empty? + return issues if issues.first.key?("pull_request") && options[:add_pr_wo_labels] + return issues if !issues.first.key?("pull_request") && options[:add_issues_wo_labels] + + issues.reject do |issue| + issue["labels"].empty? + end + end + + # @return [Array] filtered issues accourding milestone + def filter_by_milestone(filtered_issues, tag_name, all_issues) + remove_issues_in_milestones(filtered_issues) + unless tag_name.nil? + # add missed issues (according milestones) + issues_to_add = find_issues_to_add(all_issues, tag_name) + + filtered_issues |= issues_to_add + end + filtered_issues + end + + # Add all issues, that should be in that tag, according milestone + # + # @param [Array] all_issues + # @param [String] tag_name + # @return [Array] issues with milestone #tag_name + def find_issues_to_add(all_issues, tag_name) + all_issues.select do |issue| + if issue["milestone"].nil? + false + else + # check, that this milestone in tag list: + milestone_is_tag = @filtered_tags.find do |tag| + tag["name"] == issue["milestone"]["title"] + end + + if milestone_is_tag.nil? + false + else + issue["milestone"]["title"] == tag_name + end + end + end + end + + # @return [Array] array with removed issues, that contain milestones with same name as a tag + def remove_issues_in_milestones(filtered_issues) + filtered_issues.select! do |issue| + # leave issues without milestones + if issue["milestone"].nil? + true + # remove issues of open milestones if option is set + elsif issue["milestone"]["state"] == "open" + @options[:issues_of_open_milestones] + else + # check, that this milestone in tag list: + @filtered_tags.find { |tag| tag["name"] == issue["milestone"]["title"] }.nil? + end + end + end + + # Method filter issues, that belong only specified tag range + # + # @param [Array] issues issues to filter + # @param [Hash, Nil] newer_tag Tag to find PRs of. May be nil for unreleased section + # @return [Array] filtered issues + def filter_by_tag(issues, newer_tag = nil) + issues.select do |issue| + issue["first_occurring_tag"] == (newer_tag.nil? ? nil : newer_tag["name"]) + end + end + + # Method filter issues, that belong only specified tag range + # @param [Array] issues issues to filter + # @param [Symbol] hash_key key of date value default is :actual_date + # @param [Hash, Nil] older_tag all issues before this tag date will be excluded. May be nil, if it's first tag + # @param [Hash, Nil] newer_tag all issue after this tag will be excluded. May be nil for unreleased section + # @return [Array] filtered issues + def delete_by_time(issues, hash_key = "actual_date", older_tag = nil, newer_tag = nil) + # in case if not tags specified - return unchanged array + return issues if older_tag.nil? && newer_tag.nil? + + older_tag = ensure_older_tag(older_tag, newer_tag) + + newer_tag_time = newer_tag && get_time_of_tag(newer_tag) + older_tag_time = older_tag && get_time_of_tag(older_tag) + + issues.select do |issue| + if issue[hash_key] + time = Time.parse(issue[hash_key].to_s).utc + + tag_in_range_old = tag_newer_old_tag?(older_tag_time, time) + + tag_in_range_new = tag_older_new_tag?(newer_tag_time, time) + + tag_in_range = tag_in_range_old && tag_in_range_new + + tag_in_range + else + false + end + end + end + + def ensure_older_tag(older_tag, newer_tag) + return older_tag if older_tag + + idx = sorted_tags.index { |t| t["name"] == newer_tag["name"] } + # skip if we are already at the oldest element + return if idx == sorted_tags.size - 1 + + sorted_tags[idx - 1] + end + + def tag_older_new_tag?(newer_tag_time, time) + if newer_tag_time.nil? + true + else + time <= newer_tag_time + end + end + + def tag_newer_old_tag?(older_tag_time, time) + if older_tag_time.nil? + true + else + time > older_tag_time + end + end + + # Include issues with labels, specified in :include_labels + # @param [Array] issues to filter + # @return [Array] filtered array of issues + def include_issues_by_labels(issues) + filtered_issues = filter_by_include_labels(issues) + filter_wo_labels(filtered_issues) + end + + # @param [Array] items Issues & PRs to filter when without labels + # @return [Array] Issues & PRs without labels or empty array if + # add_issues_wo_labels or add_pr_wo_labels are false + def filter_wo_labels(items) + if items.any? && items.first.key?("pull_request") + return items if options[:add_pr_wo_labels] + elsif options[:add_issues_wo_labels] + return items + end + # The default is to filter items without labels + items.select { |item| item["labels"].map { |l| l["name"] }.any? } + end + + # @todo Document this + # @param [Object] issues + def filter_by_include_labels(issues) + if options[:include_labels].nil? + issues + else + issues.select do |issue| + labels = issue["labels"].map { |l| l["name"] } & options[:include_labels] + labels.any? || issue["labels"].empty? + end + end + end + + # General filtered function + # + # @param [Array] all_issues PRs or issues + # @return [Array] filtered issues + def filter_array_by_labels(all_issues) + filtered_issues = include_issues_by_labels(all_issues) + filtered_issues = exclude_issues_by_labels(filtered_issues) + exclude_issues_without_labels(filtered_issues) + end + + # Filter issues according labels + # @return [Array] Filtered issues + def get_filtered_issues(issues) + issues = filter_array_by_labels(issues) + puts "Filtered issues: #{issues.count}" if options[:verbose] + issues + end + + # This method fetches missing params for PR and filter them by specified options + # It include add all PR's with labels from options[:include_labels] array + # And exclude all from :exclude_labels array. + # @return [Array] filtered PR's + def get_filtered_pull_requests(pull_requests) + pull_requests = filter_array_by_labels(pull_requests) + pull_requests = filter_merged_pull_requests(pull_requests) + puts "Filtered pull requests: #{pull_requests.count}" if options[:verbose] + pull_requests + end + + # This method filter only merged PR and + # fetch missing required attributes for pull requests + # :merged_at - is a date, when issue PR was merged. + # More correct to use merged date, rather than closed date. + def filter_merged_pull_requests(pull_requests) + print "Fetching merged dates...\r" if options[:verbose] + closed_pull_requests = @fetcher.fetch_closed_pull_requests + + pull_requests.each do |pr| + fetched_pr = closed_pull_requests.find do |fpr| + fpr["number"] == pr["number"] + end + if fetched_pr + pr["merged_at"] = fetched_pr["merged_at"] + closed_pull_requests.delete(fetched_pr) + end + end + + pull_requests.reject! do |pr| + pr["merged_at"].nil? + end + + pull_requests + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/generator/generator_tags.rb b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/generator/generator_tags.rb new file mode 100644 index 0000000..8d202cf --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/generator/generator_tags.rb @@ -0,0 +1,220 @@ +# frozen_string_literal: true + +module GitHubChangelogGenerator + class Generator + # fetch, filter tags, fetch dates and sort them in time order + def fetch_and_filter_tags + since_tag + due_tag + + all_tags = @fetcher.get_all_tags + fetch_tags_dates(all_tags) # Creates a Hash @tag_times_hash + all_sorted_tags = sort_tags_by_date(all_tags) + + @sorted_tags = filter_included_tags(all_sorted_tags) + @sorted_tags = filter_excluded_tags(@sorted_tags) + @filtered_tags = get_filtered_tags(@sorted_tags) + @tag_section_mapping = build_tag_section_mapping(@filtered_tags, @filtered_tags) + + @filtered_tags + end + + # @param [Array] section_tags are the tags that need a subsection output + # @param [Array] filtered_tags is the list of filtered tags ordered from newest -> oldest + # @return [Hash] key is the tag to output, value is an array of [Left Tag, Right Tag] + # PRs to include in this section will be >= [Left Tag Date] and <= [Right Tag Date] + # rubocop:disable Style/For - for allows us to be more concise + def build_tag_section_mapping(section_tags, filtered_tags) + tag_mapping = {} + for i in 0..(section_tags.length - 1) + tag = section_tags[i] + + # Don't create section header for the "since" tag + next if since_tag && tag["name"] == since_tag + + # Don't create a section header for the first tag in between_tags + next if options[:between_tags] && tag == section_tags.last + + # Don't create a section header for excluded tags + next unless filtered_tags.include?(tag) + + older_tag = section_tags[i + 1] + tag_mapping[tag] = [older_tag, tag] + end + tag_mapping + end + # rubocop:enable Style/For + + # Sort all tags by date, newest to oldest + def sort_tags_by_date(tags) + puts "Sorting tags..." if options[:verbose] + tags.sort_by! do |x| + get_time_of_tag(x) + end.reverse! + end + + # Returns date for given GitHub Tag hash + # + # Memoize the date by tag name. + # + # @param [Hash] tag_name + # + # @return [Time] time of specified tag + def get_time_of_tag(tag_name) + raise ChangelogGeneratorError, "tag_name is nil" if tag_name.nil? + + name_of_tag = tag_name.fetch("name") + time_for_tag_name = @tag_times_hash[name_of_tag] + return time_for_tag_name if time_for_tag_name + + @fetcher.fetch_date_of_tag(tag_name).tap do |time_string| + @tag_times_hash[name_of_tag] = time_string + end + end + + # Detect link, name and time for specified tag. + # + # @param [Hash] newer_tag newer tag. Can be nil, if it's Unreleased section. + # @return [Array] link, name and time of the tag + def detect_link_tag_time(newer_tag) + # if tag is nil - set current time + newer_tag_time = newer_tag.nil? ? Time.new.getutc : get_time_of_tag(newer_tag) + + # if it's future release tag - set this value + if newer_tag.nil? && options[:future_release] + newer_tag_name = options[:future_release] + newer_tag_link = options[:future_release] + else + # put unreleased label if there is no name for the tag + newer_tag_name = newer_tag.nil? ? options[:unreleased_label] : newer_tag["name"] + newer_tag_link = newer_tag.nil? ? "HEAD" : newer_tag_name + end + [newer_tag_link, newer_tag_name, newer_tag_time] + end + + # @return [Object] try to find newest tag using #Reader and :base option if specified otherwise returns nil + def since_tag + @since_tag ||= options.fetch(:since_tag) { version_of_first_item } + end + + def due_tag + @due_tag ||= options.fetch(:due_tag, nil) + end + + def version_of_first_item + return unless File.file?(options[:base].to_s) + + sections = GitHubChangelogGenerator::Reader.new.read(options[:base]) + sections.first["version"] if sections && sections.any? + end + + # Return tags after filtering tags in lists provided by option: --exclude-tags + # + # @return [Array] + def get_filtered_tags(all_tags) + filtered_tags = filter_since_tag(all_tags) + filter_due_tag(filtered_tags) + end + + # @param [Array] all_tags all tags + # @return [Array] filtered tags according :since_tag option + def filter_since_tag(all_tags) + filtered_tags = all_tags + tag = since_tag + if tag + if all_tags.map { |t| t["name"] }.include? tag + idx = all_tags.index { |t| t["name"] == tag } + filtered_tags = if idx + all_tags[0..idx] + else + [] + end + else + raise ChangelogGeneratorError, "Error: can't find tag #{tag}, specified with --since-tag option." + end + end + filtered_tags + end + + # @param [Array] all_tags all tags + # @return [Array] filtered tags according :due_tag option + def filter_due_tag(all_tags) + filtered_tags = all_tags + tag = due_tag + if tag + if all_tags.any? && all_tags.map { |t| t["name"] }.include?(tag) + idx = all_tags.index { |t| t["name"] == tag } + filtered_tags = if idx > 0 + all_tags[(idx + 1)..-1] + else + [] + end + else + raise ChangelogGeneratorError, "Error: can't find tag #{tag}, specified with --due-tag option." + end + end + filtered_tags + end + + # @param [Array] all_tags all tags + # @return [Array] filtered tags according to :include_tags_regex option + def filter_included_tags(all_tags) + if options[:include_tags_regex] + regex = Regexp.new(options[:include_tags_regex]) + all_tags.select { |tag| regex =~ tag["name"] } + else + all_tags + end + end + + # @param [Array] all_tags all tags + # @return [Array] filtered tags according :exclude_tags or :exclude_tags_regex option + def filter_excluded_tags(all_tags) + if options[:exclude_tags] + apply_exclude_tags(all_tags) + elsif options[:exclude_tags_regex] + apply_exclude_tags_regex(all_tags) + else + all_tags + end + end + + private + + def apply_exclude_tags(all_tags) + if options[:exclude_tags].is_a?(Regexp) + filter_tags_with_regex(all_tags, options[:exclude_tags], "--exclude-tags") + else + filter_exact_tags(all_tags) + end + end + + def apply_exclude_tags_regex(all_tags) + regex = Regexp.new(options[:exclude_tags_regex]) + filter_tags_with_regex(all_tags, regex, "--exclude-tags-regex") + end + + def filter_tags_with_regex(all_tags, regex, regex_option_name) + warn_if_nonmatching_regex(all_tags, regex, regex_option_name) + all_tags.reject { |tag| regex =~ tag["name"] } + end + + def filter_exact_tags(all_tags) + options[:exclude_tags].each do |tag| + warn_if_tag_not_found(all_tags, tag) + end + all_tags.reject { |tag| options[:exclude_tags].include?(tag["name"]) } + end + + def warn_if_nonmatching_regex(all_tags, regex, regex_option_name) + unless all_tags.map { |t| t["name"] }.any? { |t| regex =~ t } + Helper.log.warn "Warning: unable to reject any tag, using regex "\ + "#{regex.inspect} in #{regex_option_name} option." + end + end + + def warn_if_tag_not_found(all_tags, tag) + Helper.log.warn("Warning: can't find tag #{tag}, specified with --exclude-tags option.") unless all_tags.map { |t| t["name"] }.include?(tag) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/generator/section.rb b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/generator/section.rb new file mode 100644 index 0000000..d211c5d --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/generator/section.rb @@ -0,0 +1,124 @@ +# frozen_string_literal: true + +module GitHubChangelogGenerator + # This class generates the content for a single section of a changelog entry. + # It turns the tagged issues and PRs into a well-formatted list of changes to + # be later incorporated into a changelog entry. + # + # @see GitHubChangelogGenerator::Entry + class Section + # @return [String] + attr_accessor :name + + # @return [String] a merge prefix, or an issue prefix + attr_reader :prefix + + # @return [Array] + attr_reader :issues + + # @return [Array] + attr_reader :labels + + # @return [Boolean] + attr_reader :body_only + + # @return [Options] + attr_reader :options + + def initialize(opts = {}) + @name = opts[:name] + @prefix = opts[:prefix] + @labels = opts[:labels] || [] + @issues = opts[:issues] || [] + @options = opts[:options] || Options.new({}) + @body_only = opts[:body_only] || false + @entry = Entry.new(options) + end + + # Returns the content of a section. + # + # @return [String] Generated section content + def generate_content + content = "" + + if @issues.any? + content += "#{@prefix}\n\n" unless @options[:simple_list] || @prefix.blank? + @issues.each do |issue| + merge_string = get_string_for_issue(issue) + content += "- " unless @body_only + content += "#{merge_string}\n" + end + content += "\n" + end + content + end + + private + + # Parse issue and generate single line formatted issue line. + # + # Example output: + # - Add coveralls integration [\#223](https://github.com/github-changelog-generator/github-changelog-generator/pull/223) (@github-changelog-generator) + # + # @param [Hash] issue Fetched issue from GitHub + # @return [String] Markdown-formatted single issue + def get_string_for_issue(issue) + encapsulated_title = encapsulate_string issue["title"] + + title_with_number = "#{encapsulated_title} [\\##{issue['number']}](#{issue['html_url']})" + title_with_number = "#{title_with_number}#{@entry.line_labels_for(issue)}" if @options[:issue_line_labels].present? + line = issue_line_with_user(title_with_number, issue) + issue_line_with_body(line, issue) + end + + def issue_line_with_body(line, issue) + return issue["body"] if @body_only && issue["body"].present? + return line if !@options[:issue_line_body] || issue["body"].blank? + + # get issue body till first line break + body_paragraph = body_till_first_break(issue["body"]) + # remove spaces from beginning of the string + body_paragraph.rstrip! + # encapsulate to md + encapsulated_body = " \n#{encapsulate_string(body_paragraph)}" + + "**#{line}** #{encapsulated_body}" + end + + def body_till_first_break(body) + body.split(/\n/, 2).first + end + + def issue_line_with_user(line, issue) + return line if !@options[:author] || issue["pull_request"].nil? + + user = issue["user"] + return "#{line} ({Null user})" unless user + + if @options[:usernames_as_github_logins] + "#{line} (@#{user['login']})" + else + "#{line} ([#{user['login']}](#{user['html_url']}))" + end + end + + ENCAPSULATED_CHARACTERS = %w(< > * _ \( \) [ ] #) + + # Encapsulate characters to make Markdown look as expected. + # + # @param [String] string + # @return [String] encapsulated input string + def encapsulate_string(string) + string = string.gsub('\\', '\\\\') + + ENCAPSULATED_CHARACTERS.each do |char| + # Only replace char with escaped version if it isn't inside backticks (markdown inline code). + # This relies on each opening '`' being closed (ie an even number in total). + # A char is *outside* backticks if there is an even number of backticks following it. + string = string.gsub(%r{#{Regexp.escape(char)}(?=([^`]*`[^`]*`)*[^`]*$)}, "\\#{char}") + end + + string + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/helper.rb b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/helper.rb new file mode 100644 index 0000000..b7aec52 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/helper.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require "logger" +require "rainbow" + +module GitHubChangelogGenerator + module Helper + # @return true if the currently running program is a unit test + def self.test? + defined? SpecHelper + end + + # :nocov: + @log ||= if test? + Logger.new(nil) # don't show any logs when running tests + else + Logger.new($stdout) + end + @log.formatter = proc do |severity, _datetime, _progname, msg| + string = "#{msg}\n" + case severity + when "DEBUG" then Rainbow(string).magenta + when "INFO" then Rainbow(string).white + when "WARN" then Rainbow(string).yellow + when "ERROR" then Rainbow(string).red + when "FATAL" then Rainbow(string).red.bright + else string + end + end + # :nocov: + + # Logging happens using this method + class << self + attr_reader :log + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/octo_fetcher.rb b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/octo_fetcher.rb new file mode 100644 index 0000000..4a1b25c --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/octo_fetcher.rb @@ -0,0 +1,529 @@ +# frozen_string_literal: true + +require "tmpdir" +require "set" +require "async" +require "async/barrier" +require "async/semaphore" +require "async/http/faraday" + +module GitHubChangelogGenerator + # A Fetcher responsible for all requests to GitHub and all basic manipulation with related data + # (such as filtering, validating, e.t.c) + # + # Example: + # fetcher = GitHubChangelogGenerator::OctoFetcher.new(options) + class OctoFetcher + PER_PAGE_NUMBER = 100 + MAXIMUM_CONNECTIONS = 50 + MAX_FORBIDDEN_RETRIES = 100 + CHANGELOG_GITHUB_TOKEN = "CHANGELOG_GITHUB_TOKEN" + GH_RATE_LIMIT_EXCEEDED_MSG = "Warning: Can't finish operation: GitHub API rate limit exceeded, changelog may be " \ + "missing some issues. You can limit the number of issues fetched using the `--max-issues NUM` argument." + NO_TOKEN_PROVIDED = "Warning: No token provided (-t option) and variable $CHANGELOG_GITHUB_TOKEN was not found. " \ + "This script can make only 50 requests to GitHub API per hour without a token!" + + # @param options [Hash] Options passed in + # @option options [String] :user GitHub username + # @option options [String] :project GitHub project + # @option options [String] :since Only issues updated at or after this time are returned. This is a timestamp in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ. eg. Time.parse("2016-01-01 10:00:00").iso8601 + # @option options [Boolean] :http_cache Use ActiveSupport::Cache::FileStore to cache http requests + # @option options [Boolean] :cache_file If using http_cache, this is the cache file path + # @option options [Boolean] :cache_log If using http_cache, this is the cache log file path + def initialize(options = {}) + @options = options || {} + @user = @options[:user] + @project = @options[:project] + @since = @options[:since] + @http_cache = @options[:http_cache] + @commits = [] + @branches = nil + @graph = nil + @client = nil + @commits_in_tag_cache = {} + end + + def middleware + Faraday::RackBuilder.new do |builder| + if @http_cache + cache_file = @options.fetch(:cache_file) { File.join(Dir.tmpdir, "github-changelog-http-cache") } + cache_log = @options.fetch(:cache_log) { File.join(Dir.tmpdir, "github-changelog-logger.log") } + + builder.use( + Faraday::HttpCache, + serializer: Marshal, + store: ActiveSupport::Cache::FileStore.new(cache_file), + logger: Logger.new(cache_log), + shared_cache: false + ) + end + + builder.use Octokit::Response::RaiseError + builder.adapter :async_http + end + end + + def connection_options + ca_file = @options[:ssl_ca_file] || ENV["SSL_CA_FILE"] || File.expand_path("ssl_certs/cacert.pem", __dir__) + + Octokit.connection_options.merge({ ssl: { ca_file: ca_file } }) + end + + def client_options + options = { + middleware: middleware, + connection_options: connection_options + } + + if (github_token = fetch_github_token) + options[:access_token] = github_token + end + + if (endpoint = @options[:github_endpoint]) + options[:api_endpoint] = endpoint + end + + options + end + + def client + @client ||= Octokit::Client.new(client_options) + end + + DEFAULT_REQUEST_OPTIONS = { per_page: PER_PAGE_NUMBER } + + # Fetch all tags from repo + # + # @return [Array ] array of tags + def get_all_tags + print "Fetching tags...\r" if @options[:verbose] + + check_github_response { github_fetch_tags } + end + + # Returns the number of pages for a API call + # + # @return [Integer] number of pages for this API call in total + # @param [Object] request_options + # @param [Object] method + # @param [Object] client + def calculate_pages(client, method, request_options) + # Makes the first API call so that we can call last_response + check_github_response do + client.send(method, user_project, DEFAULT_REQUEST_OPTIONS.merge(request_options)) + end + + last_response = client.last_response + + if (last_pg = last_response.rels[:last]) + querystring_as_hash(last_pg.href)["page"].to_i + else + 1 + end + end + + # Fill input array with tags + # + # @return [Array ] array of tags in repo + def github_fetch_tags + tags = [] + page_i = 0 + count_pages = calculate_pages(client, "tags", {}) + + iterate_pages(client, "tags") do |new_tags| + page_i += PER_PAGE_NUMBER + print_in_same_line("Fetching tags... #{page_i}/#{count_pages * PER_PAGE_NUMBER}") + tags.concat(new_tags) + end + print_empty_line + + if tags.count == 0 + Helper.log.warn "Warning: Can't find any tags in repo. \ +Make sure, that you push tags to remote repo via 'git push --tags'" + else + Helper.log.info "Found #{tags.count} tags" + end + # tags are a Sawyer::Resource. Convert to hash + tags.map { |resource| stringify_keys_deep(resource.to_hash) } + end + + def closed_pr_options + @closed_pr_options ||= { + filter: "all", labels: nil, state: "closed" + }.tap { |options| options[:since] = @since if @since } + end + + # This method fetch all closed issues and separate them to pull requests and pure issues + # (pull request is kind of issue in term of GitHub) + # + # @return [Tuple] with (issues [Array ], pull-requests [Array ]) + def fetch_closed_issues_and_pr + print "Fetching closed issues...\r" if @options[:verbose] + issues = [] + page_i = 0 + count_pages = calculate_pages(client, "issues", closed_pr_options) + + iterate_pages(client, "issues", **closed_pr_options) do |new_issues| + page_i += PER_PAGE_NUMBER + print_in_same_line("Fetching issues... #{page_i}/#{count_pages * PER_PAGE_NUMBER}") + issues.concat(new_issues) + break if @options[:max_issues] && issues.length >= @options[:max_issues] + end + print_empty_line + Helper.log.info "Received issues: #{issues.count}" + + # separate arrays of issues and pull requests: + issues.map { |issue| stringify_keys_deep(issue.to_hash) } + .partition { |issue_or_pr| issue_or_pr["pull_request"].nil? } + end + + # Fetch all pull requests. We need them to detect :merged_at parameter + # + # @return [Array ] all pull requests + def fetch_closed_pull_requests + pull_requests = [] + options = { state: "closed" } + + page_i = 0 + count_pages = calculate_pages(client, "pull_requests", options) + + iterate_pages(client, "pull_requests", **options) do |new_pr| + page_i += PER_PAGE_NUMBER + log_string = "Fetching merged dates... #{page_i}/#{count_pages * PER_PAGE_NUMBER}" + print_in_same_line(log_string) + pull_requests.concat(new_pr) + end + print_empty_line + + Helper.log.info "Pull Request count: #{pull_requests.count}" + pull_requests.map { |pull_request| stringify_keys_deep(pull_request.to_hash) } + end + + # Fetch event for all issues and add them to 'events' + # + # @param [Array] issues + # @return [Void] + def fetch_events_async(issues) + i = 0 + # Add accept option explicitly for disabling the warning of preview API. + preview = { accept: Octokit::Preview::PREVIEW_TYPES[:project_card_events] } + + barrier = Async::Barrier.new + semaphore = Async::Semaphore.new(MAXIMUM_CONNECTIONS, parent: barrier) + + Sync do + client = self.client + + issues.each do |issue| + semaphore.async do + issue["events"] = [] + iterate_pages(client, "issue_events", issue["number"], **preview) do |new_event| + issue["events"].concat(new_event) + end + issue["events"] = issue["events"].map { |event| stringify_keys_deep(event.to_hash) } + print_in_same_line("Fetching events for issues and PR: #{i + 1}/#{issues.count}") + i += 1 + end + end + + barrier.wait + + # to clear line from prev print + print_empty_line + end + + Helper.log.info "Fetching events for issues and PR: #{i}" + end + + # Fetch comments for PRs and add them to "comments" + # + # @param [Array] prs The array of PRs. + # @return [Void] No return; PRs are updated in-place. + def fetch_comments_async(prs) + barrier = Async::Barrier.new + semaphore = Async::Semaphore.new(MAXIMUM_CONNECTIONS, parent: barrier) + + Sync do + client = self.client + + prs.each do |pr| + semaphore.async do + pr["comments"] = [] + iterate_pages(client, "issue_comments", pr["number"]) do |new_comment| + pr["comments"].concat(new_comment) + end + pr["comments"] = pr["comments"].map { |comment| stringify_keys_deep(comment.to_hash) } + end + end + + barrier.wait + end + + nil + end + + # Fetch tag time from repo + # + # @param [Hash] tag GitHub data item about a Tag + # + # @return [Time] time of specified tag + def fetch_date_of_tag(tag) + commit_data = fetch_commit(tag["commit"]["sha"]) + commit_data = stringify_keys_deep(commit_data.to_hash) + + commit_data["commit"]["committer"]["date"] + end + + # Fetch commit for specified event + # + # @param [String] commit_id the SHA of a commit to fetch + # @return [Hash] + def fetch_commit(commit_id) + found = commits.find do |commit| + commit["sha"] == commit_id + end + if found + stringify_keys_deep(found.to_hash) + else + client = self.client + + # cache miss; don't add to @commits because unsure of order. + check_github_response do + commit = client.commit(user_project, commit_id) + commit = stringify_keys_deep(commit.to_hash) + commit + end + end + end + + # Fetch all commits + # + # @return [Array] Commits in a repo. + def commits + if @commits.empty? + Sync do + barrier = Async::Barrier.new + semaphore = Async::Semaphore.new(MAXIMUM_CONNECTIONS, parent: barrier) + + if (since_commit = @options[:since_commit]) + iterate_pages(client, "commits_since", since_commit, parent: semaphore) do |new_commits| + @commits.concat(new_commits) + end + else + iterate_pages(client, "commits", parent: semaphore) do |new_commits| + @commits.concat(new_commits) + end + end + + barrier.wait + + @commits.sort! do |b, a| + a[:commit][:author][:date] <=> b[:commit][:author][:date] + end + end + end + @commits + end + + # Return the oldest commit in a repo + # + # @return [Hash] Oldest commit in the github git history. + def oldest_commit + commits.last + end + + # @return [String] Default branch of the repo + def default_branch + @default_branch ||= client.repository(user_project)[:default_branch] + end + + # @param [String] name + # @return [Array] + def commits_in_branch(name) + @branches ||= client.branches(user_project).map { |branch| [branch[:name], branch] }.to_h + + if (branch = @branches[name]) + commits_in_tag(branch[:commit][:sha]) + else + [] + end + end + + # Fetch all SHAs occurring in or before a given tag and add them to + # "shas_in_tag" + # + # @param [Array] tags The array of tags. + # @return void + def fetch_tag_shas(tags) + # Reverse the tags array to gain max benefit from the @commits_in_tag_cache + tags.reverse_each do |tag| + tag["shas_in_tag"] = commits_in_tag(tag["commit"]["sha"]) + end + end + + private + + # @param [Set] shas + # @param [Object] sha + def commits_in_tag(sha, shas = Set.new) + # Reduce multiple runs for the same tag + return @commits_in_tag_cache[sha] if @commits_in_tag_cache.key?(sha) + + @graph ||= commits.map { |commit| [commit[:sha], commit] }.to_h + return shas unless (current = @graph[sha]) + + queue = [current] + while queue.any? + commit = queue.shift + # If we've already processed this sha, just grab it's parents from the cache + if @commits_in_tag_cache.key?(commit[:sha]) + shas.merge(@commits_in_tag_cache[commit[:sha]]) + else + shas.add(commit[:sha]) + commit[:parents].each do |p| + queue.push(@graph[p[:sha]]) unless shas.include?(p[:sha]) + end + end + end + + @commits_in_tag_cache[sha] = shas + shas + end + + # @param [Object] indata + def stringify_keys_deep(indata) + case indata + when Array + indata.map do |value| + stringify_keys_deep(value) + end + when Hash + indata.each_with_object({}) do |(key, value), output| + output[key.to_s] = stringify_keys_deep(value) + end + else + indata + end + end + + # Exception raised to warn about moved repositories. + MovedPermanentlyError = Class.new(RuntimeError) + + # Iterates through all pages until there are no more :next pages to follow + # yields the result per page + # + # @param [Octokit::Client] client + # @param [String] method (eg. 'tags') + # @param [Array] arguments + # @param [Async::Semaphore] parent + # + # @yield [Sawyer::Resource] An OctoKit-provided response (which can be empty) + # + # @return [void] + # @param [Hash] options + def iterate_pages(client, method, *arguments, parent: nil, **options) + options = DEFAULT_REQUEST_OPTIONS.merge(options) + + check_github_response { client.send(method, user_project, *arguments, **options) } + last_response = client.last_response.tap do |response| + raise(MovedPermanentlyError, response.data[:url]) if response.status == 301 + end + + yield(last_response.data) + + if parent.nil? + # The snail visits one leaf at a time: + until (next_one = last_response.rels[:next]).nil? + last_response = check_github_response { next_one.get } + yield(last_response.data) + end + elsif (last = last_response.rels[:last]) + # OR we bring out the gatling gun: + parameters = querystring_as_hash(last.href) + last_page = Integer(parameters["page"]) + + (2..last_page).each do |page| + parent.async do + data = check_github_response { client.send(method, user_project, *arguments, page: page, **options) } + yield data + end + end + end + end + + # This is wrapper with rescue block + # + # @return [Object] returns exactly the same, what you put in the block, but wrap it with begin-rescue block + # @param [Proc] block + def check_github_response + yield + rescue MovedPermanentlyError => e + fail_with_message(e, "The repository has moved, update your configuration") + rescue Octokit::TooManyRequests => e + resets_in = client.rate_limit.resets_in + Helper.log.error("#{e.class} #{e.message}; sleeping for #{resets_in}s...") + + if (task = Async::Task.current?) + task.sleep(resets_in) + else + sleep(resets_in) + end + + retry + rescue Octokit::Forbidden => e + fail_with_message(e, "Exceeded retry limit") + rescue Octokit::Unauthorized => e + fail_with_message(e, "Error: wrong GitHub token") + end + + # Presents the exception, and the aborts with the message. + # @param [Object] message + # @param [Object] error + def fail_with_message(error, message) + Helper.log.error("#{error.class}: #{error.message}") + sys_abort(message) + end + + # @param [Object] msg + def sys_abort(msg) + abort(msg) + end + + # Print specified line on the same string + # + # @param [String] log_string + def print_in_same_line(log_string) + print "#{log_string}\r" + end + + # Print long line with spaces on same line to clear prev message + def print_empty_line + print_in_same_line(" ") + end + + # Returns GitHub token. First try to use variable, provided by --token option, + # otherwise try to fetch it from CHANGELOG_GITHUB_TOKEN env variable. + # + # @return [String] + def fetch_github_token + env_var = @options[:token].presence || ENV["CHANGELOG_GITHUB_TOKEN"] + + Helper.log.warn NO_TOKEN_PROVIDED unless env_var + + env_var + end + + # @return [String] helper to return Github "user/project" + def user_project + "#{@options[:user]}/#{@options[:project]}" + end + + # Returns Hash of all querystring variables in given URI. + # + # @param [String] uri eg. https://api.github.com/repositories/43914960/tags?page=37&foo=1 + # @return [Hash] of all GET variables. eg. { 'page' => 37, 'foo' => 1 } + def querystring_as_hash(uri) + Hash[URI.decode_www_form(URI(uri).query || "")] + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/options.rb b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/options.rb new file mode 100644 index 0000000..b77503f --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/options.rb @@ -0,0 +1,159 @@ +# frozen_string_literal: true + +require "delegate" +require "github_changelog_generator/helper" + +module GitHubChangelogGenerator + # This class wraps Options, and knows a list of known options. Others options + # will raise exceptions. + class Options < SimpleDelegator + # Raised on initializing with unknown keys in the values hash, + # and when trying to store a value on an unknown key. + UnsupportedOptionError = Class.new(ArgumentError) + + # List of valid option names + KNOWN_OPTIONS = %i[ + add_issues_wo_labels + add_pr_wo_labels + add_sections + author + base + between_tags + breaking_labels + breaking_prefix + bug_labels + bug_prefix + cache_file + cache_log + config_file + compare_link + configure_sections + date_format + deprecated_labels + deprecated_prefix + due_tag + enhancement_labels + enhancement_prefix + exclude_labels + exclude_tags + exclude_tags_regex + filter_issues_by_milestone + issues_of_open_milestones + frontmatter + future_release + github_endpoint + github_site + header + http_cache + include_labels + include_tags_regex + issue_prefix + issue_line_labels + issue_line_body + issues + max_issues + merge_prefix + output + project + pulls + release_branch + release_url + removed_labels + removed_prefix + require + security_labels + security_prefix + simple_list + since_tag + since_commit + ssl_ca_file + summary_labels + summary_prefix + token + unreleased + unreleased_label + unreleased_only + user + usernames_as_github_logins + verbose + ] + + # @param values [Hash] + # + # @raise [UnsupportedOptionError] if given values contain unknown options + def initialize(values) + super(values) + unsupported_options.any? && raise(UnsupportedOptionError, unsupported_options.inspect) + end + + # Set option key to val. + # + # @param key [Symbol] + # @param val [Object] + # + # @raise [UnsupportedOptionError] when trying to set an unknown option + def []=(key, val) + supported_option?(key) || raise(UnsupportedOptionError, key.inspect) + values[key] = val + end + + # @return [Hash] + def to_hash + values + end + + # Loads the configured Ruby files from the --require option. + def load_custom_ruby_files + self[:require].each { |f| require f } + end + + # Pretty-prints a censored options hash, if :verbose. + def print_options + return unless self[:verbose] + + Helper.log.info "Using these options:" + # For ruby 2.5.0+ + censored_values.each do |key, value| + print(key.inspect, "=>", value.inspect) + puts "" + end + puts "" + end + + # Boolean method for whether the user is using configure_sections + def configure_sections? + !self[:configure_sections].nil? && !self[:configure_sections].empty? + end + + # Boolean method for whether the user is using add_sections + def add_sections? + !self[:add_sections].nil? && !self[:add_sections].empty? + end + + # @return [Boolean] whether write to `:output` + def write_to_file? + self[:output].present? + end + + private + + def values + __getobj__ + end + + # Returns a censored options hash. + # + # @return [Hash] The GitHub `:token` key is censored in the output. + def censored_values + values.clone.tap { |opts| opts[:token] = opts[:token].nil? ? "No token used" : "hidden value" } + end + + def unsupported_options + values.keys - KNOWN_OPTIONS + end + + def supported_option?(key) + KNOWN_OPTIONS.include?(key) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/parser.rb b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/parser.rb new file mode 100755 index 0000000..3cc5065 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/parser.rb @@ -0,0 +1,89 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "github_changelog_generator/helper" +require "github_changelog_generator/argv_parser" +require "github_changelog_generator/parser_file" + +module GitHubChangelogGenerator + class Parser + class << self + PARSERS = [ + ArgvParser, # Parse arguments first to get initial options populated + FileParserChooser, # Then parse possible configuration files + ArgvParser # Lastly parse arguments again to keep the given arguments the strongest + ].freeze + + def parse_options(argv = ARGV) + options = default_options + + PARSERS.each do |parser| + parser.new(options).parse!(argv) + end + + abort_if_user_and_project_not_given!(options) + + options.print_options + + options + end + + def abort_if_user_and_project_not_given!(options) + return if options[:user] && options[:project] + + warn "Configure which user and project to work on." + warn "Options --user and --project, or settings to that effect. See --help for more." + warn ArgvParser.banner + + Kernel.abort + end + + # @return [Options] Default options + def default_options + Options.new( + date_format: "%Y-%m-%d", + output: "CHANGELOG.md", + base: "HISTORY.md", + issues: true, + add_issues_wo_labels: true, + add_pr_wo_labels: true, + pulls: true, + filter_issues_by_milestone: true, + issues_of_open_milestones: true, + author: true, + unreleased: true, + unreleased_label: "Unreleased", + compare_link: true, + exclude_labels: ["duplicate", "question", "invalid", "wontfix", "Duplicate", "Question", "Invalid", "Wontfix", "Meta: Exclude From Changelog"], + summary_labels: ["Release summary", "release-summary", "Summary", "summary"], + breaking_labels: ["backwards-incompatible", "Backwards incompatible", "breaking"], + enhancement_labels: ["enhancement", "Enhancement", "Type: Enhancement"], + bug_labels: ["bug", "Bug", "Type: Bug"], + deprecated_labels: ["deprecated", "Deprecated", "Type: Deprecated"], + removed_labels: ["removed", "Removed", "Type: Removed"], + security_labels: ["security", "Security", "Type: Security"], + configure_sections: {}, + add_sections: {}, + issue_line_labels: [], + max_issues: nil, + simple_list: false, + ssl_ca_file: nil, + verbose: true, + header: "# Changelog", + merge_prefix: "**Merged pull requests:**", + issue_prefix: "**Closed issues:**", + summary_prefix: "", + breaking_prefix: "**Breaking changes:**", + enhancement_prefix: "**Implemented enhancements:**", + bug_prefix: "**Fixed bugs:**", + deprecated_prefix: "**Deprecated:**", + removed_prefix: "**Removed:**", + security_prefix: "**Security fixes:**", + http_cache: true, + require: [], + config_file: ".github_changelog_generator" + ) + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/parser_file.rb b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/parser_file.rb new file mode 100644 index 0000000..adcb324 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/parser_file.rb @@ -0,0 +1,125 @@ +# frozen_string_literal: true + +require "pathname" + +module GitHubChangelogGenerator + ParserError = Class.new(StandardError) + + class FileParserChooser + def initialize(options) + @options = options + @config_file = Pathname.new(options[:config_file]) + end + + def parse!(_argv) + return nil unless (path = resolve_path) + + ParserFile.new(@options, File.open(path)).parse! + end + + def resolve_path + return @config_file if @config_file.exist? + + path = @config_file.expand_path + return path if File.exist?(path) + + nil + end + end + + # ParserFile is a configuration file reader which sets options in the + # given Hash. + # + # In your project's root, you can put a file named + # .github_changelog_generator to override defaults. + # + # Example: + # header_label=# My Super Changelog + # ; Comments are allowed + # future-release=5.0.0 + # # Ruby-style comments, too + # since-tag=1.0.0 + # + # The configuration format is some-key=value or some_key=value. + # + class ParserFile + # @param options [Hash] options to be configured from file contents + # @param io [nil, IO] configuration file handle + def initialize(options, io = nil) + @options = options + @io = io + end + + # Sets options using configuration file content + def parse! + return unless @io + + @io.each_with_index { |line, i| parse_line!(line, i + 1) } + @io.close + end + + private + + def parse_line!(line, line_number) + return if non_configuration_line?(line) + + option_name, value = extract_pair(line) + @options[option_key_for(option_name)] = convert_value(value, option_name) + rescue StandardError + raise ParserError, "Failed on line ##{line_number}: \"#{line.gsub(/[\n\r]+/, '')}\"" + end + + # Returns true if the line starts with a pound sign or a semi-colon. + def non_configuration_line?(line) + line =~ /^[\#;]/ || line =~ /^\s+$/ + end + + # Returns a the option name as a symbol and its string value sans newlines. + # + # @param line [String] unparsed line from config file + # @return [Array] + def extract_pair(line) + key, value = line.split("=", 2) + [key.tr("-", "_").to_sym, value.gsub(/[\n\r]+/, "")] + end + + KNOWN_ARRAY_KEYS = %i[exclude_labels include_labels + summary_labels breaking_labels enhancement_labels bug_labels + deprecated_labels removed_labels security_labels + issue_line_labels between_tags exclude_tags] + KNOWN_INTEGER_KEYS = [:max_issues] + + def convert_value(value, option_name) + if KNOWN_ARRAY_KEYS.include?(option_name) + value.split(",") + elsif KNOWN_INTEGER_KEYS.include?(option_name) + value.to_i + elsif value =~ /^(true|t|yes|y|1)$/i + true + elsif value =~ /^(false|f|no|n|0)$/i + false + else + value + end + end + + IRREGULAR_OPTIONS = { + bugs_label: :bug_prefix, + enhancement_label: :enhancement_prefix, + issues_label: :issue_prefix, + header_label: :header, + front_matter: :frontmatter, + pr_label: :merge_prefix, + breaking_label: :breaking_prefix, + issues_wo_labels: :add_issues_wo_labels, + pr_wo_labels: :add_pr_wo_labels, + pull_requests: :pulls, + filter_by_milestone: :filter_issues_by_milestone, + github_api: :github_endpoint + } + + def option_key_for(option_name) + IRREGULAR_OPTIONS.fetch(option_name) { option_name } + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/reader.rb b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/reader.rb new file mode 100644 index 0000000..f482763 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/reader.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +# +# Author:: Enrico Stahn +# +# Copyright 2014, Zanui, +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module GitHubChangelogGenerator + # A Reader to read an existing ChangeLog file and return a structured object + # + # Example: + # reader = GitHubChangelogGenerator::Reader.new + # content = reader.read('./CHANGELOG.md') + class Reader + def initialize(options = {}) + defaults = { + heading_level: "##", + heading_structures: [ + /^## \[(?.+?)\](\((?.+?)\))?( \((?.+?)\))?$/, # rubocop:disable Lint/MixedRegexpCaptureTypes + /^## (?.+?)( \((?.+?)\))?$/ # rubocop:disable Lint/MixedRegexpCaptureTypes + ] + } + + @options = options.merge(defaults) + + @heading_level = @options[:heading_level] + @heading_structures = @options[:heading_structures] + end + + # Parse a single heading and return a Hash + # + # The following heading structures are currently valid: + # - ## [v1.0.2](https://github.com/zanui/chef-thumbor/tree/v1.0.1) (2015-03-24) + # - ## [v1.0.2](https://github.com/zanui/chef-thumbor/tree/v1.0.1) + # - ## [v1.0.2] (2015-03-24) + # - ## [v1.0.2] + # - ## v1.0.2 (2015-03-24) + # - ## v1.0.2 + # + # @param [String] heading Heading from the ChangeLog File + # @return [Hash] Returns a structured Hash with version, url and date + def parse_heading(heading) + captures = { "version" => nil, "url" => nil, "date" => nil } + + @heading_structures.each do |regexp| + matches = Regexp.new(regexp).match(heading) + if matches + captures.merge!(Hash[matches.names.zip(matches.captures)]) + break + end + end + + captures + end + + # Parse the given ChangeLog data into a list of Hashes + # + # @param [String] data File data from the ChangeLog.md + # @return [Array] Parsed data, e.g. [{ 'version' => ..., 'url' => ..., 'date' => ..., 'content' => ...}, ...] + def parse(data) + sections = data.split(/^## .+?$/) + headings = data.scan(/^## .+?$/) + + headings.each_with_index.map do |heading, index| + section = parse_heading(heading) + section["content"] = sections.at(index + 1) + section + end + end + + def read(file_path) + parse File.read(file_path) + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/ssl_certs/cacert.pem b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/ssl_certs/cacert.pem new file mode 100644 index 0000000..264923b --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/ssl_certs/cacert.pem @@ -0,0 +1,3138 @@ +## +## Bundle of CA Root Certificates +## +## Certificate data from Mozilla as of: Tue May 25 03:12:05 2021 GMT +## +## This is a bundle of X.509 certificates of public Certificate Authorities +## (CA). These were automatically extracted from Mozilla's root certificates +## file (certdata.txt). This file can be found in the mozilla source tree: +## https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt +## +## It contains the certificates in PEM format and therefore +## can be directly used with curl / libcurl / php_curl, or with +## an Apache+mod_ssl webserver for SSL client authentication. +## Just configure this file as the SSLCACertificateFile. +## +## Conversion done with mk-ca-bundle.pl version 1.28. +## SHA256: e292bd4e2d500c86df45b830d89417be5c42ee670408f1d2c454c63d8a782865 +## + + +GlobalSign Root CA +================== +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx +GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds +b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV +BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD +VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa +DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc +THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb +Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP +c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX +gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF +AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj +Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG +j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH +hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC +X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +GlobalSign Root CA - R2 +======================= +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6 +ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp +s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN +S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL +TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C +ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i +YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN +BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp +9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu +01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7 +9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- + +Entrust.net Premium 2048 Secure Server CA +========================================= +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u +ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp +bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV +BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx +NzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3 +d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl +MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u +ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL +Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr +hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW +nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi +VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ +KoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy +T/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT +J1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e +nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +Baltimore CyberTrust Root +========================= +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE +ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li +ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC +SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs +dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME +uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB +UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C +G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9 +XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr +l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI +VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB +BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh +cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5 +hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa +Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H +RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +Entrust Root Certification Authority +==================================== +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV +BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw +b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG +A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0 +MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu +MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu +Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v +dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz +A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww +Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68 +j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN +rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1 +MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH +hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM +Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa +v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS +W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0 +tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +Comodo AAA Services root +======================== +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw +MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl +c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV +BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG +C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs +i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW +Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH +Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK +Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f +BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl +cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz +LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm +7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z +8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C +12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +QuoVadis Root CA +================ +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE +ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz +MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp +cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD +EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk +J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL +F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL +YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen +AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w +PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y +ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7 +MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj +YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs +ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW +Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu +BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw +FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6 +tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo +fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul +LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x +gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi +5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi +5nrQNiOKSnQ2+Q== +-----END CERTIFICATE----- + +QuoVadis Root CA 2 +================== +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT +EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx +ODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6 +XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk +lvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB +lDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy +lZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt +66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn +wQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh +D7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy +BNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie +J0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud +DgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU +a6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv +Z+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3 +UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm +VjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK ++JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW +IozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1 +WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X +f6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II +4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8 +VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +QuoVadis Root CA 3 +================== +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT +EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx +OTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg +DhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij +KTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K +DDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv +BNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp +p5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8 +nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX +MJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM +Gf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz +uD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT +BgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj +YXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB +BQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD +VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4 +ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE +AxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV +qyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s +hvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z +POuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2 +Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp +8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC +bjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu +g/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p +vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr +qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +Security Communication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw +8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM +DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX +5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd +DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2 +JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g +0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a +mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ +s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ +6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi +FL39vmwLAw== +-----END CERTIFICATE----- + +Sonera Class 2 Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG +U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw +NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh +IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3 +/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT +dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG +f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P +tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH +nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT +XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt +0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI +cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph +Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx +EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH +llpwrN9M +-----END CERTIFICATE----- + +XRamp Global CA Root +==================== +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE +BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj +dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx +HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg +U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu +IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx +foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE +zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs +AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry +xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap +oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC +AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc +/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n +nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz +8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +Go Daddy Class 2 CA +=================== +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY +VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG +A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g +RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD +ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv +2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32 +qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j +YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY +vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O +BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o +atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu +MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim +PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt +I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI +Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b +vZ8= +-----END CERTIFICATE----- + +Starfield Class 2 CA +==================== +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc +U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo +MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG +A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG +SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY +bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ +JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm +epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN +F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF +MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f +hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo +bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g +QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs +afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM +PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD +KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3 +QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +DigiCert Assured ID Root CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw +IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx +MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO +9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy +UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW +/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy +oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf +GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF +66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq +hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc +EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn +SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i +8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +DigiCert Global Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw +MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn +TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5 +BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H +4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y +7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB +o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm +8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF +BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr +EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt +tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886 +UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +DigiCert High Assurance EV Root CA +================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw +KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw +MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ +MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu +Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t +Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS +OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3 +MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ +NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe +h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY +JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ +V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp +myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK +mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K +-----END CERTIFICATE----- + +DST Root CA X3 +============== +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK +ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X +DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1 +cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT +rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9 +UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy +xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d +utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ +MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug +dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE +GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw +RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS +fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- + +SwissSign Gold CA - G2 +====================== +-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw +EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN +MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp +c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq +t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C +jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg +vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF +ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR +AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend +jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO +peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR +7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi +GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64 +OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm +5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr +44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf +Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m +Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp +mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk +vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf +KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br +NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj +viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE----- + +SwissSign Silver CA - G2 +======================== +-----BEGIN CERTIFICATE----- +MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ0gxFTAT +BgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMB4X +DTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0NlowRzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3 +aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG +9w0BAQEFAAOCAg8AMIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644 +N0MvFz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7brYT7QbNHm ++/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieFnbAVlDLaYQ1HTWBCrpJH +6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH6ATK72oxh9TAtvmUcXtnZLi2kUpCe2Uu +MGoM9ZDulebyzYLs2aFK7PayS+VFheZteJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5h +qAaEuSh6XzjZG6k4sIN/c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5 +FZGkECwJMoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRHHTBs +ROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTfjNFusB3hB48IHpmc +celM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb65i/4z3GcRm25xBWNOHkDRUjvxF3X +CO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUF6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRB +tjpbO8tFnb0cwpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 +cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBAHPGgeAn0i0P +4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShpWJHckRE1qTodvBqlYJ7YH39F +kWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L +3XWgwF15kIwb4FDm3jH+mHtwX6WQ2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx +/uNncqCxv1yL5PqZIseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFa +DGi8aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2Xem1ZqSqP +e97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQRdAtq/gsD/KNVV4n+Ssuu +WxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJ +DIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ub +DgEj8Z+7fNzcbBGXJbLytGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u +-----END CERTIFICATE----- + +SecureTrust CA +============== +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy +dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe +BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX +OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t +DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH +GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b +01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH +ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj +aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu +SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf +mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ +nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +Secure Global CA +================ +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH +bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg +MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg +Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx +YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ +bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g +8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV +HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi +0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn +oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA +MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+ +OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn +CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5 +3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +COMODO Certification Authority +============================== +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE +BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG +A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb +MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD +T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH ++7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww +xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV +4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA +1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI +rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k +b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC +AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP +OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc +IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN ++8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ== +-----END CERTIFICATE----- + +Network Solutions Certificate Authority +======================================= +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG +EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr +IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx +MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx +jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT +aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT +crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc +/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB +AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv +bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA +A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q +4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/ +GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD +ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- + +COMODO ECC Certification Authority +================================== +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC +R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE +ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix +GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X +4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni +wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG +FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA +U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +Certigna +======== +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw +EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3 +MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI +Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q +XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH +GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p +ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg +DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf +Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ +tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ +BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J +SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA +hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+ +ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu +PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY +1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +Cybertrust Global Root +====================== +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li +ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4 +MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD +ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA ++Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW +0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL +AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin +89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT +8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2 +MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G +A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO +lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi +5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2 +hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T +X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW +WL1WMRJOEcgh4LMRkWXbtKaIOM5V +-----END CERTIFICATE----- + +ePKI Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG +EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx +MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq +MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs +IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi +lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv +qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX +12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O +WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+ +ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao +lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/ +vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi +Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi +MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0 +1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq +KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV +xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP +NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r +GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE +xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx +gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy +sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD +BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +certSIGN ROOT CA +================ +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD +VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa +Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE +CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I +JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH +rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2 +ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD +0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943 +AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B +Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB +AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8 +SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0 +x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt +vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz +TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +NetLock Arany (Class Gold) Főtanúsítvány +======================================== +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G +A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610 +dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB +cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx +MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO +ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6 +c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu +0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw +/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk +H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw +fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1 +neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW +qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta +YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna +NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu +dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +Hongkong Post Root CA 1 +======================= +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT +DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx +NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n +IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1 +ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr +auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh +qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY +V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV +HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i +h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio +l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei +IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps +T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT +c4afU9hDDl3WY4JxHYB0yvbiAmvZWg== +-----END CERTIFICATE----- + +SecureSign RootCA11 +=================== +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi +SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS +b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw +KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1 +cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL +TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO +wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq +g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP +O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA +bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX +t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh +OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r +bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ +Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01 +y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061 +lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +Microsec e-Szigno Root CA 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER +MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv +c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE +BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt +U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA +fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG +0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA +pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm +1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC +AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf +QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE +FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o +lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX +I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02 +yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi +LXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +GlobalSign Root CA - R3 +======================= +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt +iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ +0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3 +rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl +OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2 +xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7 +lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8 +EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E +bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18 +YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r +kpeDMdmztcpHWD9f +-----END CERTIFICATE----- + +Autoridad de Certificacion Firmaprofesional CIF A62634068 +========================================================= +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA +BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 +MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw +QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB +NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD +Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P +B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY +7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH +ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI +plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX +MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX +LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK +bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU +vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud +EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH +DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA +bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx +ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx +51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk +R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP +T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f +Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl +osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR +crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR +saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD +KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi +6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +Izenpe.com +========== +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQG +EwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wHhcNMDcxMjEz +MTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMu +QS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ +03rKDx6sp4boFmVqscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAK +ClaOxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6HLmYRY2xU ++zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFXuaOKmMPsOzTFlUFpfnXC +PCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQDyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxT +OTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbK +F7jJeodWLBoBHmy+E60QrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK +0GqfvEyNBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8Lhij+ +0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIBQFqNeb+Lz0vPqhbB +leStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+HMh3/1uaD7euBUbl8agW7EekFwID +AQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2luZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+ +SVpFTlBFIFMuQS4gLSBDSUYgQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBG +NjIgUzgxQzBBBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O +BBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUAA4ICAQB4pgwWSp9MiDrAyw6l +Fn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWblaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbga +kEyrkgPH7UIBzg/YsfqikuFgba56awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8q +hT/AQKM6WfxZSzwoJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Cs +g1lwLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCTVyvehQP5 +aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGkLhObNA5me0mrZJfQRsN5 +nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJbUjWumDqtujWTI6cfSN01RpiyEGjkpTHC +ClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZo +Q0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z +WrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +Go Daddy Root Certificate Authority - G2 +======================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu +MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G +A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq +9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD ++qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd +fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl +NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9 +BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac +vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r +5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV +N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1 +-----END CERTIFICATE----- + +Starfield Root Certificate Authority - G2 +========================================= +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 +eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw +DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg +VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB +dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv +W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs +bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk +N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf +ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU +JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol +TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx +4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw +F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ +c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +Starfield Services Root Certificate Authority - G2 +================================================== +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl +IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT +dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2 +h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa +hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP +LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB +rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG +SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP +E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy +xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza +YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6 +-----END CERTIFICATE----- + +AffirmTrust Commercial +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw +MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb +DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV +C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6 +BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww +MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV +HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG +hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi +qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv +0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh +sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +AffirmTrust Networking +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw +MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE +Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI +dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24 +/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb +h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV +HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu +UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6 +12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23 +WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9 +/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +AffirmTrust Premium +=================== +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy +OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy +dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn +BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV +5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs ++7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd +GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R +p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI +S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04 +6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5 +/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo ++Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv +MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC +6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S +L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK ++4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV +BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg +IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60 +g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb +zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw== +-----END CERTIFICATE----- + +AffirmTrust Premium ECC +======================= +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV +BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx +MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U +cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ +N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW +BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK +BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X +57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM +eQ== +-----END CERTIFICATE----- + +Certum Trusted Network CA +========================= +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK +ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy +MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU +ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC +l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J +J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4 +fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0 +cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB +Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw +DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj +jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1 +mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj +Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +TWCA Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ +VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG +EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB +IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx +QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC +oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP +4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r +y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG +9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC +mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW +QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY +T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny +Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +Security Communication RootCA2 +============================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC +SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy +aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++ ++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R +3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV +spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K +EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8 +QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB +CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj +u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk +3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q +tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29 +mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +EC-ACC +====== +-----BEGIN CERTIFICATE----- +MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE +BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w +ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD +VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE +CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT +BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7 +MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt +SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl +Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh +cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK +w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT +ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4 +HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a +E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw +0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD +VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0 +Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l +dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ +lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa +Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe +l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2 +E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D +5EI= +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions RootCA 2011 +======================================================= +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT +O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y +aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT +AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo +IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI +1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa +71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u +8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH +3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/ +MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8 +MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu +b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt +XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD +/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N +7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- + +Actalis Authentication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQxDjAM +BgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UE +AwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDky +MjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlz +IFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNvUTufClrJ +wkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX4ay8IMKx4INRimlNAJZa +by/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9KK3giq0itFZljoZUj5NDKd45RnijMCO6 +zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1f +YVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2 +oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8l +EfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7 +hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8 +EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5 +jF66CyCU3nuDuP/jVo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLY +iDrIn3hm7YnzezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyI +WOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0 +JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKx +K3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+ +Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC +4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+OkfcvHlXHo +2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7RK4X9p2jIugErsWx0Hbhz +lefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXem +OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9 +vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +Trustis FPS Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG +EwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290 +IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV +BAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ +RUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk +H5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa +cY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt +o3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA +AaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd +BgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c +GE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC +yinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P +8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV +l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl +iB6XzCGcKQENZetX2fNXlrtIzYE= +-----END CERTIFICATE----- + +Buypass Class 2 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X +DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1 +g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn +9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b +/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU +CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff +awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI +zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn +Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX +Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs +M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI +osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S +aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd +DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD +LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0 +oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC +wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS +CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN +rJgWVqA= +-----END CERTIFICATE----- + +Buypass Class 3 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X +DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH +sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR +5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh +7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ +ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH +2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV +/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ +RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA +Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq +j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G +uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG +Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8 +ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2 +KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz +6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug +UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe +eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi +Cp/HuZc= +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 3 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx +MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK +9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU +NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF +iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W +0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr +AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb +fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT +ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h +P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw== +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe +Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE +LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD +ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA +BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv +KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z +p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC +AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ +4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y +eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw +MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G +PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw +OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm +2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV +dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph +X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 EV 2009 +================================= +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS +egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh +zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T +7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60 +sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35 +11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv +cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v +ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El +MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp +b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh +c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+ +PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX +ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA +NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv +w9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +CA Disig Root R2 +================ +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw +EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp +ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx +EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp +c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC +w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia +xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7 +A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S +GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV +g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa +5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE +koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A +Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i +Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u +Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV +sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je +dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8 +1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx +mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01 +utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0 +sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg +UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV +7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +ACCVRAIZ1 +========= +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB +SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1 +MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH +UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM +jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0 +RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD +aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ +0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG +WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7 +8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR +5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J +9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK +Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw +Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu +Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM +Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA +QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh +AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA +YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj +AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA +IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk +aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0 +dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2 +MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI +hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E +R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN +YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49 +nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ +TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3 +sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg +Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd +3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p +EfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +TWCA Global Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT +CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD +QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK +EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg +Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C +nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV +r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR +Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV +tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W +KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99 +sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p +yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn +kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI +zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g +cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M +8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg +/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg +lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP +A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m +i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8 +EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3 +zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0= +-----END CERTIFICATE----- + +TeliaSonera Root CA v1 +====================== +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE +CgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4 +MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW +VGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+ +6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA +3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k +B1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn +Xb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH +oLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3 +F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ +oWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7 +gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc +TwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB +AAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW +DNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm +zqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW +pb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV +G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc +c41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT +JsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2 +qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6 +Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems +WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +E-Tugra Certification Authority +=============================== +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w +DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls +ZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN +ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw +NTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx +QDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl +cmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD +DB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd +hQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K +CKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g +ElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ +BaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0 +E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz +rt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq +jqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn +rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5 +dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB +/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG +MA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK +kEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO +XKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807 +VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo +a2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc +dlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV +KV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT +Dx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0 +8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G +C7TbO6Orb1wdtn7os4I07QZcJA== +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 2 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgx +MDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUdAqSzm1nzHoqvNK38DcLZ +SBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiCFoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/F +vudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx970 +2cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGV +WOHAD3bZwI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXy +YdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4 +r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNf +vNoBYimipidx5joifsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR +3p1m0IvVVGb6g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg== +-----END CERTIFICATE----- + +Atos TrustedRoot 2011 +===================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRvcyBU +cnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3MDcxNDU4 +MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsG +A1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV +hTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr +54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+SZFhyBH+ +DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ4J7sVaE3IqKHBAUsR320 +HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0Lcp2AMBYHlT8oDv3FdU9T1nSatCQujgKR +z3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7R +l+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZ +bNshMBgGA1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +CwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+h +k6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrh +TZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a9 +61qn8FYiqTxlVMYVqL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G +3mB/ufNPRJLvKrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + +QuoVadis Root CA 1 G3 +===================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQELBQAwSDELMAkG +A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv +b3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEg +RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakE +PBtVwedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWerNrwU8lm +PNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF34168Xfuw6cwI2H44g4hWf6 +Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh4Pw5qlPafX7PGglTvF0FBM+hSo+LdoIN +ofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXpUhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/l +g6AnhF4EwfWQvTA9xO+oabw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV +7qJZjqlc3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/GKubX +9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSthfbZxbGL0eUQMk1f +iyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KOTk0k+17kBL5yG6YnLUlamXrXXAkg +t3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOtzCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZI +hvcNAQELBQADggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2cDMT/uFPpiN3 +GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUNqXsCHKnQO18LwIE6PWThv6ct +Tr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP ++V04ikkwj+3x6xn0dxoxGE1nVGwvb2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh +3jRJjehZrJ3ydlo28hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fa +wx/kNSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNjZgKAvQU6 +O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhpq1467HxpvMc7hU6eFbm0 +FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFtnh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOV +hMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +QuoVadis Root CA 2 G3 +===================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQELBQAwSDELMAkG +A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv +b3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIg +RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFh +ZiFfqq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMWn4rjyduY +NM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ymc5GQYaYDFCDy54ejiK2t +oIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+o +MiwMzAkd056OXbxMmO7FGmh77FOm6RQ1o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+l +V0POKa2Mq1W/xPtbAd0jIaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZo +L1NesNKqIcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz8eQQ +sSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43ehvNURG3YBZwjgQQvD +6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l7ZizlWNof/k19N+IxWA1ksB8aRxh +lRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALGcC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZI +hvcNAQELBQADggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RCroijQ1h5fq7K +pVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0GaW/ZZGYjeVYg3UQt4XAoeo0L9 +x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4nlv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgz +dWqTHBLmYF5vHX/JHyPLhGGfHoJE+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6X +U/IyAgkwo1jwDQHVcsaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+Nw +mNtddbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNgKCLjsZWD +zYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeMHVOyToV7BjjHLPj4sHKN +JeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4WSr2Rz0ZiC3oheGe7IUIarFsNMkd7Egr +O3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +QuoVadis Root CA 3 G3 +===================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQELBQAwSDELMAkG +A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv +b3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMg +RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286 +IxSR/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNuFoM7pmRL +Mon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXRU7Ox7sWTaYI+FrUoRqHe +6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+cra1AdHkrAj80//ogaX3T7mH1urPnMNA3 +I4ZyYUUpSFlob3emLoG+B01vr87ERRORFHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3U +VDmrJqMz6nWB2i3ND0/kA9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f7 +5li59wzweyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634RylsSqi +Md5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBpVzgeAVuNVejH38DM +dyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0QA4XN8f+MFrXBsj6IbGB/kE+V9/Yt +rQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZI +hvcNAQELBQADggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnIFUBhynLWcKzS +t/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5WvvoxXqA/4Ti2Tk08HS6IT7SdEQ +TXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFgu/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9Du +DcpmvJRPpq3t/O5jrFc/ZSXPsoaP0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGib +Ih6BJpsQBJFxwAYf3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmD +hPbl8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+DhcI00iX +0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HNPlopNLk9hM6xZdRZkZFW +dSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ywaZWWDYWGWVjUTR939+J399roD1B0y2 +PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +DigiCert Assured ID Root G2 +=========================== +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw +IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgw +MTE1MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSAn61UQbVH +35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4HteccbiJVMWWXvdMX0h5i89vq +bFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9HpEgjAALAcKxHad3A2m67OeYfcgnDmCXRw +VWmvo2ifv922ebPynXApVfSr/5Vh88lAbx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OP +YLfykqGxvYmJHzDNw6YuYjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+Rn +lTGNAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTO +w0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPIQW5pJ6d1Ee88hjZv +0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I0jJmwYrA8y8678Dj1JGG0VDjA9tz +d29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4GnilmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAW +hsI6yLETcDbYz+70CjTVW0z9B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0M +jomZmWzwPDCvON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +DigiCert Assured ID Root G3 +=========================== +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYD +VQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1 +MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQ +BgcqhkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJfZn4f5dwb +RXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17QRSAPWXYQ1qAk8C3eNvJs +KTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgF +UaFNN6KDec6NHSrkhDAKBggqhkjOPQQDAwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5Fy +YZ5eEJJZVrmDxxDnOOlYJjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy +1vUhZscv6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +DigiCert Global Root G2 +======================= +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUx +MjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI2/Ou8jqJ +kTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx1x7e/dfgy5SDN67sH0NO +3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQq2EGnI/yuum06ZIya7XzV+hdG82MHauV +BJVJ8zUtluNJbd134/tJS7SsVQepj5WztCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyM +UNGPHgm+F6HmIcr9g+UQvIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQAB +o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV5uNu +5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY1Yl9PMWLSn/pvtsr +F9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4NeF22d+mQrvHRAiGfzZ0JFrabA0U +WTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NGFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBH +QRFXGU7Aj64GxJUTFy8bJZ918rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/ +iyK5S9kJRaTepLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +DigiCert Global Root G3 +======================= +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYD +VQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAw +MDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k +aWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0C +AQYFK4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FGfp4tn+6O +YwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPOZ9wj/wMco+I+o0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNp +Yim8S8YwCgYIKoZIzj0EAwMDaAAwZQIxAK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y +3maTD/HMsQmP3Wyr+mt/oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34 +VOKa5Vt8sycX +-----END CERTIFICATE----- + +DigiCert Trusted Root G4 +======================== +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEw +HwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1 +MTIwMDAwWjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEp +pz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9o +k3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7Fsa +vOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGY +QJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6 +MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtm +mnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7 +f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFH +dL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8 +oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBhjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2SV1EY+CtnJYY +ZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd+SeuMIW59mdNOj6PWTkiU0Tr +yF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWcfFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy +7zBZLq7gcfJW5GqXb5JQbZaNaHqasjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iah +ixTXTBmyUEFxPT9NcCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN +5r5N0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie4u1Ki7wb +/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mIr/OSmbaz5mEP0oUA51Aa +5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tK +G48BtieVU+i2iW1bvGjUI+iLUaJW+fCmgKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP +82Z+ +-----END CERTIFICATE----- + +COMODO RSA Certification Authority +================================== +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCBhTELMAkGA1UE +BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG +A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwHhcNMTAwMTE5MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMC +R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE +ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR6FSS0gpWsawNJN3Fz0Rn +dJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8Xpz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZ +FGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+ +5eNu/Nio5JIk2kNrYrhV/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pG +x8cgoLEfZd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z+pUX +2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7wqP/0uK3pN/u6uPQL +OvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZahSL0896+1DSJMwBGB7FY79tOi4lu3 +sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVICu9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+C +GCe01a60y1Dma/RMhnEw6abfFobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5 +WdYgGq/yapiqcrxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w +DQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvlwFTPoCWOAvn9sKIN9SCYPBMt +rFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+ +nq6PK7o9mfjYcwlYRm6mnPTXJ9OV2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSg +tZx8jb8uk2IntznaFxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwW +sRqZCuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiKboHGhfKp +pC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmckejkk9u+UJueBPSZI9FoJA +zMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yLS0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHq +ZJx64SIDqZxubw5lT2yHh17zbqD5daWbQOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk52 +7RH89elWsn2/x20Kk4yl0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7I +LaZRfyHBNVOFBkpdn627G190 +-----END CERTIFICATE----- + +USERTrust RSA Certification Authority +===================================== +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCBiDELMAkGA1UE +BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK +ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UE +BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK +ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCAEmUXNg7D2wiz +0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2j +Y0K2dvKpOyuR+OJv0OwWIJAJPuLodMkYtJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFn +RghRy4YUVD+8M/5+bJz/Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O ++T23LLb2VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT79uq +/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6c0Plfg6lZrEpfDKE +Y1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmTYo61Zs8liM2EuLE/pDkP2QKe6xJM +lXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97lc6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8 +yexDJtC/QV9AqURE9JnnV4eeUB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+ +eLf8ZxXhyVeEHg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPFUp/L+M+ZBn8b2kMVn54CVVeW +FPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KOVWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ +7l8wXEskEVX/JJpuXior7gtNn3/3ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQ +Eg9zKC7F4iRO/Fjs8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM +8WcRiQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYzeSf7dNXGi +FSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZXHlKYC6SQK5MNyosycdi +yA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9c +J2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRBVXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGw +sAvgnEzDHNb842m1R0aBL6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gx +Q+6IHdfGjjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +USERTrust ECC Certification Authority +===================================== +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDELMAkGA1UEBhMC +VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMC +VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqfloI+d61SRvU8Za2EurxtW2 +0eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinngo4N+LZfQYcTxmdwlkWOrfzCjtHDix6Ez +nPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNV +HQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBB +HU6+4WMBzzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbWRNZu +9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +GlobalSign ECC Root CA - R4 +=========================== +-----BEGIN CERTIFICATE----- +MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprl +OQcJFspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAwDgYDVR0P +AQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61FuOJAf/sKbvu+M8k8o4TV +MAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGXkPoUVy0D7O48027KqGx2vKLeuwIgJ6iF +JzWbVsaj8kfSt24bAgAXqmemFZHe+pTsewv4n4Q= +-----END CERTIFICATE----- + +GlobalSign ECC Root CA - R5 +=========================== +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6 +SFkc8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8kehOvRnkmS +h5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYIKoZIzj0EAwMDaAAwZQIxAOVpEslu28Yx +uglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7 +yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +Staat der Nederlanden EV Root CA +================================ +-----BEGIN CERTIFICATE----- +MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJOTDEeMBwGA1UE +CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFhdCBkZXIgTmVkZXJsYW5kZW4g +RVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0yMjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5M +MR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRl +cmxhbmRlbiBFViBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkk +SzrSM4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nCUiY4iKTW +O0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3dZ//BYY1jTw+bbRcwJu+r +0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46prfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8 +Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13lpJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gV +XJrm0w912fxBmJc+qiXbj5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr +08C+eKxCKFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS/ZbV +0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0XcgOPvZuM5l5Tnrmd +74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH1vI4gnPah1vlPNOePqc7nvQDs/nx +fRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrPpx9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwa +ivsnuL8wbqg7MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI +eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u2dfOWBfoqSmu +c0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHSv4ilf0X8rLiltTMMgsT7B/Zq +5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTCwPTxGfARKbalGAKb12NMcIxHowNDXLldRqAN +b/9Zjr7dn3LDWyvfjFvO5QxGbJKyCqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tN +f1zuacpzEPuKqf2evTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi +5Dp6Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIaGl6I6lD4 +WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeLeG9QgkRQP2YGiqtDhFZK +DyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGy +eUN51q1veieQA6TqJIc/2b3Z6fJfUEkc7uzXLg== +-----END CERTIFICATE----- + +IdenTrust Commercial Root CA 1 +============================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBKMQswCQYDVQQG +EwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBS +b290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQwMTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzES +MBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENB +IDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ld +hNlT3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU+ehcCuz/ +mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gpS0l4PJNgiCL8mdo2yMKi +1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1bVoE/c40yiTcdCMbXTMTEl3EASX2MN0C +XZ/g1Ue9tOsbobtJSdifWwLziuQkkORiT0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl +3ZBWzvurpWCdxJ35UrCLvYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzy +NeVJSQjKVsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZKdHzV +WYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHTc+XvvqDtMwt0viAg +xGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hvl7yTmvmcEpB4eoCHFddydJxVdHix +uuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5NiGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZI +hvcNAQELBQADggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwtLRvM7Kqas6pg +ghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93nAbowacYXVKV7cndJZ5t+qnt +ozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmV +YjzlVYA211QC//G5Xc7UI2/YRYRKW2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUX +feu+h1sXIFRRk0pTAwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/ro +kTLql1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG4iZZRHUe +2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZmUlO+KWA2yUPHGNiiskz +Z2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7R +cGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + +IdenTrust Public Sector Root CA 1 +================================= +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQG +EwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3Rv +ciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcNMzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJV +UzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBS +b290IENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTy +P4o7ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGyRBb06tD6 +Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlSbdsHyo+1W/CD80/HLaXI +rcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF/YTLNiCBWS2ab21ISGHKTN9T0a9SvESf +qy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoS +mJxZZoY+rfGwyj4GD3vwEUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFn +ol57plzy9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9VGxyh +LrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ2fjXctscvG29ZV/v +iDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsVWaFHVCkugyhfHMKiq3IXAAaOReyL +4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gDW/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8B +Af8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMw +DQYJKoZIhvcNAQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHVDRDtfULAj+7A +mgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9TaDKQGXSc3z1i9kKlT/YPyNt +GtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8GlwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFt +m6/n6J91eEyrRjuazr8FGF1NFTwWmhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMx +NRF4eKLg6TCMf4DfWN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4 +Mhn5+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJtshquDDI +ajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhAGaQdp/lLQzfcaFpPz+vC +ZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ +3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- + +Entrust Root Certification Authority - G2 +========================================= +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMCVVMxFjAUBgNV +BAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVy +bXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ug +b25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIw +HhcNMDkwNzA3MTcyNTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoT +DUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMx +OTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP +/vaCeb9zYQYKpSfYs1/TRU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXz +HHfV1IWNcCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hWwcKU +s/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1U1+cPvQXLOZprE4y +TGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0jaWvYkxN4FisZDQSA/i2jZRjJKRx +AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ6 +0B7vfec7aVHUbI2fkBJmqzANBgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5Z +iXMRrEPR9RP/jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v1fN2D807iDgi +nWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4RnAuknZoh8/CbCzB428Hch0P+ +vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmHVHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xO +e4pIb4tF9g== +-----END CERTIFICATE----- + +Entrust Root Certification Authority - EC1 +========================================== +-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkGA1UEBhMCVVMx +FjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVn +YWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXpl +ZCB1c2Ugb25seTEzMDEGA1UEAxMqRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gRUMxMB4XDTEyMTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYw +FAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2Fs +LXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQg +dXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt +IEVDMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHy +AsWfoPZb1YsGGYZPUxBtByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef +9eNi1KlHBz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVCR98crlOZF7ZvHH3h +vxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nXhTcGtXsI/esni0qU+eH6p44mCOh8 +kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- + +CFCA EV ROOT +============ +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJDTjEwMC4GA1UE +CgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNB +IEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkxMjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEw +MC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQD +DAxDRkNBIEVWIFJPT1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnV +BU03sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpLTIpTUnrD +7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5/ZOkVIBMUtRSqy5J35DN +uF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp7hZZLDRJGqgG16iI0gNyejLi6mhNbiyW +ZXvKWfry4t3uMCz7zEasxGPrb382KzRzEpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7 +xzbh72fROdOXW3NiGUgthxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9f +py25IGvPa931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqotaK8K +gWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNgTnYGmE69g60dWIol +hdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfVPKPtl8MeNPo4+QgO48BdK4PRVmrJ +tqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hvcWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAf +BgNVHSMEGDAWgBTj/i39KNALtbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjAdBgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObTej/tUxPQ4i9q +ecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdLjOztUmCypAbqTuv0axn96/Ua +4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBSESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sG +E5uPhnEFtC+NiWYzKXZUmhH4J/qyP5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfX +BDrDMlI1Dlb4pd19xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjn +aH9dCi77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN5mydLIhy +PDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe/v5WOaHIz16eGWRGENoX +kbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+ZAAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3C +ekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GB CA +=============================== +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBtMQswCQYDVQQG +EwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl +ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAw +MzJaFw0zOTEyMDExNTEwMzFaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYD +VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEds +b2JhbCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3HEokKtaX +scriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGxWuR51jIjK+FTzJlFXHtP +rby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk +9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNku7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4o +Qnc/nSMbsrY9gBQHTC5P99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvg +GUpuuy9rM2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZI +hvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrghcViXfa43FK8+5/ea4n32cZiZBKpD +dHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0 +VQreUGdNZtGn//3ZwLWoo4rOZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEui +HZeeevJuQHHfaPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic +Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= +-----END CERTIFICATE----- + +SZAFIR ROOT CA2 +=============== +-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQELBQAwUTELMAkG +A1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6ZW5pb3dhIFMuQS4xGDAWBgNV +BAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkwNzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJ +BgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYD +VQQDDA9TWkFGSVIgUk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5Q +qEvNQLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT3PSQ1hNK +DJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw3gAeqDRHu5rr/gsUvTaE +2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr63fE9biCloBK0TXC5ztdyO4mTp4CEHCdJ +ckm1/zuVnsHMyAHs6A6KCpbns6aH5db5BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwi +ieDhZNRnvDF5YTy7ykHNXGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P +AQH/BAQDAgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsFAAOC +AQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw8PRBEew/R40/cof5 +O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOGnXkZ7/e7DDWQw4rtTw/1zBLZpD67 +oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCPoky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul +4+vJhaAlIDf7js4MNIThPIGyd05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6 ++/NNIxuZMzSgLvWpCz/UXeHPhJ/iGcJfitYgHuNztw== +-----END CERTIFICATE----- + +Certum Trusted Network CA 2 +=========================== +-----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCBgDELMAkGA1UE +BhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1 +bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29y +ayBDQSAyMCIYDzIwMTExMDA2MDgzOTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQ +TDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENB +IDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWADGSdhhuWZGc/IjoedQF9 +7/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+o +CgCXhVqqndwpyeI1B+twTUrWwbNWuKFBOJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40b +Rr5HMNUuctHFY9rnY3lEfktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2p +uTRZCr+ESv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1mo130 +GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02isx7QBlrd9pPPV3WZ +9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOWOZV7bIBaTxNyxtd9KXpEulKkKtVB +Rgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgezTv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pye +hizKV/Ma5ciSixqClnrDvFASadgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vM +BhBgu4M1t15n3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZI +hvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQF/xlhMcQSZDe28cmk4gmb3DW +Al45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTfCVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuA +L55MYIR4PSFk1vtBHxgP58l1cb29XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMo +clm2q8KMZiYcdywmdjWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tM +pkT/WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jbAoJnwTnb +w3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksqP/ujmv5zMnHCnsZy4Ypo +J/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Kob7a6bINDd82Kkhehnlt4Fj1F4jNy3eFm +ypnTycUm/Q1oBEauttmbjL4ZvrHG8hnjXALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLX +is7VmFxWlgPF7ncGNf/P5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7 +zAYspsbiDrW5viSP +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions RootCA 2015 +======================================================= +-----BEGIN CERTIFICATE----- +MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcT +BkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0 +aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl +YXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAx +MTIxWjCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMg +QWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNV +BAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIw +MTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDC+Kk/G4n8PDwEXT2QNrCROnk8Zlrv +bTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+eh +iGsxr/CL0BgzuNtFajT0AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+ +6PAQZe104S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06CojXd +FPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV9Cz82XBST3i4vTwr +i5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrDgfgXy5I2XdGj2HUb4Ysn6npIQf1F +GQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2 +fu/Z8VFRfS0myGlZYeCsargqNhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9mu +iNX6hME6wGkoLfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc +Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVdctA4GGqd83EkVAswDQYJKoZI +hvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0IXtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+ +D1hYc2Ryx+hFjtyp8iY/xnmMsVMIM4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrM +d/K4kPFox/la/vot9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+y +d+2VZ5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/eaj8GsGsVn +82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnhX9izjFk0WaSrT2y7Hxjb +davYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQl033DlZdwJVqwjbDG2jJ9SrcR5q+ss7F +Jej6A7na+RZukYT1HCjI/CbM1xyQVqdfbzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVt +J94Cj8rDtSvK6evIIVM4pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGa +JI7ZjnHKe7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0vm9q +p/UsQu0yrbYhnr68 +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions ECC RootCA 2015 +=========================================================== +-----BEGIN CERTIFICATE----- +MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0 +aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u +cyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj +aCBJbnN0aXR1dGlvbnMgRUNDIFJvb3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEw +MzcxMlowgaoxCzAJBgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmlj +IEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUQwQgYD +VQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIEVDQyBSb290 +Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKgQehLgoRc4vgxEZmGZE4JJS+dQS8KrjVP +dJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJajq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoK +Vlp8aQuqgAkkbH7BRqNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O +BBYEFLQiC4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaeplSTA +GiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7SofTUwJCA3sS61kFyjn +dc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR +-----END CERTIFICATE----- + +ISRG Root X1 +============ +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UE +BhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQD +EwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQG +EwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMT +DElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54r +Vygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj1 +3Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8K +b4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCN +Aymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ +4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf +1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFu +hjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQH +usEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/r +OPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4G +A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY +9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV +0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwt +hDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJw +TdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nx +e5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZA +JzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahD +YVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9n +JEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJ +m+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- + +AC RAIZ FNMT-RCM +================ +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNVBAYT +AkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTAeFw0wODEw +MjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJD +TTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBALpxgHpMhm5/yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcf +qQgfBBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAzWHFctPVr +btQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxFtBDXaEAUwED653cXeuYL +j2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z374jNUUeAlz+taibmSXaXvMiwzn15Cou +08YfxGyqxRxqAQVKL9LFwag0Jl1mpdICIfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mw +WsXmo8RZZUc1g16p6DULmbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnT +tOmlcYF7wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peSMKGJ +47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2ZSysV4999AeU14EC +ll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMetUqIJ5G+GR4of6ygnXYMgrwTJbFaa +i0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FPd9xf3E6Jobd2Sn9R2gzL+HYJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1o +dHRwOi8vd3d3LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD +nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1RXxlDPiyN8+s +D8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYMLVN0V2Ue1bLdI4E7pWYjJ2cJ +j+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrT +Qfv6MooqtyuGC2mDOL7Nii4LcK2NJpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW ++YJF1DngoABd15jmfZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7 +Ixjp6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp1txyM/1d +8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B9kiABdcPUXmsEKvU7ANm +5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wokRqEIr9baRRmW1FMdW4R58MD3R++Lj8UG +rp1MYp3/RgT408m2ECVAdf4WqslKYIYvuu8wd+RU4riEmViAqhOLUTpPSPaLtrM= +-----END CERTIFICATE----- + +Amazon Root CA 1 +================ +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsFADA5MQswCQYD +VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAxMB4XDTE1 +MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv +bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgH +FzZM9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qwIFAGbHrQ +gLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6VOujw5H5SNz/0egwLX0t +dHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L93FcXmn/6pUCyziKrlA4b9v7LWIbxcce +VOF34GfID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3 +DQEBCwUAA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDIU5PM +CCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUsN+gDS63pYaACbvXy +8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa +2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2 +xJNDd2ZhwLnoQdeXeGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- + +Amazon Root CA 2 +================ +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwFADA5MQswCQYD +VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAyMB4XDTE1 +MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv +bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBAK2Wny2cSkxKgXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4 +kHbZW0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg1dKmSYXp +N+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K8nu+NQWpEjTj82R0Yiw9 +AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvd +fLC6HM783k81ds8P+HgfajZRRidhW+mez/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAEx +kv8LV/SasrlX6avvDXbR8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSS +btqDT6ZjmUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz7Mt0 +Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6+XUyo05f7O0oYtlN +c/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI0u1ufm8/0i2BWSlmy5A5lREedCf+ +3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSw +DPBMMPQFWAJI/TPlUq9LhONmUjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oA +A7CXDpO8Wqj2LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kSk5Nrp+gvU5LE +YFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl7uxMMne0nxrpS10gxdr9HIcW +xkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygmbtmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQ +gj9sAq+uEjonljYE1x2igGOpm/HlurR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbW +aQbLU8uz/mtBzUF+fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoV +Yh63n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE76KlXIx3 +KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H9jVlpNMKVv/1F2Rs76gi +JUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT4PsJYGw= +-----END CERTIFICATE----- + +Amazon Root CA 3 +================ +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5MQswCQYDVQQG +EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAzMB4XDTE1MDUy +NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ +MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZB +f8ANm+gBG1bG8lKlui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjr +Zt6jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSrttvXBp43 +rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkrBqWTrBqYaGFy+uGh0Psc +eGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteMYyRIHN8wfdVoOw== +-----END CERTIFICATE----- + +Amazon Root CA 4 +================ +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5MQswCQYDVQQG +EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSA0MB4XDTE1MDUy +NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ +MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN +/sGKe0uoe0ZLY7Bi9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri +83BkM6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WBMAoGCCqGSM49BAMDA2gA +MGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlwCkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1 +AE47xDqUEpHJWEadIRNyp4iciuRMStuW1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE----- + +TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 +============================================= +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIxGDAWBgNVBAcT +D0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxpbXNlbCB2ZSBUZWtub2xvamlr +IEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0wKwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24g +TWVya2V6aSAtIEthbXUgU00xNjA0BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRp +ZmlrYXNpIC0gU3VydW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYD +VQQGEwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXllIEJpbGlt +c2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklUQUsxLTArBgNVBAsTJEth +bXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBTTTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11 +IFNNIFNTTCBLb2sgU2VydGlmaWthc2kgLSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAr3UwM6q7a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y8 +6Ij5iySrLqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INrN3wc +wv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2XYacQuFWQfw4tJzh0 +3+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/iSIzL+aFCr2lqBs23tPcLG07xxO9 +WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4fAJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQU +ZT/HiobGPN08VFw1+DrtUgxHV8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJ +KoZIhvcNAQELBQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh +AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPfIPP54+M638yc +lNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4lzwDGrpDxpa5RXI4s6ehlj2R +e37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0j +q5Rm+K37DwhuJi1/FwcJsoz7UMCflo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= +-----END CERTIFICATE----- + +GDCA TrustAUTH R5 ROOT +====================== +-----BEGIN CERTIFICATE----- +MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCQ04xMjAw +BgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8wHQYDVQQD +DBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVow +YjELMAkGA1UEBhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ +IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJjDp6L3TQs +AlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBjTnnEt1u9ol2x8kECK62p +OqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+uKU49tm7srsHwJ5uu4/Ts765/94Y9cnrr +pftZTqfrlYwiOXnhLQiPzLyRuEH3FMEjqcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ +9Cy5WmYqsBebnh52nUpmMUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQ +xXABZG12ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloPzgsM +R6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3GkL30SgLdTMEZeS1SZ +D2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeCjGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4 +oR24qoAATILnsn8JuLwwoC8N9VKejveSswoAHQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx +9hoh49pwBiFYFIeFd3mqgnkCAwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlR +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg +p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZmDRd9FBUb1Ov9 +H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5COmSdI31R9KrO9b7eGZONn35 +6ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ryL3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd ++PwyvzeG5LuOmCd+uh8W4XAR8gPfJWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQ +HtZa37dG/OaG+svgIHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBD +F8Io2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV09tL7ECQ +8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQXR4EzzffHqhmsYzmIGrv +/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrqT8p+ck0LcIymSLumoRT2+1hEmRSuqguT +aaApJUqlyyvdimYHFngVV3Eb7PVHhPOeMTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== +-----END CERTIFICATE----- + +TrustCor RootCert CA-1 +====================== +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYDVQQGEwJQQTEP +MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig +U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp +dHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkx +MjMxMTcyMzE2WjCBpDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFu +YW1hIENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUGA1UECwwe +VHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZUcnVzdENvciBSb290Q2Vy +dCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv463leLCJhJrMxnHQFgKq1mq +jQCj/IDHUHuO1CAmujIS2CNUSSUQIpidRtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4 +pQa81QBeCQryJ3pS/C3Vseq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0 +JEsq1pme9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CVEY4h +gLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorWhnAbJN7+KIor0Gqw +/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/DeOxCbeKyKsZn3MzUOcwHwYDVR0j +BBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwDQYJKoZIhvcNAQELBQADggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5 +mDo4Nvu7Zp5I/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf +ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZyonnMlo2HD6C +qFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djtsL1Ac59v2Z3kf9YKVmgenFK+P +3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdNzl/HHk484IkzlQsPpTLWPFp5LBk= +-----END CERTIFICATE----- + +TrustCor RootCert CA-2 +====================== +-----BEGIN CERTIFICATE----- +MIIGLzCCBBegAwIBAgIIJaHfyjPLWQIwDQYJKoZIhvcNAQELBQAwgaQxCzAJBgNVBAYTAlBBMQ8w +DQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQwIgYDVQQKDBtUcnVzdENvciBT +eXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0 +eTEfMB0GA1UEAwwWVHJ1c3RDb3IgUm9vdENlcnQgQ0EtMjAeFw0xNjAyMDQxMjMyMjNaFw0zNDEy +MzExNzI2MzlaMIGkMQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5h +bWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U +cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0 +IENBLTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnIG7CKqJiJJWQdsg4foDSq8Gb +ZQWU9MEKENUCrO2fk8eHyLAnK0IMPQo+QVqedd2NyuCb7GgypGmSaIwLgQ5WoD4a3SwlFIIvl9Nk +RvRUqdw6VC0xK5mC8tkq1+9xALgxpL56JAfDQiDyitSSBBtlVkxs1Pu2YVpHI7TYabS3OtB0PAx1 +oYxOdqHp2yqlO/rOsP9+aij9JxzIsekp8VduZLTQwRVtDr4uDkbIXvRR/u8OYzo7cbrPb1nKDOOb +XUm4TOJXsZiKQlecdu/vvdFoqNL0Cbt3Nb4lggjEFixEIFapRBF37120Hapeaz6LMvYHL1cEksr1 +/p3C6eizjkxLAjHZ5DxIgif3GIJ2SDpxsROhOdUuxTTCHWKF3wP+TfSvPd9cW436cOGlfifHhi5q +jxLGhF5DUVCcGZt45vz27Ud+ez1m7xMTiF88oWP7+ayHNZ/zgp6kPwqcMWmLmaSISo5uZk3vFsQP +eSghYA2FFn3XVDjxklb9tTNMg9zXEJ9L/cb4Qr26fHMC4P99zVvh1Kxhe1fVSntb1IVYJ12/+Ctg +rKAmrhQhJ8Z3mjOAPF5GP/fDsaOGM8boXg25NSyqRsGFAnWAoOsk+xWq5Gd/bnc/9ASKL3x74xdh +8N0JqSDIvgmk0H5Ew7IwSjiqqewYmgeCK9u4nBit2uBGF6zPXQIDAQABo2MwYTAdBgNVHQ4EFgQU +2f4hQG6UnrybPZx9mCAZ5YwwYrIwHwYDVR0jBBgwFoAU2f4hQG6UnrybPZx9mCAZ5YwwYrIwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAJ5Fngw7tu/h +Osh80QA9z+LqBrWyOrsGS2h60COXdKcs8AjYeVrXWoSK2BKaG9l9XE1wxaX5q+WjiYndAfrs3fnp +kpfbsEZC89NiqpX+MWcUaViQCqoL7jcjx1BRtPV+nuN79+TMQjItSQzL/0kMmx40/W5ulop5A7Zv +2wnL/V9lFDfhOPXzYRZY5LVtDQsEGz9QLX+zx3oaFoBg+Iof6Rsqxvm6ARppv9JYx1RXCI/hOWB3 +S6xZhBqI8d3LT3jX5+EzLfzuQfogsL7L9ziUwOHQhQ+77Sxzq+3+knYaZH9bDTMJBzN7Bj8RpFxw +PIXAz+OQqIN3+tvmxYxoZxBnpVIt8MSZj3+/0WvitUfW2dCFmU2Umw9Lje4AWkcdEQOsQRivh7dv +DDqPys/cA8GiCcjl/YBeyGBCARsaU1q7N6a3vLqE6R5sGtRk2tRD/pOLS/IseRYQ1JMLiI+h2IYU +RpFHmygk71dSTlxCnKr3Sewn6EAes6aJInKc9Q0ztFijMDvd1GpUk74aTfOTlPf8hAs/hCBcNANE +xdqtvArBAs8e5ZTZ845b2EzwnexhF7sUMlQMAimTHpKG9n/v55IFDlndmQguLvqcAFLTxWYp5KeX +RKQOKIETNcX2b2TmQcTVL8w0RSXPQQCWPUouwpaYT05KnJe32x+SMsj/D1Fu1uwJ +-----END CERTIFICATE----- + +TrustCor ECA-1 +============== +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIJAISCLF8cYtBAMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYDVQQGEwJQQTEP +MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig +U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp +dHkxFzAVBgNVBAMMDlRydXN0Q29yIEVDQS0xMB4XDTE2MDIwNDEyMzIzM1oXDTI5MTIzMTE3Mjgw +N1owgZwxCzAJBgNVBAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5 +MSQwIgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29y +IENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAwwOVHJ1c3RDb3IgRUNBLTEwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPj+ARtZ+odnbb3w9U73NjKYKtR8aja+3+XzP4Q1HpGjOR +MRegdMTUpwHmspI+ap3tDvl0mEDTPwOABoJA6LHip1GnHYMma6ve+heRK9jGrB6xnhkB1Zem6g23 +xFUfJ3zSCNV2HykVh0A53ThFEXXQmqc04L/NyFIduUd+Dbi7xgz2c1cWWn5DkR9VOsZtRASqnKmc +p0yJF4OuowReUoCLHhIlERnXDH19MURB6tuvsBzvgdAsxZohmz3tQjtQJvLsznFhBmIhVE5/wZ0+ +fyCMgMsq2JdiyIMzkX2woloPV+g7zPIlstR8L+xNxqE6FXrntl019fZISjZFZtS6mFjBAgMBAAGj +YzBhMB0GA1UdDgQWBBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAfBgNVHSMEGDAWgBREnkj1zG1I1KBL +f/5ZJC+Dl5mahjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF +AAOCAQEABT41XBVwm8nHc2FvcivUwo/yQ10CzsSUuZQRg2dd4mdsdXa/uwyqNsatR5Nj3B5+1t4u +/ukZMjgDfxT2AHMsWbEhBuH7rBiVDKP/mZb3Kyeb1STMHd3BOuCYRLDE5D53sXOpZCz2HAF8P11F +hcCF5yWPldwX8zyfGm6wyuMdKulMY/okYWLW2n62HGz1Ah3UKt1VkOsqEUc8Ll50soIipX1TH0Xs +J5F95yIW6MBoNtjG8U+ARDL54dHRHareqKucBK+tIA5kmE2la8BIWJZpTdwHjFGTot+fDz2LYLSC +jaoITmJF4PkL0uDgPFveXHEnJcLmA4GLEFPjx1WitJ/X5g== +-----END CERTIFICATE----- + +SSL.com Root Certification Authority RSA +======================================== +-----BEGIN CERTIFICATE----- +MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxDjAM +BgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24x +MTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYw +MjEyMTczOTM5WhcNNDEwMjEyMTczOTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx +EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NM +LmNvbSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2RxFdHaxh3a3by/ZPkPQ/C +Fp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aXqhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8 +P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcCC52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/ge +oeOy3ZExqysdBP+lSgQ36YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkp +k8zruFvh/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrFYD3Z +fBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93EJNyAKoFBbZQ+yODJ +gUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVcUS4cK38acijnALXRdMbX5J+tB5O2 +UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi8 +1xtZPCvM8hnIk2snYxnP/Okm+Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4s +bE6x/c+cCbqiM+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGVcpNxJK1ok1iOMq8bs3AD/CUr +dIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBcHadm47GUBwwyOabqG7B52B2ccETjit3E+ZUf +ijhDPwGFpUenPUayvOUiaPd7nNgsPgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAsl +u1OJD7OAUN5F7kR/q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjq +erQ0cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jra6x+3uxj +MxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90IH37hVZkLId6Tngr75qNJ +vTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/YK9f1JmzJBjSWFupwWRoyeXkLtoh/D1JI +Pb9s2KJELtFOt3JY04kTlf5Eq/jXixtunLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406y +wKBjYZC6VWg3dGq2ktufoYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NI +WuuA8ShYIc2wBlX7Jz9TkHCpBB5XJ7k= +-----END CERTIFICATE----- + +SSL.com Root Certification Authority ECC +======================================== +-----BEGIN CERTIFICATE----- +MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMCVVMxDjAMBgNV +BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xMTAv +BgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEy +MTgxNDAzWhcNNDEwMjEyMTgxNDAzWjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAO +BgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv +bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuBBAAiA2IA +BEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI7Z4INcgn64mMU1jrYor+ +8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPgCemB+vNH06NjMGEwHQYDVR0OBBYEFILR +hXMw5zUE044CkvvlpNHEIejNMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTT +jgKS++Wk0cQh6M0wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCW +e+0F+S8Tkdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+gA0z +5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl +-----END CERTIFICATE----- + +SSL.com EV Root Certification Authority RSA R2 +============================================== +-----BEGIN CERTIFICATE----- +MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNVBAYTAlVTMQ4w +DAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9u +MTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy +MB4XDTE3MDUzMTE4MTQzN1oXDTQyMDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQI +DAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYD +VQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvqM0fNTPl9fb69LT3w23jh +hqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssufOePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7w +cXHswxzpY6IXFJ3vG2fThVUCAtZJycxa4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTO +Zw+oz12WGQvE43LrrdF9HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+ +B6KjBSYRaZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcAb9Zh +CBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQGp8hLH94t2S42Oim +9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQVPWKchjgGAGYS5Fl2WlPAApiiECto +RHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMOpgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+Slm +JuwgUHfbSguPvuUCYHBBXtSuUDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48 ++qvWBkofZ6aYMBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV +HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa49QaAJadz20Zp +qJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBWs47LCp1Jjr+kxJG7ZhcFUZh1 +++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nx +Y/hoLVUE0fKNsKTPvDxeH3jnpaAgcLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2G +guDKBAdRUNf/ktUM79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDz +OFSz/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXtll9ldDz7 +CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEmKf7GUmG6sXP/wwyc5Wxq +lD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKKQbNmC1r7fSOl8hqw/96bg5Qu0T/fkreR +rwU7ZcegbLHNYhLDkBvjJc40vG93drEQw/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1 +hlMYegouCRw2n5H9gooiS9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX +9hwJ1C07mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== +-----END CERTIFICATE----- + +SSL.com EV Root Certification Authority ECC +=========================================== +-----BEGIN CERTIFICATE----- +MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMCVVMxDjAMBgNV +BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xNDAy +BgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYw +MjEyMTgxNTIzWhcNNDEwMjEyMTgxNTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx +EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NM +LmNvbSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMAVIbc/R/fALhBYlzccBYy +3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1KthkuWnBaBu2+8KGwytAJKaNjMGEwHQYDVR0O +BBYEFFvKXuXe0oGqzagtZFG22XKbl+ZPMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe +5d7SgarNqC1kUbbZcpuX5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJ +N+vp1RPZytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZgh5Mm +m7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== +-----END CERTIFICATE----- + +GlobalSign Root CA - R6 +======================= +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEgMB4GA1UECxMX +R2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds +b2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQxMjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9i +YWxTaWduIFJvb3QgQ0EgLSBSNjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFs +U2lnbjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQss +grRIxutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1kZguSgMpE +3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxDaNc9PIrFsmbVkJq3MQbF +vuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJwLnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqM +PKq0pPbzlUoSB239jLKJz9CgYXfIWHSw1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+ +azayOeSsJDa38O+2HBNXk7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05O +WgtH8wY2SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/hbguy +CLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4nWUx2OVvq+aWh2IMP +0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpYrZxCRXluDocZXFSxZba/jJvcE+kN +b7gu3GduyYsRtYQUigAZcIN5kZeR1BonvzceMgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQE +AwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNV +HSMEGDAWgBSubAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN +nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGtIxg93eFyRJa0 +lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr6155wsTLxDKZmOMNOsIeDjHfrY +BzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLjvUYAGm0CuiVdjaExUd1URhxN25mW7xocBFym +Fe944Hn+Xds+qkxV/ZoVqW/hpvvfcDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr +3TsTjxKM4kEaSHpzoHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB1 +0jZpnOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfspA9MRf/T +uTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+vJJUEeKgDu+6B5dpffItK +oZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+t +JDfLRVpOoERIyNiwmcUVhAn21klJwGW45hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GC CA +=============================== +-----BEGIN CERTIFICATE----- +MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQswCQYDVQQGEwJD +SDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEo +MCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRa +Fw00MjA1MDkwOTU4MzNaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQL +ExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh +bCBSb290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4nieUqjFqdr +VCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4Wp2OQ0jnUsYd4XxiWD1Ab +NTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7TrYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0E +AwMDaAAwZQIwJsdpW9zV57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtk +AjEA2zQgMgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 +-----END CERTIFICATE----- + +GTS Root R1 +=========== +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQbkepxUtHDA3sM9CJuRz04TANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQG +EwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv +b3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAG +A1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx +9vaMf/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7wCl7r +aKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjwTcLCeoiKu7rPWRnW +r4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0PfyblqAj+lug8aJRT7oM6iCsVlgmy4HqM +LnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly +4cpk9+aCEI3oncKKiPo4Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr +06zqkUspzBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 +wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70paDPvOmbsB4om +3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrNVjzRlwW5y0vtOUucxD/SVRNu +JLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEM +BQADggIBADiWCu49tJYeX++dnAsznyvgyv3SjgofQXSlfKqE1OXyHuY3UjKcC9FhHb8owbZEKTV1 +d5iyfNm9dKyKaOOpMQkpAWBz40d8U6iQSifvS9efk+eCNs6aaAyC58/UEBZvXw6ZXPYfcX3v73sv +fuo21pdwCxXu11xWajOl40k4DLh9+42FpLFZXvRq4d2h9mREruZRgyFmxhE+885H7pwoHyXa/6xm +ld01D1zvICxi/ZG6qcz8WpyTgYMpl0p8WnK0OdC3d8t5/Wk6kjftbjhlRn7pYL15iJdfOBL07q9b +gsiG1eGZbYwE8na6SfZu6W0eX6DvJ4J2QPim01hcDyxC2kLGe4g0x8HYRZvBPsVhHdljUEn2NIVq +4BjFbkerQUIpm/ZgDdIx02OYI5NaAIFItO/Nis3Jz5nu2Z6qNuFoS3FJFDYoOj0dzpqPJeaAcWEr +tXvM+SUWgeExX6GjfhaknBZqlxi9dnKlC54dNuYvoS++cJEPqOba+MSSQGwlfnuzCdyyF62ARPBo +pY+Udf90WuioAnwMCeKpSwughQtiue+hMZL77/ZRBIls6Kl0obsXs7X9SQ98POyDGCBDTtWTurQ0 +sR8WNh8M5mQ5Fkzc4P4dyKliPUDqysU0ArSuiYgzNdwsE3PYJ/HQcu51OyLemGhmW/HGY0dVHLql +CFF1pkgl +-----END CERTIFICATE----- + +GTS Root R2 +=========== +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQbkepxlqz5yDFMJo/aFLybzANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQG +EwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv +b3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAG +A1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTuk +k3LvCvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY6Dlo +7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAuMC6C/Pq8tBcKSOWI +m8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7kRXuJVfeKH2JShBKzwkCX44ofR5Gm +dFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbu +ak7MkogwTZq9TwtImoS1mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscsz +cTJGr61K8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW +Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKaG73Vululycsl +aVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCqgc7dGtxRcw1PcOnlthYhGXmy +5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEM +BQADggIBALZp8KZ3/p7uC4Gt4cCpx/k1HUCCq+YEtN/L9x0Pg/B+E02NjO7jMyLDOfxA325BS0JT +vhaI8dI4XsRomRyYUpOM52jtG2pzegVATX9lO9ZY8c6DR2Dj/5epnGB3GFW1fgiTz9D2PGcDFWEJ ++YF59exTpJ/JjwGLc8R3dtyDovUMSRqodt6Sm2T4syzFJ9MHwAiApJiS4wGWAqoC7o87xdFtCjMw +c3i5T1QWvwsHoaRc5svJXISPD+AVdyx+Jn7axEvbpxZ3B7DNdehyQtaVhJ2Gg/LkkM0JR9SLA3Da +WsYDQvTtN6LwG1BUSw7YhN4ZKJmBR64JGz9I0cNv4rBgF/XuIwKl2gBbbZCr7qLpGzvpx0QnRY5r +n/WkhLx3+WuXrD5RRaIRpsyF7gpo8j5QOHokYh4XIDdtak23CZvJ/KRY9bb7nE4Yu5UC56Gtmwfu +Nmsk0jmGwZODUNKBRqhfYlcsu2xkiAhu7xNUX90txGdj08+JN7+dIPT7eoOboB6BAFDC5AwiWVIQ +7UNWhwD4FFKnHYuTjKJNRn8nxnGbJN7k2oaLDX5rIMHAnuFl2GqjpuiFizoHCBy69Y9Vmhh1fuXs +gWbRIXOhNUQLgD1bnF5vKheW0YMjiGZt5obicDIvUiLnyOd/xCxgXS/Dr55FBcOEArf9LAhST4Ld +o/DUhgkC +-----END CERTIFICATE----- + +GTS Root R3 +=========== +-----BEGIN CERTIFICATE----- +MIICDDCCAZGgAwIBAgIQbkepx2ypcyRAiQ8DVd2NHTAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJV +UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg +UjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE +ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUU +Rout736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL24Cej +QjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTB8Sa6oC2uhYHP +0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEAgFukfCPAlaUs3L6JbyO5o91lAFJekazInXJ0 +glMLfalAvWhgxeG4VDvBNhcl2MG9AjEAnjWSdIUlUfUk7GRSJFClH9voy8l27OyCbvWFGFPouOOa +KaqW04MjyaR7YbPMAuhd +-----END CERTIFICATE----- + +GTS Root R4 +=========== +-----BEGIN CERTIFICATE----- +MIICCjCCAZGgAwIBAgIQbkepyIuUtui7OyrYorLBmTAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJV +UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg +UjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE +ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa +6zzuhXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvRHYqj +QjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSATNbrdP9JNqPV +2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNnADBkAjBqUFJ0CMRw3J5QdCHojXohw0+WbhXRIjVhLfoI +N+4Zba3bssx9BzT1YBkstTTZbyACMANxsbqjYAuG7ZoIapVon+Kz4ZNkfF6Tpt95LY2F45TPI11x +zPKwTdb+mciUqXWi4w== +-----END CERTIFICATE----- + +UCA Global G2 Root +================== +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9MQswCQYDVQQG +EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBHbG9iYWwgRzIgUm9vdDAeFw0x +NjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0xCzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlU +cnVzdDEbMBkGA1UEAwwSVUNBIEdsb2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxeYrb3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmT +oni9kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzmVHqUwCoV +8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/RVogvGjqNO7uCEeBHANBS +h6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDcC/Vkw85DvG1xudLeJ1uK6NjGruFZfc8o +LTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIjtm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/ +R+zvWr9LesGtOxdQXGLYD0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBe +KW4bHAyvj5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6DlNaBa +4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6iIis7nCs+dwp4wwc +OxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznPO6Q0ibd5Ei9Hxeepl2n8pndntd97 +8XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFIHEjMz15DD/pQwIX4wVZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo +5sOASD0Ee/ojL3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5 +1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl1qnN3e92mI0A +Ds0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oUb3n09tDh05S60FdRvScFDcH9 +yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LVPtateJLbXDzz2K36uGt/xDYotgIVilQsnLAX +c47QN6MUPJiVAAwpBVueSUmxX8fjy88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHo +jhJi6IjMtX9Gl8CbEGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZk +bxqgDMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI+Vg7RE+x +ygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGyYiGqhkCyLmTTX8jjfhFn +RR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bXUB+K+wb1whnw0A== +-----END CERTIFICATE----- + +UCA Extended Validation Root +============================ +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQG +EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9u +IFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMxMDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8G +A1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrs +iWogD4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvSsPGP2KxF +Rv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aopO2z6+I9tTcg1367r3CTu +eUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dksHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR +59mzLC52LqGj3n5qiAno8geK+LLNEOfic0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH +0mK1lTnj8/FtDw5lhIpjVMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KR +el7sFsLzKuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/TuDv +B0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41Gsx2VYVdWf6/wFlth +WG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs1+lvK9JKBZP8nm9rZ/+I8U6laUpS +NwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQDfwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS +3H5aBZ8eNJr34RQwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQEL +BQADggIBADaNl8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR +ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQVBcZEhrxH9cM +aVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5c6sq1WnIeJEmMX3ixzDx/BR4 +dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb ++7lsq+KePRXBOy5nAliRn+/4Qh8st2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOW +F3sGPjLtx7dCvHaj2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwi +GpWOvpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2CxR9GUeOc +GMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmxcmtpzyKEC2IPrNkZAJSi +djzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbMfjKaiJUINlK73nZfdklJrX+9ZSCyycEr +dhh2n1ax +-----END CERTIFICATE----- + +Certigna Root CA +================ +-----BEGIN CERTIFICATE----- +MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAwWjELMAkGA1UE +BhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAwMiA0ODE0NjMwODEwMDAzNjEZ +MBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0xMzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjda +MFoxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYz +MDgxMDAwMzYxGTAXBgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sOty3tRQgX +stmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9MCiBtnyN6tMbaLOQdLNyz +KNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPuI9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8 +JXrJhFwLrN1CTivngqIkicuQstDuI7pmTLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16 +XdG+RCYyKfHx9WzMfgIhC59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq +4NYKpkDfePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3YzIoej +wpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWTCo/1VTp2lc5ZmIoJ +lXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1kJWumIWmbat10TWuXekG9qxf5kBdI +jzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp/ +/TBt2dzhauH8XwIDAQABo4IBGjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of +1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczovL3d3d3cuY2Vy +dGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilodHRwOi8vY3JsLmNlcnRpZ25h +LmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYraHR0cDovL2NybC5kaGlteW90aXMuY29tL2Nl +cnRpZ25hcm9vdGNhLmNybDANBgkqhkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOIt +OoldaDgvUSILSo3L6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxP +TGRGHVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH60BGM+RFq +7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncBlA2c5uk5jR+mUYyZDDl3 +4bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdio2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd +8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS +6Cvu5zHbugRqh5jnxV/vfaci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaY +tlu3zM63Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayhjWZS +aX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw3kAP+HwV96LOPNde +E4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= +-----END CERTIFICATE----- + +emSign Root CA - G1 +=================== +-----BEGIN CERTIFICATE----- +MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJJTjET +MBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRl +ZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBHMTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgx +ODMwMDBaMGcxCzAJBgNVBAYTAklOMRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVk +aHJhIFRlY2hub2xvZ2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQzf2N4aLTN +LnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO8oG0x5ZOrRkVUkr+PHB1 +cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aqd7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHW +DV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhMtTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ +6DqS0hdW5TUaQBw+jSztOd9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrH +hQIDAQABo0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQDAgEG +MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31xPaOfG1vR2vjTnGs2 +vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjMwiI/aTvFthUvozXGaCocV685743Q +NcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6dGNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q ++Mri/Tm3R7nrft8EI6/6nAYH6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeih +U80Bv2noWgbyRQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx +iN66zB+Afko= +-----END CERTIFICATE----- + +emSign ECC Root CA - G3 +======================= +-----BEGIN CERTIFICATE----- +MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQGEwJJTjETMBEG +A1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEg +MB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4 +MTgzMDAwWjBrMQswCQYDVQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11 +ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g +RzMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0WXTsuwYc +58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xySfvalY8L1X44uT6EYGQIr +MgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuBzhccLikenEhjQjAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+D +CBeQyh+KTOgNG3qxrdWBCUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7 +jHvrZQnD+JbNR6iC8hZVdyR+EhCVBCyj +-----END CERTIFICATE----- + +emSign Root CA - C1 +=================== +-----BEGIN CERTIFICATE----- +MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkGA1UEBhMCVVMx +EzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNp +Z24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAwMFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UE +BhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQD +ExNlbVNpZ24gUm9vdCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+up +ufGZBczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZHdPIWoU/ +Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH3DspVpNqs8FqOp099cGX +OFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvHGPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4V +I5b2P/AgNBbeCsbEBEV5f6f9vtKppa+cxSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleooms +lMuoaJuvimUnzYnu3Yy1aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+ +XJGFehiqTbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQAD +ggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87/kOXSTKZEhVb3xEp +/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4kqNPEjE2NuLe/gDEo2APJ62gsIq1 +NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrGYQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9 +wC68AivTxEDkigcxHpvOJpkT+xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQ +BmIMMMAVSKeoWXzhriKi4gp6D/piq1JM4fHfyr6DDUI= +-----END CERTIFICATE----- + +emSign ECC Root CA - C3 +======================= +-----BEGIN CERTIFICATE----- +MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQGEwJVUzETMBEG +A1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMxIDAeBgNVBAMTF2VtU2lnbiBF +Q0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAwMFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UE +BhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQD +ExdlbVNpZ24gRUNDIFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd +6bciMK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4OjavtisIGJAnB9 +SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0OBBYEFPtaSNCAIEDyqOkA +B2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMDA2gA +MGUCMQC02C8Cif22TGK6Q04ThHK1rt0c3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwU +ZOR8loMRnLDRWmFLpg9J0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ== +-----END CERTIFICATE----- + +Hongkong Post Root CA 3 +======================= +-----BEGIN CERTIFICATE----- +MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQELBQAwbzELMAkG +A1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJSG9uZyBLb25nMRYwFAYDVQQK +Ew1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25na29uZyBQb3N0IFJvb3QgQ0EgMzAeFw0xNzA2 +MDMwMjI5NDZaFw00MjA2MDMwMjI5NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtv +bmcxEjAQBgNVBAcTCUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMX +SG9uZ2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz +iNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy51BWy7sFOdem1p+/l6TWZ5Mwc50tf +jTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mIVoBc+L0sPOFMV4i707mV78vH9toxdCim +5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV9WTRYA6ziUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOe +sL4jpNrcyCse2m5FHomY2vkALgbpDDtw1VAliJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj +0mRiikKYvLTGCAj4/ahMZJx2Ab0vqWwzD9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+TtbNe/ +JgERohYpSms0BpDsE9K2+2p20jzt8NYt3eEV7KObLyzJPivkaTv/ciWxNoZbx39ri1UbSsUgYT2u +y1DhCDq+sI9jQVMwCFk8mB13umOResoQUGC/8Ne8lYePl8X+l2oBlKN8W4UdKjk60FSh0Tlxnf0h ++bV78OLgAo9uliQlLKAeLKjEiafv7ZkGL7YKTE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsG +xVd7GYYKecsAyVKvQv83j+GjHno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwID +AQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0e +i9Y5K3DTXNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEwDQYJKoZIhvcN +AQELBQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG7BJ8dNVI0lkUmcDrudHr9Egw +W62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCkMpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6LWld +y8joRTnU+kLBEUx3XZL7av9YROXrgZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5ag0zD/ov ++BS5gLNdTaqX4fnkGMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8lMH8yfaIDlNDc +eqFS3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+knEwlqLJmOzj/2ZQw +9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+l6mc1X5VTMbeRRAc6uk7 +nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6cJfTzPV4e0hz5sy229zdcxsshTrD3mUcY +hcErulWuBurQB7Lcq9CClnXO0lD+mefPL5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB +60PZ2Pierc+xYw5F9KBaLJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fq +dBb9HxEGmpv0 +-----END CERTIFICATE----- + +Entrust Root Certification Authority - G4 +========================================= +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAwgb4xCzAJBgNV +BAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3Qu +bmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1 +dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eSAtIEc0MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDExNlowgb4xCzAJBgNVBAYT +AlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 +L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eSAtIEc0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3D +umSXbcr3DbVZwbPLqGgZ2K+EbTBwXX7zLtJTmeH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV +3imz/f3ET+iq4qA7ec2/a0My3dl0ELn39GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j5pds +8ELl3FFLFUHtSUrJ3hCX1nbB76W1NhSXNdh4IjVS70O92yfbYVaCNNzLiGAMC1rlLAHGVK/XqsEQ +e9IFWrhAnoanw5CGAlZSCXqc0ieCU0plUmr1POeo8pyvi73TDtTUXm6Hnmo9RR3RXRv06QqsYJn7 +ibT/mCzPfB3pAqoEmh643IhuJbNsZvc8kPNXwbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5X +xNMhIWNlUpEbsZmOeX7m640A2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV +7rtNOzK+mndmnqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8 +dWbrAuMINClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwlN4y6mACXi0mW +Hv0liqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNjc0kCAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJ84xFYjwznooHFs6FRM5Og6sb9n +MA0GCSqGSIb3DQEBCwUAA4ICAQAS5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ9POrYs4Q +jbRaZIxowLByQzTSGwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5ZDIBf9PD3Vht +7LGrhFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0gkLpHZPt/B7NTeLUK +YvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uIAeV8KEsD+UmDfLJ/fOPt +jqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbwH5Lk6rWS02FREAutp9lfx1/cH6NcjKF+ +m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+b7DUUH8i119lAg2m9IUe2K4GS0qn0jFmwvjO5QimpAKW +RGhXxNUzzxkvFMSUHHuk2fCfDrGA4tGeEWSpiBE6doLlYsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjA +JOgc47OlIQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk5F6G ++TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuYn/PIjhs4ViFqUZPT +kcpG2om3PVODLAgfi49T3f+sHw== +-----END CERTIFICATE----- + +Microsoft ECC Root Certificate Authority 2017 +============================================= +-----BEGIN CERTIFICATE----- +MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV +UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3NvZnQgRUND +IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4 +MjMxNjA0WjBlMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw +NAYDVQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQ +BgcqhkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZRogPZnZH6 +thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYbhGBKia/teQ87zvH2RPUB +eMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTIy5lycFIM ++Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlf +Xu5gKcs68tvWMoQZP3zVL8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaR +eNtUjGUBiudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M= +-----END CERTIFICATE----- + +Microsoft RSA Root Certificate Authority 2017 +============================================= +-----BEGIN CERTIFICATE----- +MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBlMQswCQYDVQQG +EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3NvZnQg +UlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIw +NzE4MjMwMDIzWjBlMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u +MTYwNAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZNt9GkMml +7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0ZdDMbRnMlfl7rEqUrQ7e +S0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw7 +1VdyvD/IybLeS2v4I2wDwAW9lcfNcztmgGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+ +dkC0zVJhUXAoP8XFWvLJjEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49F +yGcohJUcaDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaGYaRS +MLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6W6IYZVcSn2i51BVr +lMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4KUGsTuqwPN1q3ErWQgR5WrlcihtnJ +0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH+FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJ +ClTUFLkqqNfs+avNJVgyeY+QW5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZCLgLNFgVZJ8og +6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OCgMNPOsduET/m4xaRhPtthH80 +dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk ++ONVFT24bcMKpBLBaYVu32TxU5nhSnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex +/2kskZGT4d9Mozd2TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDy +AmH3pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGRxpl/j8nW +ZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiAppGWSZI1b7rCoucL5mxAyE +7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKT +c0QWbej09+CVgI+WXTik9KveCjCHk9hNAHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D +5KbvtwEwXlGjefVwaaZBRA+GsCyRxj3qrg+E +-----END CERTIFICATE----- + +e-Szigno Root CA 2017 +===================== +-----BEGIN CERTIFICATE----- +MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNVBAYTAkhVMREw +DwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRkLjEXMBUGA1UEYQwOVkFUSFUt +MjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJvb3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZa +Fw00MjA4MjIxMjA3MDZaMHExCzAJBgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UE +CgwNTWljcm9zZWMgTHRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3pp +Z25vIFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtvxie+RJCx +s1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+HWyx7xf58etqjYzBhMA8G +A1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSHERUI0arBeAyxr87GyZDv +vzAEwDAfBgNVHSMEGDAWgBSHERUI0arBeAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEA +tVfd14pVCzbhhkT61NlojbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxO +svxyqltZ+efcMQ== +-----END CERTIFICATE----- + +certSIGN Root CA G2 +=================== +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNVBAYTAlJPMRQw +EgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04gUk9PVCBDQSBHMjAeFw0xNzAy +MDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJBgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lH +TiBTQTEcMBoGA1UECxMTY2VydFNJR04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAMDFdRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05 +N0IwvlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZuIt4Imfk +abBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhpn+Sc8CnTXPnGFiWeI8Mg +wT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKscpc/I1mbySKEwQdPzH/iV8oScLumZfNp +dWO9lfsbl83kqK/20U6o2YpxJM02PbyWxPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91Qqh +ngLjYl/rNUssuHLoPj1PrCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732 +jcZZroiFDsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fxDTvf +95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgyLcsUDFDYg2WD7rlc +z8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6CeWRgKRM+o/1Pcmqr4tTluCRVLERL +iohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1Ud +DgQWBBSCIS1mxteg4BXrzkwJd8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOB +ywaK8SJJ6ejqkX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC +b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQlqiCA2ClV9+BB +/AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0OJD7uNGzcgbJceaBxXntC6Z5 +8hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+cNywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5 +BiKDUyUM/FHE5r7iOZULJK2v0ZXkltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklW +atKcsWMy5WHgUyIOpwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tU +Sxfj03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZkPuXaTH4M +NMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE1LlSVHJ7liXMvGnjSG4N +0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MXQRBdJ3NghVdJIgc= +-----END CERTIFICATE----- + +Trustwave Global Certification Authority +======================================== +-----BEGIN CERTIFICATE----- +MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJV +UzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2 +ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTAeFw0xNzA4MjMxOTM0MTJaFw00MjA4MjMxOTM0MTJaMIGIMQswCQYDVQQGEwJV +UzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2 +ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALldUShLPDeS0YLOvR29 +zd24q88KPuFd5dyqCblXAj7mY2Hf8g+CY66j96xz0XznswuvCAAJWX/NKSqIk4cXGIDtiLK0thAf +LdZfVaITXdHG6wZWiYj+rDKd/VzDBcdu7oaJuogDnXIhhpCujwOl3J+IKMujkkkP7NAP4m1ET4Bq +stTnoApTAbqOl5F2brz81Ws25kCI1nsvXwXoLG0R8+eyvpJETNKXpP7ScoFDB5zpET71ixpZfR9o +WN0EACyW80OzfpgZdNmcc9kYvkHHNHnZ9GLCQ7mzJ7Aiy/k9UscwR7PJPrhq4ufogXBeQotPJqX+ +OsIgbrv4Fo7NDKm0G2x2EOFYeUY+VM6AqFcJNykbmROPDMjWLBz7BegIlT1lRtzuzWniTY+HKE40 +Cz7PFNm73bZQmq131BnW2hqIyE4bJ3XYsgjxroMwuREOzYfwhI0Vcnyh78zyiGG69Gm7DIwLdVcE +uE4qFC49DxweMqZiNu5m4iK4BUBjECLzMx10coos9TkpoNPnG4CELcU9402x/RpvumUHO1jsQkUm ++9jaJXLE9gCxInm943xZYkqcBW89zubWR2OZxiRvchLIrH+QtAuRcOi35hYQcRfO3gZPSEF9NUqj +ifLJS3tBEW1ntwiYTOURGa5CgNz7kAXU+FDKvuStx8KU1xad5hePrzb7AgMBAAGjQjBAMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFJngGWcNYtt2s9o9uFvo/ULSMQ6HMA4GA1UdDwEB/wQEAwIB +BjANBgkqhkiG9w0BAQsFAAOCAgEAmHNw4rDT7TnsTGDZqRKGFx6W0OhUKDtkLSGm+J1WE2pIPU/H +PinbbViDVD2HfSMF1OQc3Og4ZYbFdada2zUFvXfeuyk3QAUHw5RSn8pk3fEbK9xGChACMf1KaA0H +ZJDmHvUqoai7PF35owgLEQzxPy0QlG/+4jSHg9bP5Rs1bdID4bANqKCqRieCNqcVtgimQlRXtpla +4gt5kNdXElE1GYhBaCXUNxeEFfsBctyV3lImIJgm4nb1J2/6ADtKYdkNy1GTKv0WBpanI5ojSP5R +vbbEsLFUzt5sQa0WZ37b/TjNuThOssFgy50X31ieemKyJo90lZvkWx3SD92YHJtZuSPTMaCm/zjd +zyBP6VhWOmfD0faZmZ26NraAL4hHT4a/RDqA5Dccprrql5gR0IRiR2Qequ5AvzSxnI9O4fKSTx+O +856X3vOmeWqJcU9LJxdI/uz0UA9PSX3MReO9ekDFQdxhVicGaeVyQYHTtgGJoC86cnn+OjC/QezH +Yj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8h6jCJ3zhM0EPz8/8AKAigJ5Kp28AsEFFtyLKaEjFQqKu +3R3y4G5OBVixwJAWKqQ9EEC+j2Jjg6mcgn0tAumDMHzLJ8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP +29FpHOTKyeC2nOnOcXHebD8WpHk= +-----END CERTIFICATE----- + +Trustwave Global ECC P256 Certification Authority +================================================= +-----BEGIN CERTIFICATE----- +MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYDVQQGEwJVUzER +MA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0d2F2ZSBI +b2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM1MTBaFw00MjA4MjMxOTM1MTBaMIGRMQswCQYD +VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRy +dXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDI1 +NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABH77bOYj +43MyCMpg5lOcunSNGLB4kFKA3TjASh3RqMyTpJcGOMoNFWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqm +P62jQzBBMA8GA1UdEwEB/wQFMAMBAf8wDwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt +0UrrdaVKEJmzsaGLSvcwCgYIKoZIzj0EAwIDRwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjz +RM4q3wghDDcCIC0mA6AFvWvR9lz4ZcyGbbOcNEhjhAnFjXca4syc4XR7 +-----END CERTIFICATE----- + +Trustwave Global ECC P384 Certification Authority +================================================= +-----BEGIN CERTIFICATE----- +MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYDVQQGEwJVUzER +MA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0d2F2ZSBI +b2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM2NDNaFw00MjA4MjMxOTM2NDNaMIGRMQswCQYD +VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRy +dXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDM4 +NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTB2MBAGByqGSM49AgEGBSuBBAAiA2IABGvaDXU1CDFH +Ba5FmVXxERMuSvgQMSOjfoPTfygIOiYaOs+Xgh+AtycJj9GOMMQKmw6sWASr9zZ9lCOkmwqKi6vr +/TklZvFe/oyujUF5nQlgziip04pt89ZF1PKYhDhloKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNV +HQ8BAf8EBQMDBwYAMB0GA1UdDgQWBBRVqYSJ0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNn +ADBkAjA3AZKXRRJ+oPM+rRk6ct30UJMDEr5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsCMGcl +CrEMXu6pY5Jv5ZAL/mYiykf9ijH3g/56vxC+GCsej/YpHpRZ744hN8tRmKVuSw== +-----END CERTIFICATE----- + +NAVER Global Root Certification Authority +========================================= +-----BEGIN CERTIFICATE----- +MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEMBQAwaTELMAkG +A1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRGT1JNIENvcnAuMTIwMAYDVQQD +DClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MTgwODU4 +NDJaFw0zNzA4MTgyMzU5NTlaMGkxCzAJBgNVBAYTAktSMSYwJAYDVQQKDB1OQVZFUiBCVVNJTkVT +UyBQTEFURk9STSBDb3JwLjEyMDAGA1UEAwwpTkFWRVIgR2xvYmFsIFJvb3QgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC21PGTXLVAiQqrDZBb +UGOukJR0F0Vy1ntlWilLp1agS7gvQnXp2XskWjFlqxcX0TM62RHcQDaH38dq6SZeWYp34+hInDEW ++j6RscrJo+KfziFTowI2MMtSAuXaMl3Dxeb57hHHi8lEHoSTGEq0n+USZGnQJoViAbbJAh2+g1G7 +XNr4rRVqmfeSVPc0W+m/6imBEtRTkZazkVrd/pBzKPswRrXKCAfHcXLJZtM0l/aM9BhK4dA9WkW2 +aacp+yPOiNgSnABIqKYPszuSjXEOdMWLyEz59JuOuDxp7W87UC9Y7cSw0BwbagzivESq2M0UXZR4 +Yb8ObtoqvC8MC3GmsxY/nOb5zJ9TNeIDoKAYv7vxvvTWjIcNQvcGufFt7QSUqP620wbGQGHfnZ3z +VHbOUzoBppJB7ASjjw2i1QnK1sua8e9DXcCrpUHPXFNwcMmIpi3Ua2FzUCaGYQ5fG8Ir4ozVu53B +A0K6lNpfqbDKzE0K70dpAy8i+/Eozr9dUGWokG2zdLAIx6yo0es+nPxdGoMuK8u180SdOqcXYZai +cdNwlhVNt0xz7hlcxVs+Qf6sdWA7G2POAN3aCJBitOUt7kinaxeZVL6HSuOpXgRM6xBtVNbv8ejy +YhbLgGvtPe31HzClrkvJE+2KAQHJuFFYwGY6sWZLxNUxAmLpdIQM201GLQIDAQABo0IwQDAdBgNV +HQ4EFgQU0p+I36HNLL3s9TsBAZMzJ7LrYEswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB +Af8wDQYJKoZIhvcNAQEMBQADggIBADLKgLOdPVQG3dLSLvCkASELZ0jKbY7gyKoNqo0hV4/GPnrK +21HUUrPUloSlWGB/5QuOH/XcChWB5Tu2tyIvCZwTFrFsDDUIbatjcu3cvuzHV+YwIHHW1xDBE1UB +jCpD5EHxzzp6U5LOogMFDTjfArsQLtk70pt6wKGm+LUx5vR1yblTmXVHIloUFcd4G7ad6Qz4G3bx +hYTeodoS76TiEJd6eN4MUZeoIUCLhr0N8F5OSza7OyAfikJW4Qsav3vQIkMsRIz75Sq0bBwcupTg +E34h5prCy8VCZLQelHsIJchxzIdFV4XTnyliIoNRlwAYl3dqmJLJfGBs32x9SuRwTMKeuB330DTH +D8z7p/8Dvq1wkNoL3chtl1+afwkyQf3NosxabUzyqkn+Zvjp2DXrDige7kgvOtB5CTh8piKCk5XQ +A76+AqAF3SAi428diDRgxuYKuQl1C/AH6GmWNcf7I4GOODm4RStDeKLRLBT/DShycpWbXgnbiUSY +qqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX5Ie3cdCh13cV1ELX8vMxmV2b3RZtP+oG +I/hGoiLtk/bdmuYqh7GYVPEi92tF4+KOdh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmg +kpzNNIaRkPpkUZ3+/uul9XXeifdy +-----END CERTIFICATE----- + +AC RAIZ FNMT-RCM SERVIDORES SEGUROS +=================================== +-----BEGIN CERTIFICATE----- +MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQswCQYDVQQGEwJF +UzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgwFgYDVQRhDA9WQVRFUy1RMjgy +NjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1SQ00gU0VSVklET1JFUyBTRUdVUk9TMB4XDTE4 +MTIyMDA5MzczM1oXDTQzMTIyMDA5MzczM1oweDELMAkGA1UEBhMCRVMxETAPBgNVBAoMCEZOTVQt +UkNNMQ4wDAYDVQQLDAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYwMDRKMSwwKgYDVQQDDCNB +QyBSQUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAGByqGSM49AgEGBSuBBAAiA2IA +BPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKTRBdp20LHsbI6GA60XYyzZl2hNPk2 +LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oKUm8BA06Oi6NCMEAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqG +SM49BAMDA2kAMGYCMQCuSuMrQMN0EfKVrRYj3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoD +zBOQn5ICMQD6SmxgiHPz7riYYqnOK8LZiqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJyv+c= +-----END CERTIFICATE----- + +GlobalSign Root R46 +=================== +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUAMEYxCzAJBgNV +BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQDExNHbG9iYWxTaWduIFJv +b3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAX +BgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08Es +CVeJOaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQGvGIFAha/ +r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud316HCkD7rRlr+/fKYIje +2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo0q3v84RLHIf8E6M6cqJaESvWJ3En7YEt +bWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSEy132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvj +K8Cd+RTyG/FWaha/LIWFzXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD4 +12lPFzYE+cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCNI/on +ccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzsx2sZy/N78CsHpdls +eVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqaByFrgY/bxFn63iLABJzjqls2k+g9 +vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEM +BQADggIBAHx47PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg +JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti2kM3S+LGteWy +gxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIkpnnpHs6i58FZFZ8d4kuaPp92 +CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRFFRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZm +OUdkLG5NrmJ7v2B0GbhWrJKsFjLtrWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qq +JZ4d16GLuc1CLgSkZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwye +qiv5u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP4vkYxboz +nxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6N3ec592kD3ZDZopD8p/7 +DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3vouXsXgxT7PntgMTzlSdriVZzH81Xwj3 +QEUxeCp6 +-----END CERTIFICATE----- + +GlobalSign Root E46 +=================== +-----BEGIN CERTIFICATE----- +MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYxCzAJBgNVBAYT +AkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQDExNHbG9iYWxTaWduIFJvb3Qg +RTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNV +BAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkB +jtjqR+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGddyXqBPCCj +QjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQxCpCPtsad0kRL +gLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZk +vLtoURMMA/cVi4RguYv/Uo7njLwcAjA8+RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+ +CAezNIm8BZ/3Hobui3A= +-----END CERTIFICATE----- diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/task.rb b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/task.rb new file mode 100644 index 0000000..33a5999 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/task.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +require "rake" +require "rake/tasklib" +require "github_changelog_generator" + +module GitHubChangelogGenerator + class RakeTask < ::Rake::TaskLib + include ::Rake::DSL if defined?(::Rake::DSL) + + OPTIONS = %w[ user project token date_format output + bug_prefix enhancement_prefix issue_prefix + header merge_prefix issues + add_issues_wo_labels add_pr_wo_labels + pulls filter_issues_by_milestone author + unreleased_only unreleased unreleased_label + compare_link include_labels exclude_labels + bug_labels enhancement_labels include_tags_regex + between_tags exclude_tags exclude_tags_regex since_tag max_issues + github_site github_endpoint simple_list + future_release release_branch verbose release_url + base configure_sections add_sections http_cache] + + OPTIONS.each do |o| + attr_accessor o.to_sym + end + + # Public: Initialise a new GitHubChangelogGenerator::RakeTask. + # + # Example + # + # GitHubChangelogGenerator::RakeTask.new + def initialize(*args, &task_block) + super() + @name = args.shift || :changelog + + define(args, &task_block) + end + + def define(args, &task_block) + desc "Generate a Changelog from GitHub" + + yield(*[self, args].slice(0, task_block.arity)) if task_block + + # clear any (auto-)pre-existing task + Rake::Task[@name].clear if Rake::Task.task_defined?(@name) + + task @name do + # mimick parse_options + options = Parser.default_options + + OPTIONS.each do |o| + v = instance_variable_get("@#{o}") + options[o.to_sym] = v unless v.nil? + end + abort "user and project are required." unless options[:user] && options[:project] + generator = Generator.new options + + log = generator.compound_changelog + + output_filename = (options[:output]).to_s + File.open(output_filename, "w") { |file| file.write(log) } + puts "Done!" + puts "Generated log placed in #{Dir.pwd}/#{output_filename}" + end + end + end +end diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/version.rb b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/version.rb new file mode 100644 index 0000000..4beb8ad --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/lib/github_changelog_generator/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module GitHubChangelogGenerator + VERSION = "1.16.4" +end diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/man/git-generate-changelog.1 b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/man/git-generate-changelog.1 new file mode 100644 index 0000000..c7ec321 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/man/git-generate-changelog.1 @@ -0,0 +1,393 @@ +.\" generated with Ronn/v0.7.3 +.\" http://github.com/rtomayko/ronn/tree/0.7.3 +. +.TH "GIT\-GENERATE\-CHANGELOG" "1" "May 2020" "" "" +. +.SH "NAME" +\fBgit\-generate\-changelog\fR \- Generate changelog from GitHub +. +.SH "SYNOPSIS" +\fBgit generate\-changelog\fR [\-h|\-\-help] [\-u|\-\-user] [\-p|\-\-project] +. +.SH "DESCRIPTION" +Automatically generate changelog from your tags, issues, labels and pull requests on GitHub\. +. +.SH "OPTIONS" +\-u, \-\-user USER +. +.P +Username of the owner of target GitHub repo\. +. +.P +\-p, \-\-project PROJECT +. +.P +Name of project on GitHub\. +. +.P +\-t, \-\-token TOKEN +. +.P +To make more than 50 requests per hour your GitHub token is required\. You can generate it at: https://github\.com/settings/tokens/new +. +.P +\-f, \-\-date\-format FORMAT +. +.P +Date format\. Default is %Y\-%m\-%d\. +. +.P +\-o, \-\-output NAME +. +.P +Output file\. To print to STDOUT instead, use blank as path\. Default is CHANGELOG\.md\. +. +.P +\-b, \-\-base NAME +. +.P +Optional base file to append generated changes to\. Default is HISTORY\.md\. +. +.P +\-\-summary\-label LABEL +. +.P +Set up custom label for the release summary section\. Default is ""\. +. +.P +\-\-breaking\-label LABEL +. +.P +Set up custom label for breaking changes section\. Default is "\fBBreaking changes:\fR"\. +. +.P +\-\-enhancement\-label LABEL +. +.P +Set up custom label for enhancements section\. Default is "\fBImplemented enhancements:\fR"\. +. +.P +\-\-bugs\-label LABEL +. +.P +Set up custom label for bug\-fixes section\. Default is "\fBFixed bugs:\fR"\. +. +.P +\-\-deprecated\-label LABEL +. +.P +Set up custom label for deprecated section\. Default is "\fBDeprecated:\fR"\. +. +.P +\-\-removed\-label LABEL +. +.P +Set up custom label for removed section\. Default is "\fBRemoved:\fR"\. +. +.P +\-\-security\-label LABEL +. +.P +Set up custom label for security section\. Default is "\fBSecurity:\fR"\. +. +.P +\-\-issues\-label LABEL +. +.P +Set up custom label for closed\-issues section\. Default is "\fBClosed issues:\fR"\. +. +.P +\-\-header\-label LABEL +. +.P +Set up custom header label\. Default is "# Changelog"\. +. +.P +\-\-configure\-sections HASH, STRING +. +.P +Define your own set of sections which overrides all default sections\. +. +.P +\-\-add\-sections HASH, STRING +. +.P +Add new sections but keep the default sections\. +. +.P +\-\-front\-matter JSON +. +.P +Add YAML front matter\. Formatted as JSON because it\'s easier to add on the command line\. +. +.P +\-\-pr\-label LABEL +. +.P +Set up custom label for pull requests section\. Default is "\fBMerged pull requests:\fR"\. +. +.P +\-\-[no\-]issues +. +.P +Include closed issues in changelog\. Default is true\. +. +.P +\-\-[no\-]issues\-wo\-labels +. +.P +Include closed issues without labels in changelog\. Default is true\. +. +.P +\-\-[no\-]pr\-wo\-labels +. +.P +Include pull requests without labels in changelog\. Default is true\. +. +.P +\-\-[no\-]pull\-requests +. +.P +Include pull\-requests in changelog\. Default is true\. +. +.P +\-\-[no\-]filter\-by\-milestone +. +.P +Use milestone to detect when issue was resolved\. Default is true\. +. +.P +\-\-[no\-]issues\-of\-open\-milestones +. +.P +Include issues of open milestones\. Default is true\. +. +.P +\-\-[no\-]author +. +.P +Add author of pull request at the end\. Default is true\. +. +.P +\-\-usernames\-as\-github\-logins +. +.P +Use GitHub tags instead of Markdown links for the author of an issue or pull\-request\. +. +.P +\-\-unreleased\-only +. +.P +Generate log from unreleased closed issues only\. +. +.P +\-\-[no\-]unreleased +. +.P +Add to log unreleased closed issues\. Default is true\. +. +.P +\-\-unreleased\-label LABEL +. +.P +Set up custom label for unreleased closed issues section\. Default is "\fBUnreleased:\fR"\. +. +.P +\-\-[no\-]compare\-link +. +.P +Include compare link (Full Changelog) between older version and newer version\. Default is true\. +. +.P +\-\-include\-labels x,y,z +. +.P +Of the labeled issues, only include the ones with the specified labels\. +. +.P +\-\-exclude\-labels x,y,z +. +.P +Issues with the specified labels will be excluded from changelog\. Default is \'duplicate,question,invalid,wontfix\'\. +. +.P +\-\-summary\-labels x,y,z +. +.P +Issues with these labels will be added to a new section, called "Release Summary"\. The section display only body of issues\. Default is \'Release summary,release\-summary,Summary,summary\'\. +. +.P +\-\-breaking\-labels x,y,z +. +.P +Issues with these labels will be added to a new section, called "Breaking changes"\. Default is \'backwards\-incompatible,breaking\'\. +. +.P +\-\-enhancement\-labels x,y,z +. +.P +Issues with the specified labels will be added to "Implemented enhancements" section\. Default is \'enhancement,Enhancement\'\. +. +.P +\-\-bug\-labels x,y,z +. +.P +Issues with the specified labels will be added to "Fixed bugs" section\. Default is \'bug,Bug\'\. +. +.P +\-\-deprecated\-labels x,y,z +. +.P +Issues with the specified labels will be added to a section called "Deprecated"\. Default is \'deprecated,Deprecated\'\. +. +.P +\-\-removed\-labels x,y,z +. +.P +Issues with the specified labels will be added to a section called "Removed"\. Default is \'removed,Removed\'\. +. +.P +\-\-security\-labels x,y,z +. +.P +Issues with the specified labels will be added to a section called "Security fixes"\. Default is \'security,Security\'\. +. +.P +\-\-issue\-line\-labels x,y,z +. +.P +The specified labels will be shown in brackets next to each matching issue\. Use "ALL" to show all labels\. Default is []\. +. +.P +\-\-exclude\-tags x,y,z +. +.P +Changelog will exclude specified tags\. +. +.P +\-\-exclude\-tags\-regex REGEX +. +.P +Apply a regular expression on tag names so that they can be excluded, for example: \-\-exclude\-tags\-regex "\.*+\ed{1,}"\. +. +.P +\-\-since\-tag x +. +.P +Changelog will start after specified tag\. +. +.P +\-\-due\-tag x +. +.P +Changelog will end before specified tag\. +. +.P +\-\-since\-commit x +. +.P +Fetch only commits after this time\. eg\. "2017\-01\-01 10:00:00" +. +.P +\-\-max\-issues NUMBER +. +.P +Maximum number of issues to fetch from GitHub\. Default is unlimited\. +. +.P +\-\-release\-url URL +. +.P +The URL to point to for release links, in printf format (with the tag as variable)\. +. +.P +\-\-github\-site URL +. +.P +The Enterprise GitHub site where your project is hosted\. +. +.P +\-\-github\-api URL +. +.P +The enterprise endpoint to use for your GitHub API\. +. +.P +\-\-simple\-list +. +.P +Create a simple list from issues and pull requests\. Default is false\. +. +.P +\-\-future\-release RELEASE\-VERSION +. +.P +Put the unreleased changes in the specified release number\. +. +.P +\-\-release\-branch RELEASE\-BRANCH +. +.P +Limit pull requests to the release branch, such as master or release\. +. +.P +\-\-http\-cache +. +.P +Use HTTP Cache to cache GitHub API requests (useful for large repos)\. Default is true\. +. +.P +\-\-[no\-]cache\-file CACHE\-FILE +. +.P +Filename to use for cache\. Default is github\-changelog\-http\-cache in a temporary directory\. +. +.P +\-\-cache\-log CACHE\-LOG +. +.P +Filename to use for cache log\. Default is github\-changelog\-logger\.log in a temporary directory\. +. +.P +\-\-ssl\-ca\-file PATH +. +.P +Path to cacert\.pem file\. Default is a bundled lib/github_changelog_generator/ssl_certs/cacert\.pem\. Respects SSL_CA_PATH\. +. +.P +\-\-require file1\.rb,file2\.rb +. +.P +Paths to Ruby file(s) to require before generating changelog\. +. +.P +\-\-[no\-]verbose +. +.P +Run verbosely\. Default is true\. +. +.P +\-v, \-\-version +. +.P +Print version number\. +. +.P +\-h, \-\-help +. +.P +Displays Help\. +. +.SH "REBASED COMMITS" +GitHub pull requests that have been merged whose merge commit SHA has been modified through rebasing, cherry picking, or some other method may be tracked via a special comment on GitHub\. Git commit SHAs found in comments on pull requests matching the regular expression \fB/rebased commit: ([0\-9a\-f]{40})/i\fR will be used in place of the original merge SHA when being added to the changelog\. +. +.SH "EXAMPLES" +. +.SH "AUTHOR" +Written by Petr Korolev sky4winder@gmail\.com +. +.SH "REPORTING BUGS" +<\fIhttps://github\.com/github\-changelog\-generator/github\-changelog\-generator/issues\fR> +. +.SH "SEE ALSO" +<\fIhttps://github\.com/github\-changelog\-generator/github\-changelog\-generator/\fR> diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/man/git-generate-changelog.1.html b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/man/git-generate-changelog.1.html new file mode 100644 index 0000000..ab97d37 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/man/git-generate-changelog.1.html @@ -0,0 +1,359 @@ + + + + + + git-generate-changelog(1) - Generate changelog from GitHub + + + + +
+ + + +
    +
  1. git-generate-changelog(1)
  2. +
  3. +
  4. git-generate-changelog(1)
  5. +
+ +

NAME

+

+ git-generate-changelog - Generate changelog from GitHub +

+ +

SYNOPSIS

+ +

git generate-changelog [-h|--help] [-u|--user] [-p|--project]

+ +

DESCRIPTION

+ +

Automatically generate changelog from your tags, issues, labels and pull requests on GitHub.

+ +

OPTIONS

+ +

-u, --user USER

+ +

Username of the owner of target GitHub repo.

+ +

-p, --project PROJECT

+ +

Name of project on GitHub.

+ +

-t, --token TOKEN

+ +

To make more than 50 requests per hour your GitHub token is required. You can generate it at: https://github.com/settings/tokens/new

+ +

-f, --date-format FORMAT

+ +

Date format. Default is %Y-%m-%d.

+ +

-o, --output NAME

+ +

Output file. To print to STDOUT instead, use blank as path. Default is CHANGELOG.md.

+ +

-b, --base NAME

+ +

Optional base file to append generated changes to.

+ +

--summary-label LABEL

+ +

Set up custom label for the release summary section. Default is "".

+ +

--breaking-label LABEL

+ +

Set up custom label for breaking changes section. Default is "Breaking changes:".

+ +

--enhancement-label LABEL

+ +

Set up custom label for enhancements section. Default is "Implemented enhancements:".

+ +

--bugs-label LABEL

+ +

Set up custom label for bug-fixes section. Default is "Fixed bugs:".

+ +

--deprecated-label LABEL

+ +

Set up custom label for deprecated section. Default is "Deprecated:".

+ +

--removed-label LABEL

+ +

Set up custom label for removed section. Default is "Removed:".

+ +

--security-label LABEL

+ +

Set up custom label for security section. Default is "Security:".

+ +

--issues-label LABEL

+ +

Set up custom label for closed-issues section. Default is "Closed issues:".

+ +

--header-label LABEL

+ +

Set up custom header label. Default is "# Changelog".

+ +

--configure-sections HASH, STRING

+ +

Define your own set of sections which overrides all default sections.

+ +

--add-sections HASH, STRING

+ +

Add new sections but keep the default sections.

+ +

--front-matter JSON

+ +

Add YAML front matter. Formatted as JSON because it's easier to add on the command line.

+ +

--pr-label LABEL

+ +

Set up custom label for pull requests section. Default is "Merged pull requests:".

+ +

--[no-]issues

+ +

Include closed issues in changelog. Default is true.

+ +

--[no-]issues-wo-labels

+ +

Include closed issues without labels in changelog. Default is true.

+ +

--[no-]pr-wo-labels

+ +

Include pull requests without labels in changelog. Default is true.

+ +

--[no-]pull-requests

+ +

Include pull-requests in changelog. Default is true.

+ +

--[no-]filter-by-milestone

+ +

Use milestone to detect when issue was resolved. Default is true.

+ +

--[no-]issues-of-open-milestones

+ +

Include issues of open milestones. Default is true.

+ +

--[no-]author

+ +

Add author of pull request at the end. Default is true.

+ +

--usernames-as-github-logins

+ +

Use GitHub tags instead of Markdown links for the author of an issue or pull-request.

+ +

--unreleased-only

+ +

Generate log from unreleased closed issues only.

+ +

--[no-]unreleased

+ +

Add to log unreleased closed issues. Default is true.

+ +

--unreleased-label LABEL

+ +

Set up custom label for unreleased closed issues section. Default is "Unreleased:".

+ +

--[no-]compare-link

+ +

Include compare link (Full Changelog) between older version and newer version. Default is true.

+ +

--include-labels x,y,z

+ +

Of the labeled issues, only include the ones with the specified labels.

+ +

--exclude-labels x,y,z

+ +

Issues with the specified labels will be excluded from changelog. Default is 'duplicate,question,invalid,wontfix'.

+ +

--summary-labels x,y,z

+ +

Issues with these labels will be added to a new section, called "Release Summary". The section display only body of issues. Default is 'Release summary,release-summary,Summary,summary'.

+ +

--breaking-labels x,y,z

+ +

Issues with these labels will be added to a new section, called "Breaking changes". Default is 'backwards-incompatible,breaking'.

+ +

--enhancement-labels x,y,z

+ +

Issues with the specified labels will be added to "Implemented enhancements" section. Default is 'enhancement,Enhancement'.

+ +

--bug-labels x,y,z

+ +

Issues with the specified labels will be added to "Fixed bugs" section. Default is 'bug,Bug'.

+ +

--deprecated-labels x,y,z

+ +

Issues with the specified labels will be added to a section called "Deprecated". Default is 'deprecated,Deprecated'.

+ +

--removed-labels x,y,z

+ +

Issues with the specified labels will be added to a section called "Removed". Default is 'removed,Removed'.

+ +

--security-labels x,y,z

+ +

Issues with the specified labels will be added to a section called "Security fixes". Default is 'security,Security'.

+ +

--issue-line-labels x,y,z

+ +

The specified labels will be shown in brackets next to each matching issue. Use "ALL" to show all labels. Default is [].

+ +

--exclude-tags x,y,z

+ +

Changelog will exclude specified tags.

+ +

--exclude-tags-regex REGEX

+ +

Apply a regular expression on tag names so that they can be excluded, for example: --exclude-tags-regex ".*+\d{1,}".

+ +

--since-tag x

+ +

Changelog will start after specified tag.

+ +

--due-tag x

+ +

Changelog will end before specified tag.

+ +

--since-commit x

+ +

Fetch only commits after this time. eg. "2017-01-01 10:00:00"

+ +

--max-issues NUMBER

+ +

Maximum number of issues to fetch from GitHub. Default is unlimited.

+ +

--release-url URL

+ +

The URL to point to for release links, in printf format (with the tag as variable).

+ +

--github-site URL

+ +

The Enterprise GitHub site where your project is hosted.

+ +

--github-api URL

+ +

The enterprise endpoint to use for your GitHub API.

+ +

--simple-list

+ +

Create a simple list from issues and pull requests. Default is false.

+ +

--future-release RELEASE-VERSION

+ +

Put the unreleased changes in the specified release number.

+ +

--release-branch RELEASE-BRANCH

+ +

Limit pull requests to the release branch, such as master or release.

+ +

--http-cache

+ +

Use HTTP Cache to cache GitHub API requests (useful for large repos). Default is true.

+ +

--[no-]cache-file CACHE-FILE

+ +

Filename to use for cache. Default is github-changelog-http-cache in a temporary directory.

+ +

--cache-log CACHE-LOG

+ +

Filename to use for cache log. Default is github-changelog-logger.log in a temporary directory.

+ +

--ssl-ca-file PATH

+ +

Path to cacert.pem file. Default is a bundled lib/github_changelog_generator/ssl_certs/cacert.pem. Respects SSL_CA_PATH.

+ +

--require file1.rb,file2.rb

+ +

Paths to Ruby file(s) to require before generating changelog.

+ +

--[no-]verbose

+ +

Run verbosely. Default is true.

+ +

-v, --version

+ +

Print version number.

+ +

-h, --help

+ +

Displays Help.

+ +

REBASED COMMITS

+ +

GitHub pull requests that have been merged whose merge commit SHA has been modified through rebasing, cherry picking, or some other method may be tracked via a special comment on GitHub. Git commit SHAs found in comments on pull requests matching the regular expression /rebased commit: ([0-9a-f]{40})/i will be used in place of the original merge SHA when being added to the changelog.

+ +

EXAMPLES

+ +

AUTHOR

+ +

Written by Petr Korolev sky4winder@gmail.com

+ +

REPORTING BUGS

+ +

<https://github.com/github-changelog-generator/github-changelog-generator/issues>

+ +

SEE ALSO

+ +

<https://github.com/github-changelog-generator/github-changelog-generator/>

+ + +
    +
  1. +
  2. May 2020
  3. +
  4. git-generate-changelog(1)
  5. +
+ +
+ + diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/man/git-generate-changelog.html b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/man/git-generate-changelog.html new file mode 100644 index 0000000..4e054ee --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/man/git-generate-changelog.html @@ -0,0 +1,270 @@ + + + + + + git-generate-changelog(1) - Generate changelog from github + + + + +
+ + + +
    +
  1. git-generate-changelog(1)
  2. +
  3. +
  4. git-generate-changelog(1)
  5. +
+ +

NAME

+

+ git-generate-changelog - Generate changelog from github +

+ +

SYNOPSIS

+ +

git generate-changelog [-h|--help] [-u|--user] [-p|--project]

+ +

DESCRIPTION

+ +

Automatically generate changelog from your tags, issues, labels and pull requests on GitHub.

+ +

OPTIONS

+ +

-u, --user USER

+ +

Username of the owner of target GitHub repo

+ +

-p, --project PROJECT

+ +

Name of project on GitHub

+ +

-t, --token TOKEN

+ +

To make more than 50 requests per hour your GitHub token is required. You can generate it at: https://github.com/settings/tokens/new

+ +

-f, --date-format FORMAT

+ +

Date format. Default is %Y-%m-%d

+ +

-o, --output NAME

+ +

Output file. Default is CHANGELOG.md

+ +

-b, --base NAME

+ +

Optional base file to append generated changes to.

+ +

--bugs-label LABEL

+ +

Setup custom label for bug-fixes section. Default is "Fixed bugs:

+ +

--enhancement-label LABEL

+ +

Setup custom label for enhancements section. Default is "Implemented enhancements:"

+ +

--issues-label LABEL

+ +

Setup custom label for closed-issues section. Default is "Closed issues:"

+ +

--header-label LABEL

+ +

Setup custom header label. Default is "# Changelog"

+ +

--pr-label LABEL

+ +

Setup custom label for pull requests section. Default is "Merged pull requests:"

+ +

--[no-]issues

+ +

Include closed issues in changelog. Default is true

+ +

--[no-]issues-wo-labels

+ +

Include closed issues without labels in changelog. Default is true

+ +

--[no-]pr-wo-labels

+ +

Include pull requests without labels in changelog. Default is true

+ +

--[no-]pull-requests

+ +

Include pull-requests in changelog. Default is true

+ +

--[no-]filter-by-milestone

+ +

Use milestone to detect when issue was resolved. Default is true

+ +

--[no-]author

+ +

Add author of pull-request in the end. Default is true

+ +

--unreleased-only

+ +

Generate log from unreleased closed issues only.

+ +

--[no-]unreleased

+ +

Add to log unreleased closed issues. Default is true

+ +

--unreleased-label LABEL

+ +

Add to log unreleased closed issues. Default is true

+ +

--[no-]compare-link

+ +

Include compare link (Full Changelog) between older version and newer version. Default is true

+ +

--include-labels x,y,z

+ +

Only issues with the specified labels will be included in the changelog.

+ +

--exclude-labels x,y,z

+ +

Issues with the specified labels will be always excluded from changelog. Default is 'duplicate,question,invalid,wontfix'

+ +

--bug-labels x,y,z

+ +

Issues with the specified labels will be always added to "Fixed bugs" section. Default is 'bug,Bug'

+ +

--enhancement-labels x,y,z

+ +

Issues with the specified labels will be always added to "Implemented enhancements" section. Default is 'enhancement,Enhancement'

+ +

--exclude-tags x,y,z

+ +

Changelog will exclude specified tags

+ +

--since-tag x

+ +

Changelog will start after specified tag

+ +

--due-tag x

+ +

Changelog will end before specified tag

+ +

--max-issues NUMBER

+ +

Max number of issues to fetch from GitHub. Default is unlimited

+ +

--release-url URL

+ +

The URL to point to for release links, in printf format (with the tag as variable).

+ +

--github-site URL

+ +

The Enterprise Github site on which your project is hosted.

+ +

--github-api URL

+ +

The enterprise endpoint to use for your Github API.

+ +

--simple-list

+ +

Create simple list from issues and pull requests. Default is false.

+ +

--future-release RELEASE-VERSION

+ +

Put the unreleased changes in the specified release number.

+ +

--configure-sections HASH, STRING

+ +

Define your own set of sections which overrides all default sections") do |v|

+ +

--add-sections HASH, STRING

+ +

Add new sections but keep the default sections"

+ +

--include-merged

+ +

If configure_sections is set, use this to restore the merged pull requests sections

+ +

--[no-]verbose

+ +

Run verbosely. Default is true

+ +

-v, --version

+ +

Print version number

+ +

-h, --help

+ +

Displays Help

+ +

EXAMPLES

+ +

AUTHOR

+ +

Written by Petr Korolev sky4winder@gmail.com

+ +

REPORTING BUGS

+ +

<https://github.com/github-changelog-generator/github-changelog-generator/issues>

+ +

SEE ALSO

+ +

<https://github.com/github-changelog-generator/github-changelog-generator/>

+ + +
    +
  1. +
  2. October 2015
  3. +
  4. git-generate-changelog(1)
  5. +
+ +
+ + diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/man/git-generate-changelog.md b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/man/git-generate-changelog.md new file mode 100644 index 0000000..2f4a4ec --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/man/git-generate-changelog.md @@ -0,0 +1,274 @@ +git-generate-changelog(1) - Generate changelog from GitHub +================================ + +## SYNOPSIS + +`git generate-changelog` [-h|--help] [-u|--user] [-p|--project] + +## DESCRIPTION + +Automatically generate changelog from your tags, issues, labels and pull requests on GitHub. + +## OPTIONS + + -u, --user USER + + Username of the owner of target GitHub repo. + + -p, --project PROJECT + + Name of project on GitHub. + + -t, --token TOKEN + + To make more than 50 requests per hour your GitHub token is required. You can generate it at: https://github.com/settings/tokens/new + + -f, --date-format FORMAT + + Date format. Default is %Y-%m-%d. + + -o, --output NAME + + Output file. To print to STDOUT instead, use blank as path. Default is CHANGELOG.md. + + -b, --base NAME + + Optional base file to append generated changes to. Default is HISTORY.md. + + --summary-label LABEL + + Set up custom label for the release summary section. Default is "". + + --breaking-label LABEL + + Set up custom label for breaking changes section. Default is "**Breaking changes:**". + + --enhancement-label LABEL + + Set up custom label for enhancements section. Default is "**Implemented enhancements:**". + + --bugs-label LABEL + + Set up custom label for bug-fixes section. Default is "**Fixed bugs:**". + + --deprecated-label LABEL + + Set up custom label for deprecated section. Default is "**Deprecated:**". + + --removed-label LABEL + + Set up custom label for removed section. Default is "**Removed:**". + + --security-label LABEL + + Set up custom label for security section. Default is "**Security:**". + + --issues-label LABEL + + Set up custom label for closed-issues section. Default is "**Closed issues:**". + + --header-label LABEL + + Set up custom header label. Default is "# Changelog". + + --configure-sections HASH, STRING + + Define your own set of sections which overrides all default sections. + + --add-sections HASH, STRING + + Add new sections but keep the default sections. + + --front-matter JSON + + Add YAML front matter. Formatted as JSON because it's easier to add on the command line. + + --pr-label LABEL + + Set up custom label for pull requests section. Default is "**Merged pull requests:**". + + --[no-]issues + + Include closed issues in changelog. Default is true. + + --[no-]issues-wo-labels + + Include closed issues without labels in changelog. Default is true. + + --[no-]pr-wo-labels + + Include pull requests without labels in changelog. Default is true. + + --[no-]pull-requests + + Include pull-requests in changelog. Default is true. + + --[no-]filter-by-milestone + + Use milestone to detect when issue was resolved. Default is true. + + --[no-]issues-of-open-milestones + + Include issues of open milestones. Default is true. + + --[no-]author + + Add author of pull request at the end. Default is true. + + --usernames-as-github-logins + + Use GitHub tags instead of Markdown links for the author of an issue or pull-request. + + --unreleased-only + + Generate log from unreleased closed issues only. + + --[no-]unreleased + + Add to log unreleased closed issues. Default is true. + + --unreleased-label LABEL + + Set up custom label for unreleased closed issues section. Default is "**Unreleased:**". + + --[no-]compare-link + + Include compare link (Full Changelog) between older version and newer version. Default is true. + + --include-labels x,y,z + + Of the labeled issues, only include the ones with the specified labels. + + --exclude-labels x,y,z + + Issues with the specified labels will be excluded from changelog. Default is 'duplicate,question,invalid,wontfix'. + + --summary-labels x,y,z + + Issues with these labels will be added to a new section, called "Release Summary". The section display only body of issues. Default is 'Release summary,release-summary,Summary,summary'. + + --breaking-labels x,y,z + + Issues with these labels will be added to a new section, called "Breaking changes". Default is 'backwards-incompatible,breaking'. + + --enhancement-labels x,y,z + + Issues with the specified labels will be added to "Implemented enhancements" section. Default is 'enhancement,Enhancement'. + + --bug-labels x,y,z + + Issues with the specified labels will be added to "Fixed bugs" section. Default is 'bug,Bug'. + + --deprecated-labels x,y,z + + Issues with the specified labels will be added to a section called "Deprecated". Default is 'deprecated,Deprecated'. + + --removed-labels x,y,z + + Issues with the specified labels will be added to a section called "Removed". Default is 'removed,Removed'. + + --security-labels x,y,z + + Issues with the specified labels will be added to a section called "Security fixes". Default is 'security,Security'. + + --issue-line-labels x,y,z + + The specified labels will be shown in brackets next to each matching issue. Use "ALL" to show all labels. Default is []. + + --exclude-tags x,y,z + + Changelog will exclude specified tags. + + --exclude-tags-regex REGEX + + Apply a regular expression on tag names so that they can be excluded, for example: --exclude-tags-regex ".*\+\d{1,}". + + --since-tag x + + Changelog will start after specified tag. + + --due-tag x + + Changelog will end before specified tag. + + --since-commit x + + Fetch only commits after this time. eg. "2017-01-01 10:00:00" + + --max-issues NUMBER + + Maximum number of issues to fetch from GitHub. Default is unlimited. + + --release-url URL + + The URL to point to for release links, in printf format (with the tag as variable). + + --github-site URL + + The Enterprise GitHub site where your project is hosted. + + --github-api URL + + The enterprise endpoint to use for your GitHub API. + + --simple-list + + Create a simple list from issues and pull requests. Default is false. + + --future-release RELEASE-VERSION + + Put the unreleased changes in the specified release number. + + --release-branch RELEASE-BRANCH + + Limit pull requests to the release branch, such as master or release. + + --http-cache + + Use HTTP Cache to cache GitHub API requests (useful for large repos). Default is true. + + --[no-]cache-file CACHE-FILE + + Filename to use for cache. Default is github-changelog-http-cache in a temporary directory. + + --cache-log CACHE-LOG + + Filename to use for cache log. Default is github-changelog-logger.log in a temporary directory. + + --ssl-ca-file PATH + + Path to cacert.pem file. Default is a bundled lib/github_changelog_generator/ssl_certs/cacert.pem. Respects SSL_CA_PATH. + + --require file1.rb,file2.rb + + Paths to Ruby file(s) to require before generating changelog. + + --[no-]verbose + + Run verbosely. Default is true. + + -v, --version + + Print version number. + + -h, --help + + Displays Help. + +## REBASED COMMITS + +GitHub pull requests that have been merged whose merge commit SHA has been modified through rebasing, cherry picking, or some other method may be tracked via a special comment on GitHub. Git commit SHAs found in comments on pull requests matching the regular expression `/rebased commit: ([0-9a-f]{40})/i` will be used in place of the original merge SHA when being added to the changelog. + +## EXAMPLES + +## AUTHOR + +Written by Petr Korolev sky4winder@gmail.com + +## REPORTING BUGS + +<> + +## SEE ALSO + +<> diff --git a/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/spec/files/angular.js.md b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/spec/files/angular.js.md new file mode 100644 index 0000000..898ef13 --- /dev/null +++ b/vendor/bundle/ruby/2.7.0/gems/github_changelog_generator-1.16.4/spec/files/angular.js.md @@ -0,0 +1,9395 @@ + +# 1.4.0-beta.6 cookie-liberation (2015-03-17) + + +## Bug Fixes + +- **$animate:** call `applyStyles` from options on `leave` + ([4374f892](https://github.com/angular/angular.js/commit/4374f892c6fa4af6ba1f2ed47c5f888fdb5fadc5), + [#10068](https://github.com/angular/angular.js/issues/10068)) +- **$browser:** don't crash if `history.state` access causes error in IE + ([3b8163b7](https://github.com/angular/angular.js/commit/3b8163b7b664f24499e75460ab50c066eaec0f78), + [#10367](https://github.com/angular/angular.js/issues/10367), [#10369](https://github.com/angular/angular.js/issues/10369)) +- **$sanitize:** disallow unsafe svg animation tags + ([67688d5c](https://github.com/angular/angular.js/commit/67688d5ca00f6de4c7fe6084e2fa762a00d25610), + [#11290](https://github.com/angular/angular.js/issues/11290)) +- **Angular:** properly compare RegExp with other objects for equality + ([f22e1fc9](https://github.com/angular/angular.js/commit/f22e1fc9610ae111a3ea8746a3a57169c99ce142), + [#11204](https://github.com/angular/angular.js/issues/11204), [#11205](https://github.com/angular/angular.js/issues/11205)) +- **date filter:** display localised era for `G` format codes + ([2b4dfa9e](https://github.com/angular/angular.js/commit/2b4dfa9e2b63d7ebb78f3b0fd3439d18f932e1cd), + [#10503](https://github.com/angular/angular.js/issues/10503), [#11266](https://github.com/angular/angular.js/issues/11266)) +- **filterFilter:** + - fix filtering using an object expression when the filter value is undefined + ([c62fa6bd](https://github.com/angular/angular.js/commit/c62fa6bd898e1048d4690d41034489dc60ba6ac2), + [#10419](https://github.com/angular/angular.js/issues/10419), [#10424](https://github.com/angular/angular.js/issues/10424)) + - do not throw an error if property is null when comparing objects + ([2c4ffd6a](https://github.com/angular/angular.js/commit/2c4ffd6af4eb012c4054fe7c096267bbc5510af0), + [#10991](https://github.com/angular/angular.js/issues/10991), [#10992](https://github.com/angular/angular.js/issues/10992), [#11116](https://github.com/angular/angular.js/issues/11116)) +- **form:** allow dynamic form names which initially evaluate to blank + ([410f7c68](https://github.com/angular/angular.js/commit/410f7c682633c681be641cd2a321f9e51671d474)) +- **jqLite:** attr should ignore comment, text and attribute nodes + ([bb5bf7f8](https://github.com/angular/angular.js/commit/bb5bf7f8162d11610a53428e630b47030bdc38e5)) +- **ng/$locale:** add ERA info in generic locale + ([4acb0af2](https://github.com/angular/angular.js/commit/4acb0af24c7fb3705a197ca96adc532de4766a7a)) +- **ngJq:** don't rely on existence of jqlite + ([342e5f3c](https://github.com/angular/angular.js/commit/342e5f3ce38d2fd10c5d5a98ca66f864286a7922), + [#11044](https://github.com/angular/angular.js/issues/11044)) +- **ngMessages:** ensure that multi-level transclusion works with `ngMessagesInclude` + ([d7ec5f39](https://github.com/angular/angular.js/commit/d7ec5f392e1550658ddf271a30627b1749eccb69), + [#11196](https://github.com/angular/angular.js/issues/11196)) +- **ngOptions:** fix model<->option interaction when using `track by` + ([6a03ca27](https://github.com/angular/angular.js/commit/6a03ca274314352052c3082163367a146bb11c2d), + [#10869](https://github.com/angular/angular.js/issues/10869), [#10893](https://github.com/angular/angular.js/issues/10893)) +- **rootScope:** prevent memory leak when destroying scopes + ([fb7db4a0](https://github.com/angular/angular.js/commit/fb7db4a07bd1b0b67824d3808fe315419b272689), + [#11173](https://github.com/angular/angular.js/issues/11173), [#11169](https://github.com/angular/angular.js/issues/11169)) + + +## Features + +- **$cookies:** + - allow passing cookie options + ([92c366d2](https://github.com/angular/angular.js/commit/92c366d205da36ec26502aded23db71a6473dad7), + [#8324](https://github.com/angular/angular.js/issues/8324), [#3988](https://github.com/angular/angular.js/issues/3988), [#1786](https://github.com/angular/angular.js/issues/1786), [#950](https://github.com/angular/angular.js/issues/950)) + - move logic into $cookies and deprecate $cookieStore + ([38fbe3ee](https://github.com/angular/angular.js/commit/38fbe3ee8370fc449b82d80df07b5c2ed2cd5fbe), + [#6411](https://github.com/angular/angular.js/issues/6411), [#7631](https://github.com/angular/angular.js/issues/7631)) +- **$cookiesProvider:** provide path, domain, expires and secure options + ([53c66369](https://github.com/angular/angular.js/commit/53c663699126815eabc2a3bc1e3bafc8b3874268)) +- **$interval:** pass additional arguments to the callback + ([4f1f9cfd](https://github.com/angular/angular.js/commit/4f1f9cfdb721cf308ca1162b2227836dc1d28388), + [#10632](https://github.com/angular/angular.js/issues/10632)) +- **$timeout:** pass additional arguments to the callback + ([3a4b6b83](https://github.com/angular/angular.js/commit/3a4b6b83efdb8051e5c4803c0892c19ceb2cba50), + [#10631](https://github.com/angular/angular.js/issues/10631)) +- **angular.merge:** provide an alternative to `angular.extend` that merges 'deeply' + ([c0498d45](https://github.com/angular/angular.js/commit/c0498d45feb913c318224ea70b5adf7112df6bac), + [#10507](https://github.com/angular/angular.js/issues/10507), [#10519](https://github.com/angular/angular.js/issues/10519)) +- **filterFilter:** compare object with custom `toString()` to primitive + ([f8c42161](https://github.com/angular/angular.js/commit/f8c421617096a8d613f4eb6d0f5b098ee149c029), + [#10464](https://github.com/angular/angular.js/issues/10464), [#10548](https://github.com/angular/angular.js/issues/10548)) +- **ngAria:** + - add `button` role to `ngClick` + ([bb365070](https://github.com/angular/angular.js/commit/bb365070a3ed7c2d26056d378ab6a8ef493b23cc), + [#9254](https://github.com/angular/angular.js/issues/9254), [#10318](https://github.com/angular/angular.js/issues/10318)) + - add roles to custom inputs + ([29cdaee2](https://github.com/angular/angular.js/commit/29cdaee2b6e853bc3f8882a00661698d146ecd18), + [#10012](https://github.com/angular/angular.js/issues/10012), [#10318](https://github.com/angular/angular.js/issues/10318)) +- **ngLocale:** Add FIRSTDAYOFWEEK and WEEKENDRANGE from google data + ([3d149c7f](https://github.com/angular/angular.js/commit/3d149c7f20ffabab5a635af9ddcfc7105112ab4a)) +- **ngMock:** + - allow mock $controller service to set up controller bindings + ([d02d0585](https://github.com/angular/angular.js/commit/d02d0585a086ecd2e1de628218b5a6d85c8fc7bd), + [#9425](https://github.com/angular/angular.js/issues/9425), [#11239](https://github.com/angular/angular.js/issues/11239)) + - add `they` helpers for testing multiple specs + ([e650c458](https://github.com/angular/angular.js/commit/e650c45894abe6314a806e6b3e32c908df5c00fd), + [#10864](https://github.com/angular/angular.js/issues/10864)) +- **ngModel:** support conversion to timezone other than UTC + ([0413bee8](https://github.com/angular/angular.js/commit/0413bee8cc563a6555f8d42d5f183f6fbefc7350), + [#11005](https://github.com/angular/angular.js/issues/11005)) + + +## Breaking Changes + +- **$cookies:** due to [38fbe3ee](https://github.com/angular/angular.js/commit/38fbe3ee8370fc449b82d80df07b5c2ed2cd5fbe), + + +`$cookies` no longer exposes properties that represent the current browser cookie +values. Now you must explicitly the methods described above to access the cookie +values. This also means that you can no longer watch the `$cookies` properties for +changes to the browser's cookies. + +This feature is generally only needed if a 3rd party library was programmatically +changing the cookies at runtime. If you rely on this then you must either write code that +can react to the 3rd party library making the changes to cookies or implement your own polling +mechanism. + + + + + +# 1.3.15 locality-filtration (2015-03-17) + +## Bug Fixes + +- **$animate:** call `applyStyles` with options on `leave` + ([ebd84e80](https://github.com/angular/angular.js/commit/ebd84e8008f45ccaa84290f6da8c2a114fcfa8cd), + [#10068](https://github.com/angular/angular.js/issues/10068)) +- **$browser:** don't crash if history.state access causes error in IE + ([92767c09](https://github.com/angular/angular.js/commit/92767c098feaf8c58faf2d67f882305019d8160e), + [#10367](https://github.com/angular/angular.js/issues/10367), [#10369](https://github.com/angular/angular.js/issues/10369)) +- **Angular:** properly compare RegExp with other objects for equality + ([b8e8f9af](https://github.com/angular/angular.js/commit/b8e8f9af78f4ef3e556dd3cef6bfee35ad4cb82a), + [#11204](https://github.com/angular/angular.js/issues/11204), [#11205](https://github.com/angular/angular.js/issues/11205)) +- **date filter:** display localised era for `G` format codes + ([f2683f95](https://github.com/angular/angular.js/commit/f2683f956fcd3216eaa263db20b31e0d46338800), + [#10503](https://github.com/angular/angular.js/issues/10503), [#11266](https://github.com/angular/angular.js/issues/11266)) +- **filterFilter:** + - fix filtering using an object expression when the filter value is `undefined` + ([63b9956f](https://github.com/angular/angular.js/commit/63b9956faf4c3679c88a9401b8ccbb111c0294ee), + [#10419](https://github.com/angular/angular.js/issues/10419), [#10424](https://github.com/angular/angular.js/issues/10424)) + - do not throw an error if property is null when comparing objects + ([01161a0e](https://github.com/angular/angular.js/commit/01161a0e9fb1af93e9f06535aed8392ed7f116a4), + [#10991](https://github.com/angular/angular.js/issues/10991), [#10992](https://github.com/angular/angular.js/issues/10992), [#11116](https://github.com/angular/angular.js/issues/11116)) +- **form:** allow dynamic form names which initially evaluate to blank + ([190ea883](https://github.com/angular/angular.js/commit/190ea883c588d63f8b900a8de1d45c6c9ebb01ec), + [#11096](https://github.com/angular/angular.js/issues/11096)) +- **ng/$locale:** add ERA info in generic locale + ([57842530](https://github.com/angular/angular.js/commit/578425303f2480959da80f31920d08f277d42010)) +- **rootScope:** prevent memory leak when destroying scopes + ([528cf09e](https://github.com/angular/angular.js/commit/528cf09e3f78ad4e3bb6a329ebe315c4f29b4cdb), + [#11173](https://github.com/angular/angular.js/issues/11173), [#11169](https://github.com/angular/angular.js/issues/11169)) +- **templateRequest:** avoid throwing syntax error in Android 2.3 + ([75abbd52](https://github.com/angular/angular.js/commit/75abbd525f07866fdcc6fb311802b8fe700af174), + [#11089](https://github.com/angular/angular.js/issues/11089), [#11051](https://github.com/angular/angular.js/issues/11051), [#11088](https://github.com/angular/angular.js/issues/11088)) + + +## Features + +- **ngAria:** + - add `button` role to `ngClick` + ([b9ad91cf](https://github.com/angular/angular.js/commit/b9ad91cf1e86310a2d2bf13b29fa13a9b835e1ce), + [#9254](https://github.com/angular/angular.js/issues/9254), [#10318](https://github.com/angular/angular.js/issues/10318)) + - add roles to custom inputs + ([21369943](https://github.com/angular/angular.js/commit/21369943fafd577b36827a641b021b1c14cefb57), + [#10012](https://github.com/angular/angular.js/issues/10012), [#10318](https://github.com/angular/angular.js/issues/10318)) +- **ngMock:** + - allow mock $controller service to set up controller bindings + ([b3878a36](https://github.com/angular/angular.js/commit/b3878a36d9f8e56ad7be1eedb9691c9bd12568cb), + [#9425](https://github.com/angular/angular.js/issues/9425), [#11239](https://github.com/angular/angular.js/issues/11239)) + - add `they` helpers for testing multiple specs + ([7288be25](https://github.com/angular/angular.js/commit/7288be25a75d6ca6ac7eca05a7d6b12ccb3a22f8), + [#10864](https://github.com/angular/angular.js/issues/10864)) + + + + +# 1.4.0-beta.5 karmic-stabilization (2015-02-24) + + +## Bug Fixes + +- **$http:** properly access request headers with mixed case + ([5da1256f](https://github.com/angular/angular.js/commit/5da1256fc2812d5b28fb0af0de81256054856369), + [#10881](https://github.com/angular/angular.js/issues/10881), [#10883](https://github.com/angular/angular.js/issues/10883)) +- **input:** create max and/or min validator regardless of initial value + ([c211e7a5](https://github.com/angular/angular.js/commit/c211e7a5ad5f1fb8748125f14912aa8715081925), + [#10307](https://github.com/angular/angular.js/issues/10307), [#10327](https://github.com/angular/angular.js/issues/10327)) +- **ngAria:** correctly set "checked" attr for checkboxes and radios + ([d6eba217](https://github.com/angular/angular.js/commit/d6eba21733c6e67e90e3a4763d8d41ad89a73a0c), + [#10389](https://github.com/angular/angular.js/issues/10389), [#10212](https://github.com/angular/angular.js/issues/10212)) +- **ngModel:** fix issues when parserName is same as validator key + ([056a3170](https://github.com/angular/angular.js/commit/056a31700803c0a6014b43cfcc36c5c500cc596e), + [#10698](https://github.com/angular/angular.js/issues/10698), [#10850](https://github.com/angular/angular.js/issues/10850), [#11046](https://github.com/angular/angular.js/issues/11046)) +- **ngOptions:** ngModel is optional + ([ef894c87](https://github.com/angular/angular.js/commit/ef894c87eaead76d90169113ab6acc9287654ea3)) +- **ngSanitize:** Do not ignore white-listed svg camelCased attributes + ([46b80654](https://github.com/angular/angular.js/commit/46b80654cae9105642909cd55f73f7c26d2fbd80), + [#10779](https://github.com/angular/angular.js/issues/10779), [#10990](https://github.com/angular/angular.js/issues/10990), [#11124](https://github.com/angular/angular.js/issues/11124)) +- **select:** remove unknown option when model is undefined and empty option is available + ([30b48132](https://github.com/angular/angular.js/commit/30b48132e0fb92ea8dd25a9794b4c41a3a81a951), + [#11078](https://github.com/angular/angular.js/issues/11078), [#11092](https://github.com/angular/angular.js/issues/11092)) +- **templateRequest:** avoid throwing syntax error in Android 2.3 + ([f6272333](https://github.com/angular/angular.js/commit/f6272333127d908b19da23f9cd8a74052711795b), + [#11089](https://github.com/angular/angular.js/issues/11089), [#11051](https://github.com/angular/angular.js/issues/11051), [#11088](https://github.com/angular/angular.js/issues/11088)) + + +## Features + +- **CommonJS:** - angular modules are now packaged for npm with helpful exports + +- **limitTo:** extend the filter to take a beginning index argument + ([aaae3cc4](https://github.com/angular/angular.js/commit/aaae3cc4160417e6dad802ed9d9f6d5471821a87), + [#5355](https://github.com/angular/angular.js/issues/5355), [#10899](https://github.com/angular/angular.js/issues/10899)) +- **ngMessages:** provide support for dynamic message resolution + ([c9a4421f](https://github.com/angular/angular.js/commit/c9a4421fc3c97448527eadef1f42eb2f487ec2e0), + [#10036](https://github.com/angular/angular.js/issues/10036), [#9338](https://github.com/angular/angular.js/issues/9338)) +- **ngOptions:** add support for disabling an option + ([da9eac86](https://github.com/angular/angular.js/commit/da9eac8660343b1cd9fdcf9d2d1bda06067142d7), + [#638](https://github.com/angular/angular.js/issues/638), [#11017](https://github.com/angular/angular.js/issues/11017)) + + +## Performance Improvements + +- **$compile:** + - replace forEach(controller) with plain loops + ([5b522867](https://github.com/angular/angular.js/commit/5b5228675f67c8f5e04c7183c3ef5e71cb2bf08b), + [#11084](https://github.com/angular/angular.js/issues/11084)) + - avoid .data when fetching required controllers + ([fa0aa839](https://github.com/angular/angular.js/commit/fa0aa83937378cf8fc720c38bcc5c78fc923624e)) +- **ngOptions:** only watch labels if a display expression is specified + ([51faaffd](https://github.com/angular/angular.js/commit/51faaffdbcc734c55d52ff6c42b386d5c90207ea)) + + +## Breaking Changes + +- **ngMessages:** due to [c9a4421f](https://github.com/angular/angular.js/commit/c9a4421fc3c97448527eadef1f42eb2f487ec2e0), + + +The `ngMessagesInclude` attribute is now its own directive and that must +be placed as a **child** element within the element with the ngMessages +directive. (Keep in mind that the former behaviour of the +ngMessageInclude attribute was that all **included** ngMessage template +code was placed at the **bottom** of the element containing the +ngMessages directive; therefore to make this behave in the same way, +place the element containing the ngMessagesInclude directive at the +end of the container containing the ngMessages directive). + +```html + +
+
Your message is required
+
+ + +
+
Your message is required
+
+
+``` + + + +# 1.3.14 instantaneous-browserification (2015-02-24) + + +## Features + +- **CommonJS:** - angular modules are now packaged for npm with helpful exports + +## Bug Fixes + +- **input:** create max and/or min validator regardless of initial value + ([abfce532](https://github.com/angular/angular.js/commit/abfce5327ce6fd29c33c62d2edf3600674a6b4c0), + [#10307](https://github.com/angular/angular.js/issues/10307), [#10327](https://github.com/angular/angular.js/issues/10327)) +- **ngAria:** correctly set "checked" attr for checkboxes and radios + ([944c150e](https://github.com/angular/angular.js/commit/944c150e6c3001e51d4bf5e2d8149ae4c565d1e3), + [#10389](https://github.com/angular/angular.js/issues/10389), [#10212](https://github.com/angular/angular.js/issues/10212)) +- **ngModel:** fix issues when parserName is same as validator key + ([6b7625a0](https://github.com/angular/angular.js/commit/6b7625a09508c4b5355121a9d4206a734b07b2e1), + [#10698](https://github.com/angular/angular.js/issues/10698), [#10850](https://github.com/angular/angular.js/issues/10850), [#11046](https://github.com/angular/angular.js/issues/11046)) + + + + +# 1.4.0-beta.4 overlyexplosive-poprocks (2015-02-09) + + +## Bug Fixes + +- **$location:** prevent page reload if initial url has empty hash at the end + ([a509e9aa](https://github.com/angular/angular.js/commit/a509e9aa149d0f88cc39f703d539f7ffd4cd6103), + [#10397](https://github.com/angular/angular.js/issues/10397), [#10960](https://github.com/angular/angular.js/issues/10960)) +- **$parse:** Initialize elements in an array from left to right + ([966f6d83](https://github.com/angular/angular.js/commit/966f6d831f9469a917601f9a10604612cd7bd792)) +- **ngAria:** ensure native controls fire a single click + ([9d53e5a3](https://github.com/angular/angular.js/commit/9d53e5a38dd369dec82d82e13e078df3d6054c8a), + [#10388](https://github.com/angular/angular.js/issues/10388), [#10766](https://github.com/angular/angular.js/issues/10766)) +- **ngMock:** handle cases where injector is created before tests + ([898714df](https://github.com/angular/angular.js/commit/898714df9ea38f9ef700015ced5ddea52f096b77), + [#10967](https://github.com/angular/angular.js/issues/10967)) +- **sanitize:** handle newline characters inside special tags + ([cc8755cd](https://github.com/angular/angular.js/commit/cc8755cda6efda0b52954388e8a8d5306e4bfbca), + [030a42e7](https://github.com/angular/angular.js/commit/030a42e79dec8a4bb73053762f7a54d797a058f6) + [#10943](https://github.com/angular/angular.js/issues/10943)) + + +## Features + +- **ng-jq:** adds the ability to force jqLite or a specific jQuery version + ([09ee82d8](https://github.com/angular/angular.js/commit/09ee82d84dcbea4a6e8d85903af82dcd087a78a7)) + + + + +# 1.3.13 meticulous-riffleshuffle (2015-02-09) + + +## Bug Fixes + +- **$location:** prevent page reload if initial url has empty hash at the end + ([4b3a590b](https://github.com/angular/angular.js/commit/4b3a590b009d7fdceda7f52e7ba0352a271b3256), + [#10397](https://github.com/angular/angular.js/issues/10397), [#10960](https://github.com/angular/angular.js/issues/10960)) +- **ngAria:** ensure native controls fire a single click + ([69ee593f](https://github.com/angular/angular.js/commit/69ee593fd2cb5f1d7757efbe6b256e4458752fd7), + [#10388](https://github.com/angular/angular.js/issues/10388), [#10766](https://github.com/angular/angular.js/issues/10766)) +- **ngMock:** handle cases where injector is created before tests + ([39ddef68](https://github.com/angular/angular.js/commit/39ddef682971d3b7282bf9d08f6eaf97b7f4bca4), + [#10967](https://github.com/angular/angular.js/issues/10967)) +- **sanitize:** handle newline characters inside special tags + ([11aedbd7](https://github.com/angular/angular.js/commit/11aedbd741ccddba060a9805adba1779391731da), + [ce49d4d6](https://github.com/angular/angular.js/commit/ce49d4d61bd02464b6c6376af8048f6eb09330a8) + [#10943](https://github.com/angular/angular.js/issues/10943)) + + + + + + +# 1.4.0-beta.3 substance-mimicry (2015-02-02) + + +## Bug Fixes + +- **$compile:** + - do not initialize optional '&' binding if attribute not specified + ([6a38dbfd](https://github.com/angular/angular.js/commit/6a38dbfd3c34c8f9efff503d17eb3cbeb666d422), + [#6404](https://github.com/angular/angular.js/issues/6404), [#9216](https://github.com/angular/angular.js/issues/9216)) + - respect return value from controller constructor + ([62d514b0](https://github.com/angular/angular.js/commit/62d514b06937cc7dd86e973ea11165c88343b42d)) +- **$controller:** throw better error when controller expression is bad + ([dda65e99](https://github.com/angular/angular.js/commit/dda65e992b72044c0fa0c8f5f33184028c0e3ad7), + [#10875](https://github.com/angular/angular.js/issues/10875), [#10910](https://github.com/angular/angular.js/issues/10910)) +- **$parse:** + - handle null targets at assign + ([2e5a7e52](https://github.com/angular/angular.js/commit/2e5a7e52a0385575bbb55a801471b009afafeca3)) + - remove references to last arguments to a fn call + ([e61eae1b](https://github.com/angular/angular.js/commit/e61eae1b1f2351c51bcfe4142749a4e68a2806ff), + [#10894](https://github.com/angular/angular.js/issues/10894)) +- **a:** don't reload if there is only a name attribute + ([d729fcf0](https://github.com/angular/angular.js/commit/d729fcf030be1d3ef37196d36ea3bf3249ee3318), + [#6273](https://github.com/angular/angular.js/issues/6273), [#10880](https://github.com/angular/angular.js/issues/10880)) +- **angular.copy:** support copying `TypedArray`s + ([aa0f6449](https://github.com/angular/angular.js/commit/aa0f64496a66d2a5d1a4d033f2eb075a8b084a78), + [#10745](https://github.com/angular/angular.js/issues/10745)) +- **filter:** format timezone correctly in the case that UTC timezone is used + ([8c469191](https://github.com/angular/angular.js/commit/8c46919199090a05634789774124b38983430c76), + [#9359](https://github.com/angular/angular.js/issues/9359)) +- **ngRoute:** dont duplicate optional params into query + ([27bf2ce4](https://github.com/angular/angular.js/commit/27bf2ce40c5adfb1494d69c9d0ac9cf433834a12), + [#10689](https://github.com/angular/angular.js/issues/10689)) +- **ngScenario:** allow ngScenario to handle lazy-loaded and manually bootstrapped applications + ([c69caa7b](https://github.com/angular/angular.js/commit/c69caa7beee4e920f8f587eb3e943be99864a14f), + [#10723](https://github.com/angular/angular.js/issues/10723)) +- **validators:** maxlength should use viewValue for $isEmpty + ([bfcf9946](https://github.com/angular/angular.js/commit/bfcf9946e16d21b55dde50d4d21c71c898b10215), + [#10898](https://github.com/angular/angular.js/issues/10898)) + + +## Features + +- **$compile:** allow using bindToController as object, support both new/isolate scopes + ([35498d70](https://github.com/angular/angular.js/commit/35498d7045ba9138016464a344e2c145ce5264c1), + [#10420](https://github.com/angular/angular.js/issues/10420), [#10467](https://github.com/angular/angular.js/issues/10467)) +- **filter:** support conversion to timezone other than UTC + ([c6d8512a](https://github.com/angular/angular.js/commit/c6d8512a1d7345516d1bd9a039d81821b9518bff), + [#10858](https://github.com/angular/angular.js/issues/10858)) +- **ngMocks:** cleanup $inject annotations after each test + ([0baa17a3](https://github.com/angular/angular.js/commit/0baa17a3b7ad2b242df2b277b81cebdf75b04287)) + + +## Performance Improvements + +- **$scope:** Add a property $$watchersCount to scope + ([c1500ea7](https://github.com/angular/angular.js/commit/c1500ea775c4cb130088b7d5bb5fb872bda50bae)) +- **$parse** new and more performant parser + ([0d42426](https://github.com/angular/angular.js/commit/0d424263ead16635afb582affab2b147f8e71626)) + + +## Breaking Changes + +- **$compile:** due to [6a38dbfd](https://github.com/angular/angular.js/commit/6a38dbfd3c34c8f9efff503d17eb3cbeb666d422), +Previously, '&' expressions would always set up a function in the isolate scope. Now, if the binding +is marked as optional and the attribute is not specified, no function will be added to the isolate scope. + + + +# 1.3.12 outlandish-knitting (2015-02-02) + + +## Bug Fixes + +- **$controller:** throw better error when controller expression is bad + ([632b2ddd](https://github.com/angular/angular.js/commit/632b2ddd34c07b3b5a207bd83ca3a5e6e613e63b), + [#10875](https://github.com/angular/angular.js/issues/10875), [#10910](https://github.com/angular/angular.js/issues/10910)) +- **$parse:** remove references to last arguments to a fn call + ([7caad220](https://github.com/angular/angular.js/commit/7caad2205a6e9927890192a3638f55532bdaaf75), + [#10894](https://github.com/angular/angular.js/issues/10894)) +- **ngRoute:** dont duplicate optional params into query + ([f41ca4a5](https://github.com/angular/angular.js/commit/f41ca4a53ed53f172fb334911be56e42aad58794), + [#10689](https://github.com/angular/angular.js/issues/10689)) +- **ngScenario:** Allow ngScenario to handle lazy-loaded and manually bootstrapped applications + ([0bcd0872](https://github.com/angular/angular.js/commit/0bcd0872d8d2e37e6cb7aa5bc5cb0c742b4294f9), + [#10723](https://github.com/angular/angular.js/issues/10723)) +- **validators:** maxlength should use viewValue for $isEmpty + ([abd8e2a9](https://github.com/angular/angular.js/commit/abd8e2a9eb2d21ac67989c2f7b64c4c6547a1585), + [#10898](https://github.com/angular/angular.js/issues/10898)) + + +## Features + +- **ngMocks:** cleanup $inject annotations after each test + ([6ec59460](https://github.com/angular/angular.js/commit/6ec5946094ee92b820bbacc886fa2367715e60b4)) + + + + + +# 1.4.0-beta.2 holographic-rooster (2015-01-26) + + +## Bug Fixes + +- **$location:** don't rewrite when link is shift-clicked + ([8b33de6f](https://github.com/angular/angular.js/commit/8b33de6fd0ec0eb785fed697f062763b5c1d8d23), + [#9904](https://github.com/angular/angular.js/issues/9904), [#9906](https://github.com/angular/angular.js/issues/9906)) +- **$templateRequest:** cache downloaded templates as strings + ([b3a9bd3a](https://github.com/angular/angular.js/commit/b3a9bd3ae043e3042ea7ccfe08e3b36a84feb35e), + [#10630](https://github.com/angular/angular.js/issues/10630), [#10646](https://github.com/angular/angular.js/issues/10646)) +- **filterFilter:** throw error if input is not an array + ([cea8e751](https://github.com/angular/angular.js/commit/cea8e75144e6910b806b63a6ec2a6d118316fddd), + [#9992](https://github.com/angular/angular.js/issues/9992), [#10352](https://github.com/angular/angular.js/issues/10352)) +- **htmlAnchorDirective:** + - remove "element !== target element" check + ([2958cd30](https://github.com/angular/angular.js/commit/2958cd308b5ebaf223a3e5df3fb5bf0f23408447), + [#10866](https://github.com/angular/angular.js/issues/10866)) + - don't add event listener if replaced, ignore event if target is different element + ([b146af11](https://github.com/angular/angular.js/commit/b146af11271de8fa4c51c6db87df104269f41a33), + [#4262](https://github.com/angular/angular.js/issues/4262), [#10849](https://github.com/angular/angular.js/issues/10849)) +- **ngPluralize:** fix wrong text content when count is null/undefined + ([3228d3b4](https://github.com/angular/angular.js/commit/3228d3b4991af681e57de5ab079c1e1c11cf35cb), + [#10836](https://github.com/angular/angular.js/issues/10836), [#10841](https://github.com/angular/angular.js/issues/10841)) + +## Breaking Changes + +- **filterFilter:** due to [cea8e751](https://github.com/angular/angular.js/commit/cea8e75144e6910b806b63a6ec2a6d118316fddd), + Previously, the filter was not applied if used with a non array. +Now, it throws an error. This can be worked around by converting an object to an array, using +a filter such as https://github.com/petebacondarwin/angular-toArrayFilter + +Closes #9992 +Closes #10352 + + + +# 1.3.11 spiffy-manatee (2015-01-26) + + +## Bug Fixes + +- **$location:** don't rewrite when link is shift-clicked + ([939ca37c](https://github.com/angular/angular.js/commit/939ca37cfe5f6fc35b09b6705caabd1fcc3cf9d3), + [#9904](https://github.com/angular/angular.js/issues/9904), [#9906](https://github.com/angular/angular.js/issues/9906)) +- **htmlAnchorDirective:** + - remove "element !== target element" check + ([779e3f6b](https://github.com/angular/angular.js/commit/779e3f6b5f8d2550e758cb0c5f64187ba8e00e29), + [#10866](https://github.com/angular/angular.js/issues/10866)) + - don't add event listener if replaced, ignore event if target is different element + ([837a0775](https://github.com/angular/angular.js/commit/837a077578081bbd07863bef85241537d19fa652), + [#4262](https://github.com/angular/angular.js/issues/4262), [#10849](https://github.com/angular/angular.js/issues/10849)) + + + +# 1.4.0-beta.1 trepidatious-salamander (2015-01-20) + + +## Bug Fixes + +- **$animate:** ensure no transitions are applied when an empty inline style object is provided + ([0db5b21b](https://github.com/angular/angular.js/commit/0db5b21b1d09431535e0c0bf8ac63d4b5b24d349), + [#10613](https://github.com/angular/angular.js/issues/10613), [#10770](https://github.com/angular/angular.js/issues/10770)) +- **$compile:** support class directives on SVG elements + ([23c8a90d](https://github.com/angular/angular.js/commit/23c8a90d22f7c7b41b5a756b89498ffac828980a), + [#10736](https://github.com/angular/angular.js/issues/10736), [#10756](https://github.com/angular/angular.js/issues/10756)) +- **form:** clean up success state of controls when they are removed + ([2408f2de](https://github.com/angular/angular.js/commit/2408f2ded5ead6e678c241e38ef474c1fadff92b), + [#10509](https://github.com/angular/angular.js/issues/10509)) +- **ngController:** allow bound constructor fns as controllers + ([d17fbc38](https://github.com/angular/angular.js/commit/d17fbc3862e0a2e646db1222f184dbe663da4a1f), + [#10784](https://github.com/angular/angular.js/issues/10784), [#10790](https://github.com/angular/angular.js/issues/10790)) +- **ngRepeat:** do not sort object keys alphabetically + ([c260e738](https://github.com/angular/angular.js/commit/c260e7386391877625eda086480de73e8a0ba921), + [#6210](https://github.com/angular/angular.js/issues/6210), [#10538](https://github.com/angular/angular.js/issues/10538)) + + +## Features + +- **$http:** provide a config object as an argument to header functions + ([d435464c](https://github.com/angular/angular.js/commit/d435464c51d3912f56cfc830d86bfc64a1578327), + [#7235](https://github.com/angular/angular.js/issues/7235), [#10622](https://github.com/angular/angular.js/issues/10622)) + + +## Breaking Changes + +- **ngRepeat:** due to [c260e738](https://github.com/angular/angular.js/commit/c260e7386391877625eda086480de73e8a0ba921), + + +Previously, the order of items when using ngRepeat to iterate +over object properties was guaranteed to be consistent by sorting the +keys into alphabetic order. + +Now, the order of the items is browser dependent based on the order returned +from iterating over the object using the `for key in obj` syntax. + +It seems that browsers generally follow the strategy of providing +keys in the order in which they were defined, although there are exceptions +when keys are deleted and reinstated. See +https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_issues + +The best approach is to convert Objects into Arrays by a filter such as +https://github.com/petebacondarwin/angular-toArrayFilter +or some other mechanism, and then sort them manually in the order you need. + +Closes #6210 +Closes #10538 + + + + +# 1.3.10 heliotropic-sundial (2015-01-20) + + +## Bug Fixes + +- **$animate:** ensure no transitions are applied when an empty inline style object is provided + ([9b8df52a](https://github.com/angular/angular.js/commit/9b8df52aa960b9b6288fc150d55ea2e35f56555e), + [#10613](https://github.com/angular/angular.js/issues/10613), [#10770](https://github.com/angular/angular.js/issues/10770)) +- **$compile:** support class directives on SVG elements + ([7a9e3360](https://github.com/angular/angular.js/commit/7a9e3360284d58197a1fe34de57f5e0f6d1f4a76), + [#10736](https://github.com/angular/angular.js/issues/10736), [#10756](https://github.com/angular/angular.js/issues/10756)) +- **form:** clean up success state of controls when they are removed + ([cdc7280d](https://github.com/angular/angular.js/commit/cdc7280dd3d5a2ded784c06dd55fe36c2053fb6f), + [#10509](https://github.com/angular/angular.js/issues/10509)) +- **ngController:** allow bound constructor fns as controllers + ([d015c8a8](https://github.com/angular/angular.js/commit/d015c8a80b28754633c846fc50d11c9437519486), + [#10784](https://github.com/angular/angular.js/issues/10784), [#10790](https://github.com/angular/angular.js/issues/10790)) + + + + +# 1.4.0-beta.0 photonic-umbrakinesis (2015-01-13) + + +## Bug Fixes + +- **$location:** support right button click on anchors in firefox + ([aa798f12](https://github.com/angular/angular.js/commit/aa798f123658cb78b5581513d26577016195cafe), + [#7984](https://github.com/angular/angular.js/issues/7984)) +- **$templateRequest:** propagate HTTP status on failed requests + ([e24f22bd](https://github.com/angular/angular.js/commit/e24f22bdb1740388938d58778aa24d307a79a796), + [#10514](https://github.com/angular/angular.js/issues/10514), [#10628](https://github.com/angular/angular.js/issues/10628)) +- **dateFilter:** ignore invalid dates + ([1334b8c8](https://github.com/angular/angular.js/commit/1334b8c8326b93e0ca016c85516627900c7a9fd3), + [#10640](https://github.com/angular/angular.js/issues/10640)) +- **filterFilter:** use isArray() to determine array type + ([a01ce6b8](https://github.com/angular/angular.js/commit/a01ce6b81c197b0a4a1057981e8e9c1b74f37587), + [#10621](https://github.com/angular/angular.js/issues/10621)) +- **ngChecked:** ensure that ngChecked doesn't interfere with ngModel + ([e079111b](https://github.com/angular/angular.js/commit/e079111b33bf36be21c0941718b41cc9ca67bea0), + [#10662](https://github.com/angular/angular.js/issues/10662), [#10664](https://github.com/angular/angular.js/issues/10664)) +- **ngClass:** handle multi-class definitions as an element of an array + ([e1132f53](https://github.com/angular/angular.js/commit/e1132f53b03a5a71aa9b6eded24d64e3bc83929b), + [#8578](https://github.com/angular/angular.js/issues/8578), [#10651](https://github.com/angular/angular.js/issues/10651)) +- **ngModelOptions:** allow sharing options between multiple inputs + ([9c9c6b3f](https://github.com/angular/angular.js/commit/9c9c6b3fe4edfe78ae275c413ee3eefb81f1ebf6), + [#10667](https://github.com/angular/angular.js/issues/10667)) +- **ngOptions:** + - support one-time binding on the option values + ([ba90261b](https://github.com/angular/angular.js/commit/ba90261b7586b519483883800ea876510faf5c21), + [#10687](https://github.com/angular/angular.js/issues/10687), [#10694](https://github.com/angular/angular.js/issues/10694)) + - prevent infinite digest if track by expression is stable + ([fc21db8a](https://github.com/angular/angular.js/commit/fc21db8a15545fad53124fc941b3c911a8d57067), + [#9464](https://github.com/angular/angular.js/issues/9464)) + - update model if selected option is removed + ([933591d6](https://github.com/angular/angular.js/commit/933591d69cee2c5580da1d8522ba90a7d924da0e), + [#7736](https://github.com/angular/angular.js/issues/7736)) + - ensure that the correct option is selected when options are loaded async + ([7fda214c](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef), + [#8019](https://github.com/angular/angular.js/issues/8019), [#9714](https://github.com/angular/angular.js/issues/9714), [#10639](https://github.com/angular/angular.js/issues/10639)) +- **ngPluralize:** generate a warning when using a not defined rule + ([c66b4b6a](https://github.com/angular/angular.js/commit/c66b4b6a133f7215d50c23db516986cfc1f0a985)) + + +## Features + +- **$filter:** display Infinity symbol when number is Infinity + ([51d67742](https://github.com/angular/angular.js/commit/51d6774286202b55ade402ca097e417e70fd546b), + [#10421](https://github.com/angular/angular.js/issues/10421)) +- **$timeout:** allow `fn` to be an optional parameter + ([5a603023](https://github.com/angular/angular.js/commit/5a60302389162c6ef45f311c1aaa65a00d538c66), + [#9176](https://github.com/angular/angular.js/issues/9176)) +- **limitTo:** ignore limit when invalid + ([a3c3bf33](https://github.com/angular/angular.js/commit/a3c3bf3332e5685dc319c46faef882cb6ac246e1), + [#10510](https://github.com/angular/angular.js/issues/10510)) +- **ngMock/$exceptionHandler:** log errors when rethrowing + ([deb3cb4d](https://github.com/angular/angular.js/commit/deb3cb4daef0054457bd9fb8995829fff0e8f1e4), + [#10540](https://github.com/angular/angular.js/issues/10540), [#10564](https://github.com/angular/angular.js/issues/10564)) + + +## Performance Improvements + +- **ngStyleDirective:** use $watchCollection + ([8928d023](https://github.com/angular/angular.js/commit/8928d0234551a272992d0eccef73b3ad6cb8bfd1), + [#10535](https://github.com/angular/angular.js/issues/10535)) + + +## Breaking Changes + +- **limitTo:** due to [a3c3bf33](https://github.com/angular/angular.js/commit/a3c3bf3332e5685dc319c46faef882cb6ac246e1), + limitTo changed behavior when limit value is invalid. +Instead of returning empty object/array it returns unchanged input. + + +- **ngOptions:** due to [7fda214c](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef), + + +When using `ngOptions`: the directive applies a surrogate key as the value of the `