diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..58ec1bdb --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,30 @@ +# Spec 005 T3 — weekly dependency updates, grouped so each ecosystem opens one PR. +version: 2 +updates: + - package-ecosystem: bundler + directory: / + schedule: { interval: weekly } + groups: + gems: { patterns: ["*"] } + + - package-ecosystem: npm + directory: / + schedule: { interval: weekly } + groups: + root-packages: { patterns: ["*"] } + + - package-ecosystem: npm + directory: /e2e + schedule: { interval: weekly } + groups: + e2e-packages: { patterns: ["*"] } + + - package-ecosystem: github-actions + directory: / + schedule: { interval: weekly } + groups: + actions: { patterns: ["*"] } + + - package-ecosystem: gitsubmodule + directory: / + schedule: { interval: weekly } diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..5e2ed470 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,34 @@ +# Spec 005 T3 — static analysis of the site's JavaScript, Ruby plugins and workflows. +name: CodeQL + +on: + push: + branches: [master] + pull_request: + branches: [master] + schedule: + - cron: '23 4 * * 1' + +permissions: + contents: read + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + runs-on: ubuntu-latest + permissions: + security-events: write + strategy: + fail-fast: false + matrix: + language: [actions, javascript-typescript, ruby] + steps: + - uses: actions/checkout@v4 + - uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: none + queries: security-and-quality + - uses: github/codeql-action/analyze@v3 + with: + category: /language:${{ matrix.language }} diff --git a/_config.yml b/_config.yml index b00e9307..500a2733 100644 --- a/_config.yml +++ b/_config.yml @@ -72,8 +72,9 @@ staticman: # Secret used to secure communication with Staticman secret: "REPLACE_WITH_YOUR_SECRET" # reCAPTCHA configuration + # Off until contribute (spec 003) replaces Staticman; the site key below is a placeholder. recaptcha: - enabled: true + enabled: false siteKey: "REPLACE_WITH_YOUR_SITE_KEY" secret: "REPLACE_WITH_YOUR_SECRET" diff --git a/_includes/components/atoms/head.html b/_includes/components/atoms/head.html index 659addd7..ecb93558 100644 --- a/_includes/components/atoms/head.html +++ b/_includes/components/atoms/head.html @@ -142,25 +142,11 @@ - - + + - -{% if jekyll.environment == "production" %} - -{% endif %} diff --git a/_includes/head.html b/_includes/head.html index 320573f1..5c78cc03 100644 --- a/_includes/head.html +++ b/_includes/head.html @@ -4,10 +4,10 @@ - + - + - -{% if jekyll.environment == "production" %} - -{% endif %} - \ No newline at end of file diff --git a/docs/adr/0003-deploy-on-cloudflare-workers.md b/docs/adr/0003-deploy-on-cloudflare-workers.md new file mode 100644 index 00000000..a237730d --- /dev/null +++ b/docs/adr/0003-deploy-on-cloudflare-workers.md @@ -0,0 +1,22 @@ +# 0003 — Deploy the site on Cloudflare Workers (static assets) + +Date: 2026-10-11 · Status: Accepted · Spec: [005](../specs/005-website-modernization/spec.md) + +## Context + +The site is served through Cloudflare, configured outside the repository (`.cloudflare/pages.toml` is not a format +Cloudflare Pages reads), while `.github/workflows/deploy.yml` targets GitHub Pages. The `contribute` and `discuss` +services will run on Cloudflare Workers, and their sign-in sessions should be first-party cookies on the IPL domain. + +## Decision + +Build the Jekyll site in CI and deploy `_site` as **Workers static assets** with Wrangler (`wrangler.toml` in the +repository), keeping `_headers`, `_redirects` and the 404 page. The site Worker owns `ipl.better-web.org` and later +routes `/contribute/*` and `/discuss/*` to the services through service bindings. Preview deployments per pull request. +The migration happens **after** the modernization (spec 005), which removes the parallel build systems first. + +## Consequences + +- One platform and toolchain (Wrangler) for site and services; versioned, reviewable deploy configuration. +- First-party sessions for GitHub/ORCID sign-in; no cross-site cookies or wildcard CORS. +- The build moves to GitHub Actions; the GitHub Pages workflow and the Pages configuration are removed. diff --git a/docs/specs/005-website-modernization/plan.md b/docs/specs/005-website-modernization/plan.md new file mode 100644 index 00000000..4c47796a --- /dev/null +++ b/docs/specs/005-website-modernization/plan.md @@ -0,0 +1,18 @@ +# 005 — Plan + +Spec: [spec.md](spec.md). Base branch: `chore/modernize` (stacked on `feat/design-v1`, which is not merged yet). + +## Approach +Each phase is a PR, tests green at every step; deletions are listed and approved by the maintainer before running. + +| Phase | Changes | Verification | +| --- | --- | --- | +| 1 Safety | Pin `@tailwindcss/browser@4.3.3` and Font Awesome 6.4.0 with SRI (`crossorigin="anonymous"`); remove GA snippets from both head includes; `.github/dependabot.yml`, CodeQL workflow; delete approved dead files; untrack `vendor/bundle` | New build tests for AC1/AC2; full suites | +| 2 Toolchain | Ruby 3.4 (`.ruby-version`, `Gemfile`, Docker/CI); `bundle update` gated by suites; consolidate scripts in `package.json`; port remaining Rake generation into plugins; CI workflow running Minitest + Playwright/axe; remove Rake/Make/`build.sh`/`deploy.yml` | Suites on CI; page-count parity before/after | +| 3 Front-end | Tailwind CLI v4 build step (`@theme` from `tailwind-theme.css`, content globs over templates and submodules) emitting `assets/css/site.css`; remove the runtime script; icon atom replaces Font Awesome per component | Screenshot diff of reference pages; axe | +| 4 Performance | Liquid profiling; move per-page menu/resource loops into plugin-generated data | Build time < 10 s | +| 5 Workers | `wrangler.toml` with `[assets] directory = "_site"`, `not_found_handling = "404-page"`; deploy job with PR previews | Smoke + E2E against the preview URL | + +## Risks +- Tailwind compilation must scan submodule pages too, or legacy classes disappear (content globs include `en-US/**`, `pt-BR/**`). +- Removing Rake must not drop pages: parity test counts generated pages per language before/after. diff --git a/docs/specs/005-website-modernization/spec.md b/docs/specs/005-website-modernization/spec.md new file mode 100644 index 00000000..c7337745 --- /dev/null +++ b/docs/specs/005-website-modernization/spec.md @@ -0,0 +1,32 @@ +# 005 — Website modernization + +Status: in progress · Tracking issue: [#27](https://github.com/InternetPublicLibrary/IPLibrary-website/issues/27) · +ADR: [0003](../../adr/0003-deploy-on-cloudflare-workers.md) + +## Why +The site builds and runs, but on an end-of-life Ruby, with 87 MB of vendored gems in git, three overlapping build +systems, a CI that does not run what developers run, unpinned third-party scripts and dead code. Moving to +Cloudflare Workers (ADR 0003) is only safe once the build is single, reproducible and tested. + +## What +1. **Safety and hygiene:** third-party scripts pinned to exact versions with SRI; the dead Universal Analytics + snippet removed; `vendor/bundle` and debug/unused files out of git; Dependabot and CodeQL. +2. **One toolchain:** Ruby 3.4+; one set of `package.json` scripts calling Jekyll directly; Rake/Make/`build.sh` + retired after parity; CI runs `rake test`, Playwright + axe and internal link checks on every PR. +3. **Compiled front-end:** Tailwind compiled at build time from the IPL palette; no runtime CSS compiler; Font + Awesome replaced by the local icon atom. +4. **Performance:** full build under 10 s; Lighthouse budget on the reference pages. +5. **Deploy:** Workers static assets via Wrangler with PR previews (ADR 0003); GitHub Pages workflow removed. + +## Acceptance criteria +- [ ] AC1 — No third-party `"}) - # # => "{\"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: - # - #
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 unicode keys and values with String#unicode_normalize (NFC)
- #
- # @return [Hash, Array, String] The normalized values
- def normalize_value(value)
- # Handle unicode normalization
- if value.respond_to?(:to_ary)
- value.to_ary.map! { |val| normalize_value(val) }
- elsif value.kind_of?(Hash)
- value = value.inject({}) { |acc, (k, v)|
- acc[normalize_value(k)] = normalize_value(v)
- acc
- }
- else
- value = value.to_s if !value.kind_of?(String)
- if value.encoding != Encoding::UTF_8
- value = value.dup.force_encoding(Encoding::UTF_8)
- end
- value = value.unicode_normalize(:nfc)
- 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/3.2.0/gems/addressable-2.8.7/lib/addressable/uri.rb b/vendor/bundle/ruby/3.2.0/gems/addressable-2.8.7/lib/addressable/uri.rb
deleted file mode 100644
index 40b80cf0..00000000
--- a/vendor/bundle/ruby/3.2.0/gems/addressable-2.8.7/lib/addressable/uri.rb
+++ /dev/null
@@ -1,2602 +0,0 @@
-# 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
- RESERVED_AND_UNRESERVED = RESERVED + UNRESERVED
- 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
-
- module CharacterClassesRegexps
- AUTHORITY = /[^#{CharacterClasses::AUTHORITY}]/
- FRAGMENT = /[^#{CharacterClasses::FRAGMENT}]/
- HOST = /[^#{CharacterClasses::HOST}]/
- PATH = /[^#{CharacterClasses::PATH}]/
- QUERY = /[^#{CharacterClasses::QUERY}]/
- RESERVED = /[^#{CharacterClasses::RESERVED}]/
- RESERVED_AND_UNRESERVED = /[^#{CharacterClasses::RESERVED_AND_UNRESERVED}]/
- SCHEME = /[^#{CharacterClasses::SCHEME}]/
- UNRESERVED = /[^#{CharacterClasses::UNRESERVED}]/
- 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 unless 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]
- port = nil if port == EMPTY_STR
- 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
-
- unless 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)
- unless 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)
- # #=> #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=CharacterClassesRegexps::RESERVED_AND_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 |char|
- SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE[char.ord]
- end
- if upcase_encoded.length > 0
- upcase_encoded_chars = upcase_encoded.bytes.map do |byte|
- SEQUENCE_ENCODING_TABLE[byte]
- 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(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=
- CharacterClassesRegexps::RESERVED_AND_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?('%')
-
- bytes = leave_encoded.bytes
- leave_encoded_pattern = bytes.map { |b| SEQUENCE_ENCODING_TABLE[b] }.join('|')
- "|%(?!#{leave_encoded_pattern}|#{leave_encoded_pattern.upcase})"
- 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(
- unencoded.unicode_normalize(:nfc),
- 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::CharacterClassesRegexps::SCHEME),
- :authority => self.encode_component(uri_object.authority,
- Addressable::URI::CharacterClassesRegexps::AUTHORITY),
- :path => self.encode_component(uri_object.path,
- Addressable::URI::CharacterClassesRegexps::PATH),
- :query => self.encode_component(uri_object.query,
- Addressable::URI::CharacterClassesRegexps::QUERY),
- :fragment => self.encode_component(uri_object.fragment,
- Addressable::URI::CharacterClassesRegexps::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] = value.to_str.unicode_normalize(:nfc)
- 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::CharacterClassesRegexps::SCHEME),
- :user => self.encode_component(components[:user],
- Addressable::URI::CharacterClassesRegexps::UNRESERVED),
- :password => self.encode_component(components[:password],
- Addressable::URI::CharacterClassesRegexps::UNRESERVED),
- :host => components[:host],
- :port => components[:port],
- :path => self.encode_component(components[:path],
- Addressable::URI::CharacterClassesRegexps::PATH),
- :query => self.encode_component(components[:query],
- Addressable::URI::CharacterClassesRegexps::QUERY),
- :fragment => self.encode_component(components[:fragment],
- Addressable::URI::CharacterClassesRegexps::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"),
- CharacterClassesRegexps::UNRESERVED
- ).gsub("%20", "+"),
- self.encode_component(
- value.gsub(/(\r\n|\n|\r)/, "\r\n"),
- CharacterClassesRegexps::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
-
- reset_ivs
-
- 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
-
- to_s # force path validation
- 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.
- attr_reader :scheme
-
- ##
- # The scheme component for this URI, normalized.
- #
- # @return [String] The scheme component, normalized.
- def normalized_scheme
- return nil unless self.scheme
- if @normalized_scheme == NONE
- @normalized_scheme = 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
- @normalized_scheme = NONE
- remove_composite_values
-
- # Ensure we haven't created an invalid URI
- validate()
- end
-
- ##
- # The user component for this URI.
- #
- # @return [String] The user component.
- attr_reader :user
-
- ##
- # The user component for this URI, normalized.
- #
- # @return [String] The user component, normalized.
- def normalized_user
- return nil unless self.user
- return @normalized_user unless @normalized_user == NONE
- @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 unless user
- end
-
- # Reset dependent values
- @userinfo = nil
- @normalized_userinfo = NONE
- @authority = nil
- @normalized_user = NONE
- remove_composite_values
-
- # Ensure we haven't created an invalid URI
- validate()
- end
-
- ##
- # The password component for this URI.
- #
- # @return [String] The password component.
- attr_reader :password
-
- ##
- # The password component for this URI, normalized.
- #
- # @return [String] The password component, normalized.
- def normalized_password
- return nil unless self.password
- return @normalized_password unless @normalized_password == NONE
- @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
- if @password != nil
- self.user = EMPTY_STR if user.nil?
- end
-
- # Reset dependent values
- @userinfo = nil
- @normalized_userinfo = NONE
- @authority = nil
- @normalized_password = NONE
- 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 unless @normalized_userinfo == NONE
- @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
- @authority = nil
- remove_composite_values
-
- # Ensure we haven't created an invalid URI
- validate()
- end
-
- ##
- # The host component for this URI.
- #
- # @return [String] The host component.
- attr_reader :host
-
- ##
- # 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
- @authority = nil
- @normalized_host = nil
- 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 = new_password
- self.user = new_user
- self.host = new_host
- self.port = new_port
-
- # Reset dependent values
- @userinfo = nil
- @normalized_userinfo = NONE
- 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 = new_scheme
- self.host = new_host
- self.port = new_port
- self.userinfo = nil
-
- # Reset dependent values
- @userinfo = nil
- @normalized_userinfo = NONE
- @authority = nil
- @normalized_authority = nil
- 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.
- attr_reader :port
-
- ##
- # The port component for this URI, normalized.
- #
- # @return [Integer] The port component, normalized.
- def normalized_port
- return nil unless self.port
- return @normalized_port unless @normalized_port == NONE
- @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
- @authority = nil
- @normalized_port = NONE
- 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.
- attr_reader :path
-
- 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(delimiter, -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
- @normalized_path = nil
- 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.
- attr_reader :query
-
- ##
- # 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 unless @normalized_query == NONE
- @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
- @normalized_query = NONE
- 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, CharacterClassesRegexps::UNRESERVED
- )
- if value == nil
- buffer << "#{encoded_key}&"
- elsif value.kind_of?(Array)
- value.each do |sub_value|
- encoded_value = URI.encode_component(
- sub_value, CharacterClassesRegexps::UNRESERVED
- )
- buffer << "#{encoded_key}=#{encoded_value}&"
- end
- else
- encoded_value = URI.encode_component(
- value, CharacterClassesRegexps::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.
- attr_reader :fragment
-
- ##
- # The fragment component for this URI, normalized.
- #
- # @return [String] The fragment component, normalized.
- def normalized_fragment
- return nil unless self.fragment
- return @normalized_fragment unless @normalized_fragment == NONE
- @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
- @normalized_fragment = NONE
- 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)
- unless 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")
- # #=> #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
- ensure
- @validation_deferred = false
- end
-
- def encode_with(coder)
- instance_variables.each do |ivar|
- value = instance_variable_get(ivar)
- if value != NONE
- key = ivar.to_s.slice(1..-1)
- coder[key] = value
- end
- end
- nil
- end
-
- def init_with(coder)
- reset_ivs
- coder.map.each do |key, value|
- instance_variable_set("@#{key}", value)
- end
- 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
- reset_ivs
-
- @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"a"*1_000_000_000:
-
-```ruby
-require 'benchmark'
-puts Benchmark.measure { "a"*1_000_000_000 }
-```
-
-On my machine (OSX 10.8.3 on i5 1.7 GHz) this generates:
-
-```
-0.350000 0.400000 0.750000 ( 0.835234)
-```
-
-This report shows the user CPU time, system CPU time, the total time (sum of user CPU time, system CPU time, children's user CPU time, and children's system CPU time), and the elapsed real time. The unit of time is seconds.
-
-Do some experiments sequentially using the #bm method:
-
-```ruby
-require 'benchmark'
-n = 5000000
-Benchmark.bm do |x|
- x.report { for i in 1..n; a = "1"; end }
- x.report { n.times do ; a = "1"; end }
- x.report { 1.upto(n) do ; a = "1"; end }
-end
-```
-
-The result:
-
-```
- user system total real
-1.010000 0.000000 1.010000 ( 1.014479)
-1.000000 0.000000 1.000000 ( 0.998261)
-0.980000 0.000000 0.980000 ( 0.981335)
-```
-
-Continuing the previous example, put a label in each report:
-
-```ruby
-require 'benchmark'
-n = 5000000
-Benchmark.bm(7) do |x|
- x.report("for:") { for i in 1..n; a = "1"; end }
- x.report("times:") { n.times do ; a = "1"; end }
- x.report("upto:") { 1.upto(n) do ; a = "1"; end }
-end
-```
-
-The result:
-
-```
- user system total real
-for: 1.010000 0.000000 1.010000 ( 1.015688)
-times: 1.000000 0.000000 1.000000 ( 1.003611)
-upto: 1.030000 0.000000 1.030000 ( 1.028098)
-```
-
-The times for some benchmarks depend on the order in which items are run. These differences are due to the cost of memory allocation and garbage collection. To avoid these discrepancies, the #bmbm method is provided. For example, to compare ways to sort an array of floats:
-
-```ruby
-require 'benchmark'
-array = (1..1000000).map { rand }
-Benchmark.bmbm do |x|
- x.report("sort!") { array.dup.sort! }
- x.report("sort") { array.dup.sort }
-end
-```
-
-The result:
-
-```
-Rehearsal -----------------------------------------
-sort! 1.490000 0.010000 1.500000 ( 1.490520)
-sort 1.460000 0.000000 1.460000 ( 1.463025)
--------------------------------- total: 2.960000sec
- user system total real
-sort! 1.460000 0.000000 1.460000 ( 1.460465)
-sort 1.450000 0.010000 1.460000 ( 1.448327)
-```
-
-Report statistics of sequential experiments with unique labels, using the #benchmark method:
-
-```ruby
-require 'benchmark'
-include Benchmark # we need the CAPTION and FORMAT constants
-n = 5000000
-Benchmark.benchmark(CAPTION, 7, FORMAT, ">total:", ">avg:") do |x|
- tf = x.report("for:") { for i in 1..n; a = "1"; end }
- tt = x.report("times:") { n.times do ; a = "1"; end }
- tu = x.report("upto:") { 1.upto(n) do ; a = "1"; end }
- [tf+tt+tu, (tf+tt+tu)/3]
-end
-```
-
-The result:
-
-```
- user system total real
-for: 0.950000 0.000000 0.950000 ( 0.952039)
-times: 0.980000 0.000000 0.980000 ( 0.984938)
-upto: 0.950000 0.000000 0.950000 ( 0.946787)
->total: 2.880000 0.000000 2.880000 ( 2.883764)
->avg: 0.960000 0.000000 0.960000 ( 0.961255)
-```
-
-## 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).
-
-## Contributing
-
-Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/benchmark.
diff --git a/vendor/bundle/ruby/3.2.0/gems/benchmark-0.4.1/Rakefile b/vendor/bundle/ruby/3.2.0/gems/benchmark-0.4.1/Rakefile
deleted file mode 100644
index 8830e057..00000000
--- a/vendor/bundle/ruby/3.2.0/gems/benchmark-0.4.1/Rakefile
+++ /dev/null
@@ -1,8 +0,0 @@
-require "bundler/gem_tasks"
-require "rake/testtask"
-
-Rake::TestTask.new(:test) do |t|
- t.test_files = FileList["test/**/test_*.rb"]
-end
-
-task :default => :test
diff --git a/vendor/bundle/ruby/3.2.0/gems/benchmark-0.4.1/benchmark.gemspec b/vendor/bundle/ruby/3.2.0/gems/benchmark-0.4.1/benchmark.gemspec
deleted file mode 100644
index 35deff8d..00000000
--- a/vendor/bundle/ruby/3.2.0/gems/benchmark-0.4.1/benchmark.gemspec
+++ /dev/null
@@ -1,32 +0,0 @@
-name = File.basename(__FILE__, ".gemspec")
-version = ["lib", Array.new(name.count("-")+1, ".").join("/")].find do |dir|
- break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb")) do |line|
- /^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
- end rescue nil
-end
-
-Gem::Specification.new do |spec|
- spec.name = name
- spec.version = version
- spec.authors = ["Yukihiro Matsumoto"]
- spec.email = ["matz@ruby-lang.org"]
-
- spec.summary = %q{a performance benchmarking library}
- spec.description = spec.summary
- spec.homepage = "https://github.com/ruby/benchmark"
- spec.licenses = ["Ruby", "BSD-2-Clause"]
-
- spec.required_ruby_version = ">= 2.1.0"
-
- spec.metadata["homepage_uri"] = spec.homepage
- spec.metadata["source_code_uri"] = spec.homepage
-
- # Specify which files should be added to the gem when it is released.
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
- `git ls-files -z 2>#{IO::NULL}`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
- end
- spec.bindir = "exe"
- spec.executables = []
- spec.require_paths = ["lib"]
-end
diff --git a/vendor/bundle/ruby/3.2.0/gems/benchmark-0.4.1/bin/console b/vendor/bundle/ruby/3.2.0/gems/benchmark-0.4.1/bin/console
deleted file mode 100644
index 87e7b95e..00000000
--- a/vendor/bundle/ruby/3.2.0/gems/benchmark-0.4.1/bin/console
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env ruby
-
-require "bundler/setup"
-require "benchmark"
-
-# You can add fixtures and/or initialization code here to make experimenting
-# with your gem easier. You can also use a different console, if you like.
-
-# (If you use this, don't forget to add pry to your Gemfile!)
-# require "pry"
-# Pry.start
-
-require "irb"
-IRB.start(__FILE__)
diff --git a/vendor/bundle/ruby/3.2.0/gems/benchmark-0.4.1/bin/setup b/vendor/bundle/ruby/3.2.0/gems/benchmark-0.4.1/bin/setup
deleted file mode 100644
index dce67d86..00000000
--- a/vendor/bundle/ruby/3.2.0/gems/benchmark-0.4.1/bin/setup
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-IFS=$'\n\t'
-set -vx
-
-bundle install
-
-# Do any other automated setup that you need to do here
diff --git a/vendor/bundle/ruby/3.2.0/gems/benchmark-0.4.1/lib/benchmark.rb b/vendor/bundle/ruby/3.2.0/gems/benchmark-0.4.1/lib/benchmark.rb
deleted file mode 100644
index 0d1b8df6..00000000
--- a/vendor/bundle/ruby/3.2.0/gems/benchmark-0.4.1/lib/benchmark.rb
+++ /dev/null
@@ -1,595 +0,0 @@
-# frozen_string_literal: true
-#--
-# benchmark.rb - a performance benchmarking library
-#
-# $Id$
-#
-# Created by Gotoken (gotoken@notwork.org).
-#
-# Documentation by Gotoken (original RD), Lyle Johnson (RDoc conversion), and
-# Gavin Sinclair (editing).
-#++
-#
-# == Overview
-#
-# The Benchmark module provides methods for benchmarking Ruby code, giving
-# detailed reports on the time taken for each task.
-#
-
-# The Benchmark module provides methods to measure and report the time
-# used to execute Ruby code.
-#
-# * Measure the time to construct the string given by the expression
-# "a"*1_000_000_000:
-#
-# require 'benchmark'
-#
-# puts Benchmark.measure { "a"*1_000_000_000 }
-#
-# On my machine (OSX 10.8.3 on i5 1.7 GHz) this generates:
-#
-# 0.350000 0.400000 0.750000 ( 0.835234)
-#
-# This report shows the user CPU time, system CPU time, the total time
-# (sum of user CPU time, system CPU time, children's user CPU time,
-# and children's system CPU time), and the elapsed real time. The unit
-# of time is seconds.
-#
-# * Do some experiments sequentially using the #bm method:
-#
-# require 'benchmark'
-#
-# n = 5000000
-# Benchmark.bm do |x|
-# x.report { for i in 1..n; a = "1"; end }
-# x.report { n.times do ; a = "1"; end }
-# x.report { 1.upto(n) do ; a = "1"; end }
-# end
-#
-# The result:
-#
-# user system total real
-# 1.010000 0.000000 1.010000 ( 1.014479)
-# 1.000000 0.000000 1.000000 ( 0.998261)
-# 0.980000 0.000000 0.980000 ( 0.981335)
-#
-# * Continuing the previous example, put a label in each report:
-#
-# require 'benchmark'
-#
-# n = 5000000
-# Benchmark.bm(7) do |x|
-# x.report("for:") { for i in 1..n; a = "1"; end }
-# x.report("times:") { n.times do ; a = "1"; end }
-# x.report("upto:") { 1.upto(n) do ; a = "1"; end }
-# end
-#
-# The result:
-#
-# user system total real
-# for: 1.010000 0.000000 1.010000 ( 1.015688)
-# times: 1.000000 0.000000 1.000000 ( 1.003611)
-# upto: 1.030000 0.000000 1.030000 ( 1.028098)
-#
-# * The times for some benchmarks depend on the order in which items
-# are run. These differences are due to the cost of memory
-# allocation and garbage collection. To avoid these discrepancies,
-# the #bmbm method is provided. For example, to compare ways to
-# sort an array of floats:
-#
-# require 'benchmark'
-#
-# array = (1..1000000).map { rand }
-#
-# Benchmark.bmbm do |x|
-# x.report("sort!") { array.dup.sort! }
-# x.report("sort") { array.dup.sort }
-# end
-#
-# The result:
-#
-# Rehearsal -----------------------------------------
-# sort! 1.490000 0.010000 1.500000 ( 1.490520)
-# sort 1.460000 0.000000 1.460000 ( 1.463025)
-# -------------------------------- total: 2.960000sec
-#
-# user system total real
-# sort! 1.460000 0.000000 1.460000 ( 1.460465)
-# sort 1.450000 0.010000 1.460000 ( 1.448327)
-#
-# * Report statistics of sequential experiments with unique labels,
-# using the #benchmark method:
-#
-# require 'benchmark'
-# include Benchmark # we need the CAPTION and FORMAT constants
-#
-# n = 5000000
-# Benchmark.benchmark(CAPTION, 7, FORMAT, ">total:", ">avg:") do |x|
-# tf = x.report("for:") { for i in 1..n; a = "1"; end }
-# tt = x.report("times:") { n.times do ; a = "1"; end }
-# tu = x.report("upto:") { 1.upto(n) do ; a = "1"; end }
-# [tf+tt+tu, (tf+tt+tu)/3]
-# end
-#
-# The result:
-#
-# user system total real
-# for: 0.950000 0.000000 0.950000 ( 0.952039)
-# times: 0.980000 0.000000 0.980000 ( 0.984938)
-# upto: 0.950000 0.000000 0.950000 ( 0.946787)
-# >total: 2.880000 0.000000 2.880000 ( 2.883764)
-# >avg: 0.960000 0.000000 0.960000 ( 0.961255)
-
-module Benchmark
-
- VERSION = "0.4.1"
-
- BENCHMARK_VERSION = "2002-04-25" # :nodoc:
-
- # Invokes the block with a Benchmark::Report object, which
- # may be used to collect and report on the results of individual
- # benchmark tests. Reserves +label_width+ leading spaces for
- # labels on each line. Prints +caption+ at the top of the
- # report, and uses +format+ to format each line.
- # (Note: +caption+ must contain a terminating newline character,
- # see the default Benchmark::Tms::CAPTION for an example.)
- #
- # Returns an array of Benchmark::Tms objects.
- #
- # If the block returns an array of
- # Benchmark::Tms objects, these will be used to format
- # additional lines of output. If +labels+ parameter are
- # given, these are used to label these extra lines.
- #
- # _Note_: Other methods provide a simpler interface to this one, and are
- # suitable for nearly all benchmarking requirements. See the examples in
- # Benchmark, and the #bm and #bmbm methods.
- #
- # Example:
- #
- # require 'benchmark'
- # include Benchmark # we need the CAPTION and FORMAT constants
- #
- # n = 5000000
- # Benchmark.benchmark(CAPTION, 7, FORMAT, ">total:", ">avg:") do |x|
- # tf = x.report("for:") { for i in 1..n; a = "1"; end }
- # tt = x.report("times:") { n.times do ; a = "1"; end }
- # tu = x.report("upto:") { 1.upto(n) do ; a = "1"; end }
- # [tf+tt+tu, (tf+tt+tu)/3]
- # end
- #
- # Generates:
- #
- # user system total real
- # for: 0.970000 0.000000 0.970000 ( 0.970493)
- # times: 0.990000 0.000000 0.990000 ( 0.989542)
- # upto: 0.970000 0.000000 0.970000 ( 0.972854)
- # >total: 2.930000 0.000000 2.930000 ( 2.932889)
- # >avg: 0.976667 0.000000 0.976667 ( 0.977630)
- #
-
- def benchmark(caption = "", label_width = nil, format = nil, *labels) # :yield: report
- sync = $stdout.sync
- $stdout.sync = true
- label_width ||= 0
- label_width += 1
- format ||= FORMAT
- report = Report.new(label_width, format)
- results = yield(report)
-
- print " " * report.width + caption unless caption.empty?
- report.list.each { |i|
- print i.label.to_s.ljust(report.width)
- print i.format(report.format, *format)
- }
-
- Array === results and results.grep(Tms).each {|t|
- print((labels.shift || t.label || "").ljust(label_width), t.format(format))
- }
- report.list
- ensure
- $stdout.sync = sync unless sync.nil?
- end
-
-
- # A simple interface to the #benchmark method, #bm generates sequential
- # reports with labels. +label_width+ and +labels+ parameters have the same
- # meaning as for #benchmark.
- #
- # require 'benchmark'
- #
- # n = 5000000
- # Benchmark.bm(7) do |x|
- # x.report("for:") { for i in 1..n; a = "1"; end }
- # x.report("times:") { n.times do ; a = "1"; end }
- # x.report("upto:") { 1.upto(n) do ; a = "1"; end }
- # end
- #
- # Generates:
- #
- # user system total real
- # for: 0.960000 0.000000 0.960000 ( 0.957966)
- # times: 0.960000 0.000000 0.960000 ( 0.960423)
- # upto: 0.950000 0.000000 0.950000 ( 0.954864)
- #
-
- def bm(label_width = 0, *labels, &blk) # :yield: report
- benchmark(CAPTION, label_width, FORMAT, *labels, &blk)
- end
-
-
- # Sometimes benchmark results are skewed because code executed
- # earlier encounters different garbage collection overheads than
- # that run later. #bmbm attempts to minimize this effect by running
- # the tests twice, the first time as a rehearsal in order to get the
- # runtime environment stable, the second time for
- # real. GC.start is executed before the start of each of
- # the real timings; the cost of this is not included in the
- # timings. In reality, though, there's only so much that #bmbm can
- # do, and the results are not guaranteed to be isolated from garbage
- # collection and other effects.
- #
- # Because #bmbm takes two passes through the tests, it can
- # calculate the required label width.
- #
- # require 'benchmark'
- #
- # array = (1..1000000).map { rand }
- #
- # Benchmark.bmbm do |x|
- # x.report("sort!") { array.dup.sort! }
- # x.report("sort") { array.dup.sort }
- # end
- #
- # Generates:
- #
- # Rehearsal -----------------------------------------
- # sort! 1.440000 0.010000 1.450000 ( 1.446833)
- # sort 1.440000 0.000000 1.440000 ( 1.448257)
- # -------------------------------- total: 2.890000sec
- #
- # user system total real
- # sort! 1.460000 0.000000 1.460000 ( 1.458065)
- # sort 1.450000 0.000000 1.450000 ( 1.455963)
- #
- # #bmbm yields a Benchmark::Job object and returns an array of
- # Benchmark::Tms objects.
- #
- def bmbm(width = 0) # :yield: job
- job = Job.new(width)
- yield(job)
- width = job.width + 1
- sync = $stdout.sync
- $stdout.sync = true
-
- # rehearsal
- puts 'Rehearsal '.ljust(width+CAPTION.length,'-')
- ets = job.list.inject(Tms.new) { |sum,(label,item)|
- print label.ljust(width)
- res = Benchmark.measure(&item)
- print res.format
- sum + res
- }.format("total: %tsec")
- print " #{ets}\n\n".rjust(width+CAPTION.length+2,'-')
-
- # take
- print ' '*width + CAPTION
- job.list.map { |label,item|
- GC.start
- print label.ljust(width)
- Benchmark.measure(label, &item).tap { |res| print res }
- }
- ensure
- $stdout.sync = sync unless sync.nil?
- end
-
- #
- # Returns the time used to execute the given block as a
- # Benchmark::Tms object. Takes +label+ option.
- #
- # require 'benchmark'
- #
- # n = 1000000
- #
- # time = Benchmark.measure do
- # n.times { a = "1" }
- # end
- # puts time
- #
- # Generates:
- #
- # 0.220000 0.000000 0.220000 ( 0.227313)
- #
- def measure(label = "") # :yield:
- t0, r0 = Process.times, Process.clock_gettime(Process::CLOCK_MONOTONIC)
- yield
- t1, r1 = Process.times, Process.clock_gettime(Process::CLOCK_MONOTONIC)
- Benchmark::Tms.new(t1.utime - t0.utime,
- t1.stime - t0.stime,
- t1.cutime - t0.cutime,
- t1.cstime - t0.cstime,
- r1 - r0,
- label)
- end
-
- #
- # Returns the elapsed real time used to execute the given block.
- # The unit of time is seconds.
- #
- # Benchmark.realtime { "a" * 1_000_000_000 }
- # #=> 0.5098029999935534
- #
- def realtime # :yield:
- r0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
- yield
- Process.clock_gettime(Process::CLOCK_MONOTONIC) - r0
- end
-
- module_function :benchmark, :measure, :realtime, :bm, :bmbm
-
- #
- # A Job is a sequence of labelled blocks to be processed by the
- # Benchmark.bmbm method. It is of little direct interest to the user.
- #
- class Job # :nodoc:
- #
- # Returns an initialized Job instance.
- # Usually, one doesn't call this method directly, as new
- # Job objects are created by the #bmbm method.
- # +width+ is a initial value for the label offset used in formatting;
- # the #bmbm method passes its +width+ argument to this constructor.
- #
- def initialize(width)
- @width = width
- @list = []
- end
-
- #
- # Registers the given label and block pair in the job list.
- #
- def item(label = "", &blk) # :yield:
- raise ArgumentError, "no block" unless block_given?
- label = label.to_s
- w = label.length
- @width = w if @width < w
- @list << [label, blk]
- self
- end
-
- alias report item
-
- # An array of 2-element arrays, consisting of label and block pairs.
- attr_reader :list
-
- # Length of the widest label in the #list.
- attr_reader :width
- end
-
- #
- # This class is used by the Benchmark.benchmark and Benchmark.bm methods.
- # It is of little direct interest to the user.
- #
- class Report # :nodoc:
- #
- # Returns an initialized Report instance.
- # Usually, one doesn't call this method directly, as new
- # Report objects are created by the #benchmark and #bm methods.
- # +width+ and +format+ are the label offset and
- # format string used by Tms#format.
- #
- def initialize(width = 0, format = nil)
- @width, @format, @list = width, format, []
- end
-
- #
- # Prints the +label+ and measured time for the block,
- # formatted by +format+. See Tms#format for the
- # formatting rules.
- #
- def item(label = "", *format, &blk) # :yield:
- w = label.to_s.length
- @width = w if @width < w
- @list << res = Benchmark.measure(label, &blk)
- res
- end
-
- alias report item
-
- # An array of Benchmark::Tms objects representing each item.
- attr_reader :width, :format, :list
- end
-
-
-
- #
- # A data object, representing the times associated with a benchmark
- # measurement.
- #
- class Tms
-
- # Default caption, see also Benchmark::CAPTION
- CAPTION = " user system total real\n"
-
- # Default format string, see also Benchmark::FORMAT
- FORMAT = "%10.6u %10.6y %10.6t %10.6r\n"
-
- # User CPU time
- attr_reader :utime
-
- # System CPU time
- attr_reader :stime
-
- # User CPU time of children
- attr_reader :cutime
-
- # System CPU time of children
- attr_reader :cstime
-
- # Elapsed real time
- attr_reader :real
-
- # Total time, that is +utime+ + +stime+ + +cutime+ + +cstime+
- attr_reader :total
-
- # Label
- attr_reader :label
-
- #
- # Returns an initialized Tms object which has
- # +utime+ as the user CPU time, +stime+ as the system CPU time,
- # +cutime+ as the children's user CPU time, +cstime+ as the children's
- # system CPU time, +real+ as the elapsed real time and +label+ as the label.
- #
- def initialize(utime = 0.0, stime = 0.0, cutime = 0.0, cstime = 0.0, real = 0.0, label = nil)
- @utime, @stime, @cutime, @cstime, @real, @label = utime, stime, cutime, cstime, real, label.to_s
- @total = @utime + @stime + @cutime + @cstime
- end
-
- #
- # Returns a new Tms object whose times are the sum of the times for this
- # Tms object, plus the time required to execute the code block (+blk+).
- #
- def add(&blk) # :yield:
- self + Benchmark.measure(&blk)
- end
-
- #
- # An in-place version of #add.
- # Changes the times of this Tms object by making it the sum of the times
- # for this Tms object, plus the time required to execute
- # the code block (+blk+).
- #
- def add!(&blk)
- t = Benchmark.measure(&blk)
- @utime = utime + t.utime
- @stime = stime + t.stime
- @cutime = cutime + t.cutime
- @cstime = cstime + t.cstime
- @real = real + t.real
- self
- end
-
- #
- # Returns a new Tms object obtained by memberwise summation
- # of the individual times for this Tms object with those of the +other+
- # Tms object.
- # This method and #/() are useful for taking statistics.
- #
- def +(other); memberwise(:+, other) end
-
- #
- # Returns a new Tms object obtained by memberwise subtraction
- # of the individual times for the +other+ Tms object from those of this
- # Tms object.
- #
- def -(other); memberwise(:-, other) end
-
- #
- # Returns a new Tms object obtained by memberwise multiplication
- # of the individual times for this Tms object by +x+.
- #
- def *(x); memberwise(:*, x) end
-
- #
- # Returns a new Tms object obtained by memberwise division
- # of the individual times for this Tms object by +x+.
- # This method and #+() are useful for taking statistics.
- #
- def /(x); memberwise(:/, x) end
-
- #
- # Returns the contents of this Tms object as
- # a formatted string, according to a +format+ string
- # like that passed to Kernel.format. In addition, #format
- # accepts the following extensions:
- #
- # %u:: Replaced by the user CPU time, as reported by Tms#utime.
- # %y:: Replaced by the system CPU time, as reported by Tms#stime (Mnemonic: y of "s*y*stem")
- # %U:: Replaced by the children's user CPU time, as reported by Tms#cutime
- # %Y:: Replaced by the children's system CPU time, as reported by Tms#cstime
- # %t:: Replaced by the total CPU time, as reported by Tms#total
- # %r:: Replaced by the elapsed real time, as reported by Tms#real
- # %n:: Replaced by the label string, as reported by Tms#label (Mnemonic: n of "*n*ame")
- #
- # If +format+ is not given, FORMAT is used as default value, detailing the
- # user, system, total and real elapsed time.
- #
- def format(format = nil, *args)
- str = (format || FORMAT).dup
- str.gsub!(/(%[-+.\d]*)n/) { "#{$1}s" % label }
- str.gsub!(/(%[-+.\d]*)u/) { "#{$1}f" % utime }
- str.gsub!(/(%[-+.\d]*)y/) { "#{$1}f" % stime }
- str.gsub!(/(%[-+.\d]*)U/) { "#{$1}f" % cutime }
- str.gsub!(/(%[-+.\d]*)Y/) { "#{$1}f" % cstime }
- str.gsub!(/(%[-+.\d]*)t/) { "#{$1}f" % total }
- str.gsub!(/(%[-+.\d]*)r/) { "(#{$1}f)" % real }
- format ? str % args : str
- end
-
- #
- # Same as #format.
- #
- def to_s
- format
- end
-
- #
- # Returns a new 6-element array, consisting of the
- # label, user CPU time, system CPU time, children's
- # user CPU time, children's system CPU time and elapsed
- # real time.
- #
- def to_a
- [@label, @utime, @stime, @cutime, @cstime, @real]
- end
-
- #
- # Returns a hash containing the same data as `to_a`.
- #
- def to_h
- {
- label: @label,
- utime: @utime,
- stime: @stime,
- cutime: @cutime,
- cstime: @cstime,
- real: @real
- }
- end
-
- protected
-
- #
- # Returns a new Tms object obtained by memberwise operation +op+
- # of the individual times for this Tms object with those of the other
- # Tms object (+x+).
- #
- # +op+ can be a mathematical operation such as +, -,
- # *, /
- #
- def memberwise(op, x)
- case x
- when Benchmark::Tms
- Benchmark::Tms.new(utime.__send__(op, x.utime),
- stime.__send__(op, x.stime),
- cutime.__send__(op, x.cutime),
- cstime.__send__(op, x.cstime),
- real.__send__(op, x.real)
- )
- else
- Benchmark::Tms.new(utime.__send__(op, x),
- stime.__send__(op, x),
- cutime.__send__(op, x),
- cstime.__send__(op, x),
- real.__send__(op, x)
- )
- end
- end
- end
-
- # The default caption string (heading above the output times).
- CAPTION = Benchmark::Tms::CAPTION
-
- # The default format string used to display times. See also Benchmark::Tms#format.
- FORMAT = Benchmark::Tms::FORMAT
-end
diff --git a/vendor/bundle/ruby/3.2.0/gems/bigdecimal-3.2.2/LICENSE b/vendor/bundle/ruby/3.2.0/gems/bigdecimal-3.2.2/LICENSE
deleted file mode 100644
index a1f19ff9..00000000
--- a/vendor/bundle/ruby/3.2.0/gems/bigdecimal-3.2.2/LICENSE
+++ /dev/null
@@ -1,56 +0,0 @@
-Ruby is copyrighted free software by Yukihiro Matsumoto 'Infinity', '+Infinity' and
- * '-Infinity' (case-sensitive)
- *
- * === Not a Number
- *
- * When a computation results in an undefined value, the special value +NaN+
- * (for 'not a number') is returned.
- *
- * Example:
- *
- * BigDecimal("0.0") / BigDecimal("0.0") #=> NaN
- *
- * You can also create undefined values.
- *
- * NaN is never considered to be the same as any other value, even NaN itself:
- *
- * n = BigDecimal('NaN')
- * n == 0.0 #=> false
- * n == n #=> false
- *
- * === Positive and negative zero
- *
- * If a computation results in a value which is too small to be represented as
- * a BigDecimal within the currently specified limits of precision, zero must
- * be returned.
- *
- * If the value which is too small to be represented is negative, a BigDecimal
- * value of negative zero is returned.
- *
- * BigDecimal("1.0") / BigDecimal("-Infinity") #=> -0.0
- *
- * If the value is positive, a value of positive zero is returned.
- *
- * BigDecimal("1.0") / BigDecimal("Infinity") #=> 0.0
- *
- * (See BigDecimal.mode for how to specify limits of precision.)
- *
- * Note that +-0.0+ and +0.0+ are considered to be the same for the purposes of
- * comparison.
- *
- * Note also that in mathematics, there is no particular concept of negative
- * or positive zero; true mathematical zero has no sign.
- *
- * == bigdecimal/util
- *
- * When you require +bigdecimal/util+, the #to_d method will be
- * available on BigDecimal and the native Integer, Float, Rational,
- * and String classes:
- *
- * require 'bigdecimal/util'
- *
- * 42.to_d # => 0.42e2
- * 0.5.to_d # => 0.5e0
- * (2/3r).to_d(3) # => 0.667e0
- * "0.5".to_d # => 0.5e0
- *
- * == Methods for Working with \JSON
- *
- * - {::json_create}[https://docs.ruby-lang.org/en/master/BigDecimal.html#method-c-json_create]:
- * Returns a new \BigDecimal object constructed from the given object.
- * - {#as_json}[https://docs.ruby-lang.org/en/master/BigDecimal.html#method-i-as_json]:
- * Returns a 2-element hash representing +self+.
- * - {#to_json}[https://docs.ruby-lang.org/en/master/BigDecimal.html#method-i-to_json]:
- * Returns a \JSON string representing +self+.
- *
- * These methods are provided by the {JSON gem}[https://github.com/flori/json]. To make these methods available:
- *
- * require 'json/add/bigdecimal'
- *
- * * == License
- *
- * Copyright (C) 2002 by Shigeo Kobayashi
-
-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). [](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 three main Ruby interpreters
-(MRI/CRuby, JRuby, 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 the [memory model](docs-source/synchronization.md) of Ruby which
-would provide consistent behaviour and guarantees on all three main Ruby interpreters
-(MRI/CRuby, JRuby, 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.
-
- * 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 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 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.
- *
- * 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 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 extends K, ? extends V> 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
- * 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 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 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/3.2.0/gems/concurrent-ruby-1.3.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java b/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.3.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java
deleted file mode 100644
index 93a277fb..00000000
--- a/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.3.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * 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 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 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 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
- */
- public static interface Spliterator {@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
- *
- * @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 super K, ? extends V> 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 super K, ? super V, ? extends V> 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
- *
- * @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 super K, ? super V, ? extends V> 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 super V, ? super V, ? extends V> 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
- *
- *
- *
- *
- *
- *
- * {@code ConcurrentHashMapV8
- */
- public static interface Spliterator