This commit is contained in:
@@ -0,0 +1,301 @@
|
||||
# Addressable 2.8.7 <a name="v2.8.7">
|
||||
- Allow `public_suffix` 6 ([#535])
|
||||
|
||||
[#535]: https://github.com/sporkmonger/addressable/pull/535
|
||||
|
||||
# Addressable 2.8.6 <a name="v2.8.6">
|
||||
- Memoize regexps for common character classes ([#524])
|
||||
|
||||
[#524]: https://github.com/sporkmonger/addressable/pull/524
|
||||
|
||||
# Addressable 2.8.5 <a name="v2.8.5">
|
||||
- Fix thread safety issue with encoding tables ([#515])
|
||||
- Define URI::NONE as a module to avoid serialization issues ([#509])
|
||||
- Fix YAML serialization ([#508])
|
||||
|
||||
[#508]: https://github.com/sporkmonger/addressable/pull/508
|
||||
[#509]: https://github.com/sporkmonger/addressable/pull/509
|
||||
[#515]: https://github.com/sporkmonger/addressable/pull/515
|
||||
|
||||
# Addressable 2.8.4 <a name="v2.8.4">
|
||||
- Restore `Addressable::IDNA.unicode_normalize_kc` as a deprecated method ([#504])
|
||||
|
||||
[#504]: https://github.com/sporkmonger/addressable/pull/504
|
||||
|
||||
# Addressable 2.8.3 <a name="v2.8.3">
|
||||
- Fix template expand level 2 hash support for non-string objects ([#499], [#498])
|
||||
|
||||
[#499]: https://github.com/sporkmonger/addressable/pull/499
|
||||
[#498]: https://github.com/sporkmonger/addressable/pull/498
|
||||
|
||||
# Addressable 2.8.2 <a name="v2.8.2">
|
||||
- Improve cache hits and JIT friendliness ([#486](https://github.com/sporkmonger/addressable/pull/486))
|
||||
- Improve code style and test coverage ([#482](https://github.com/sporkmonger/addressable/pull/482))
|
||||
- Ensure reset of deferred validation ([#481](https://github.com/sporkmonger/addressable/pull/481))
|
||||
- Resolve normalization differences between `IDNA::Native` and `IDNA::Pure` ([#408](https://github.com/sporkmonger/addressable/issues/408), [#492])
|
||||
- Remove redundant colon in `Addressable::URI::CharacterClasses::AUTHORITY` regex ([#438](https://github.com/sporkmonger/addressable/pull/438)) (accidentally reverted by [#449] merge but [added back](https://github.com/sporkmonger/addressable/pull/492#discussion_r1105125280) in [#492])
|
||||
|
||||
[#492]: https://github.com/sporkmonger/addressable/pull/492
|
||||
|
||||
# Addressable 2.8.1 <a name="v2.8.1">
|
||||
- refactor `Addressable::URI.normalize_path` to address linter offenses ([#430](https://github.com/sporkmonger/addressable/pull/430))
|
||||
- update gemspec to reflect supported Ruby versions ([#466], [#464], [#463])
|
||||
- compatibility w/ public_suffix 5.x ([#466], [#465], [#460])
|
||||
- fixes "invalid byte sequence in UTF-8" exception when unencoding URLs containing non UTF-8 characters ([#459](https://github.com/sporkmonger/addressable/pull/459))
|
||||
- `Ractor` compatibility ([#449])
|
||||
- use the whole string instead of a single line for template match ([#431](https://github.com/sporkmonger/addressable/pull/431))
|
||||
- force UTF-8 encoding only if needed ([#341](https://github.com/sporkmonger/addressable/pull/341))
|
||||
|
||||
[#449]: https://github.com/sporkmonger/addressable/pull/449
|
||||
[#460]: https://github.com/sporkmonger/addressable/pull/460
|
||||
[#463]: https://github.com/sporkmonger/addressable/pull/463
|
||||
[#464]: https://github.com/sporkmonger/addressable/pull/464
|
||||
[#465]: https://github.com/sporkmonger/addressable/pull/465
|
||||
[#466]: https://github.com/sporkmonger/addressable/pull/466
|
||||
|
||||
# Addressable 2.8.0 <a name="v2.8.0">
|
||||
- fixes ReDoS vulnerability in Addressable::Template#match
|
||||
- no longer replaces `+` with spaces in queries for non-http(s) schemes
|
||||
- fixed encoding ipv6 literals
|
||||
- the `:compacted` flag for `normalized_query` now dedupes parameters
|
||||
- fix broken `escape_component` alias
|
||||
- dropping support for Ruby 2.0 and 2.1
|
||||
- adding Ruby 3.0 compatibility for development tasks
|
||||
- drop support for `rack-mount` and remove Addressable::Template#generate
|
||||
- performance improvements
|
||||
- switch CI/CD to GitHub Actions
|
||||
|
||||
# Addressable 2.7.0 <a name="v2.7.0">
|
||||
- added `:compacted` flag to `normalized_query`
|
||||
- `heuristic_parse` handles `mailto:` more intuitively
|
||||
- dropped explicit support for JRuby 9.0.5.0
|
||||
- compatibility w/ public_suffix 4.x
|
||||
- performance improvements
|
||||
|
||||
# Addressable 2.6.0 <a name="v2.6.0">
|
||||
- added `tld=` method to allow assignment to the public suffix
|
||||
- most `heuristic_parse` patterns are now case-insensitive
|
||||
- `heuristic_parse` handles more `file://` URI variations
|
||||
- fixes bug in `heuristic_parse` when uri starts with digit
|
||||
- fixes bug in `request_uri=` with query strings
|
||||
- fixes template issues with `nil` and `?` operator
|
||||
- `frozen_string_literal` pragmas added
|
||||
- minor performance improvements in regexps
|
||||
- fixes to eliminate warnings
|
||||
|
||||
# Addressable 2.5.2 <a name="v2.5.2">
|
||||
- better support for frozen string literals
|
||||
- fixed bug w/ uppercase characters in scheme
|
||||
- IDNA errors w/ emoji URLs
|
||||
- compatibility w/ public_suffix 3.x
|
||||
|
||||
# Addressable 2.5.1 <a name="v2.5.1">
|
||||
- allow unicode normalization to be disabled for URI Template expansion
|
||||
- removed duplicate test
|
||||
|
||||
# Addressable 2.5.0 <a name="v2.5.0">
|
||||
- dropping support for Ruby 1.9
|
||||
- adding support for Ruby 2.4 preview
|
||||
- add support for public suffixes and tld; first runtime dependency
|
||||
- hostname escaping should match RFC; underscores in hostnames no longer escaped
|
||||
- paths beginning with // and missing an authority are now considered invalid
|
||||
- validation now also takes place after setting a path
|
||||
- handle backslashes in authority more like a browser for `heuristic_parse`
|
||||
- unescaped backslashes in host now raise an `InvalidURIError`
|
||||
- `merge!`, `join!`, `omit!` and `normalize!` don't disable deferred validation
|
||||
- `heuristic_parse` now trims whitespace before parsing
|
||||
- host parts longer than 63 bytes will be ignored and not passed to libidn
|
||||
- normalized values always encoded as UTF-8
|
||||
|
||||
# Addressable 2.4.0 <a name="v2.4.0">
|
||||
- support for 1.8.x dropped
|
||||
- double quotes in a host now raises an error
|
||||
- newlines in host will no longer get unescaped during normalization
|
||||
- stricter handling of bogus scheme values
|
||||
- stricter handling of encoded port values
|
||||
- calling `require 'addressable'` will now load both the URI and Template files
|
||||
- assigning to the `hostname` component with an `IPAddr` object is now supported
|
||||
- assigning to the `origin` component is now supported
|
||||
- fixed minor bug where an exception would be thrown for a missing ACE suffix
|
||||
- better partial expansion of URI templates
|
||||
|
||||
# Addressable 2.3.8 <a name="v2.3.8">
|
||||
- fix warnings
|
||||
- update dependency gems
|
||||
- support for 1.8.x officially deprecated
|
||||
|
||||
# Addressable 2.3.7 <a name="v2.3.7">
|
||||
- fix scenario in which invalid URIs don't get an exception until inspected
|
||||
- handle hostnames with two adjacent periods correctly
|
||||
- upgrade of RSpec
|
||||
|
||||
# Addressable 2.3.6 <a name="v2.3.6">
|
||||
- normalization drops empty query string
|
||||
- better handling in template extract for missing values
|
||||
- template modifier for `'?'` now treated as optional
|
||||
- fixed issue where character class parameters were modified
|
||||
- templates can now be tested for equality
|
||||
- added `:sorted` option to normalization of query strings
|
||||
- fixed issue with normalization of hosts given in `'example.com.'` form
|
||||
|
||||
# Addressable 2.3.5 <a name="v2.3.5">
|
||||
- added Addressable::URI#empty? method
|
||||
- Addressable::URI#hostname methods now strip square brackets from IPv6 hosts
|
||||
- compatibility with Net::HTTP in Ruby 2.0.0
|
||||
- Addressable::URI#route_from should always give relative URIs
|
||||
|
||||
# Addressable 2.3.4 <a name="v2.3.4">
|
||||
- fixed issue with encoding altering its inputs
|
||||
- query string normalization now leaves ';' characters alone
|
||||
- FakeFS is detected before attempting to load unicode tables
|
||||
- additional testing to ensure frozen objects don't cause problems
|
||||
|
||||
# Addressable 2.3.3 <a name="v2.3.3">
|
||||
- fixed issue with converting common primitives during template expansion
|
||||
- fixed port encoding issue
|
||||
- removed a few warnings
|
||||
- normalize should now ignore %2B in query strings
|
||||
- the IDNA logic should now be handled by libidn in Ruby 1.9
|
||||
- no template match should now result in nil instead of an empty MatchData
|
||||
- added license information to gemspec
|
||||
|
||||
# Addressable 2.3.2 <a name="v2.3.2">
|
||||
- added Addressable::URI#default_port method
|
||||
- fixed issue with Marshalling Unicode data on Windows
|
||||
- improved heuristic parsing to better handle IPv4 addresses
|
||||
|
||||
# Addressable 2.3.1 <a name="v2.3.1">
|
||||
- fixed missing unicode data file
|
||||
|
||||
# Addressable 2.3.0 <a name="v2.3.0">
|
||||
- updated Addressable::Template to use RFC 6570, level 4
|
||||
- fixed compatibility problems with some versions of Ruby
|
||||
- moved unicode tables into a data file for performance reasons
|
||||
- removing support for multiple query value notations
|
||||
|
||||
# Addressable 2.2.8 <a name="v2.2.8">
|
||||
- fixed issues with dot segment removal code
|
||||
- form encoding can now handle multiple values per key
|
||||
- updated development environment
|
||||
|
||||
# Addressable 2.2.7 <a name="v2.2.7">
|
||||
- fixed issues related to Addressable::URI#query_values=
|
||||
- the Addressable::URI.parse method is now polymorphic
|
||||
|
||||
# Addressable 2.2.6 <a name="v2.2.6">
|
||||
- changed the way ambiguous paths are handled
|
||||
- fixed bug with frozen URIs
|
||||
- https supported in heuristic parsing
|
||||
|
||||
# Addressable 2.2.5 <a name="v2.2.5">
|
||||
- 'parsing' a pre-parsed URI object is now a dup operation
|
||||
- introduced conditional support for libidn
|
||||
- fixed normalization issue on ampersands in query strings
|
||||
- added additional tests around handling of query strings
|
||||
|
||||
# Addressable 2.2.4 <a name="v2.2.4">
|
||||
- added origin support from draft-ietf-websec-origin-00
|
||||
- resolved issue with attempting to navigate below root
|
||||
- fixed bug with string splitting in query strings
|
||||
|
||||
# Addressable 2.2.3 <a name="v2.2.3">
|
||||
- added :flat_array notation for query strings
|
||||
|
||||
# Addressable 2.2.2 <a name="v2.2.2">
|
||||
- fixed issue with percent escaping of '+' character in query strings
|
||||
|
||||
# Addressable 2.2.1 <a name="v2.2.1">
|
||||
- added support for application/x-www-form-urlencoded.
|
||||
|
||||
# Addressable 2.2.0 <a name="v2.2.0">
|
||||
- added site methods
|
||||
- improved documentation
|
||||
|
||||
# Addressable 2.1.2 <a name="v2.1.2">
|
||||
- added HTTP request URI methods
|
||||
- better handling of Windows file paths
|
||||
- validation_deferred boolean replaced with defer_validation block
|
||||
- normalization of percent-encoded paths should now be correct
|
||||
- fixed issue with constructing URIs with relative paths
|
||||
- fixed warnings
|
||||
|
||||
# Addressable 2.1.1 <a name="v2.1.1">
|
||||
- more type checking changes
|
||||
- fixed issue with unicode normalization
|
||||
- added method to find template defaults
|
||||
- symbolic keys are now allowed in template mappings
|
||||
- numeric values and symbolic values are now allowed in template mappings
|
||||
|
||||
# Addressable 2.1.0 <a name="v2.1.0">
|
||||
- refactored URI template support out into its own class
|
||||
- removed extract method due to being useless and unreliable
|
||||
- removed Addressable::URI.expand_template
|
||||
- removed Addressable::URI#extract_mapping
|
||||
- added partial template expansion
|
||||
- fixed minor bugs in the parse and heuristic_parse methods
|
||||
- fixed incompatibility with Ruby 1.9.1
|
||||
- fixed bottleneck in Addressable::URI#hash and Addressable::URI#to_s
|
||||
- fixed unicode normalization exception
|
||||
- updated query_values methods to better handle subscript notation
|
||||
- worked around issue with freezing URIs
|
||||
- improved specs
|
||||
|
||||
# Addressable 2.0.2 <a name="v2.0.2">
|
||||
- fixed issue with URI template expansion
|
||||
- fixed issue with percent escaping characters 0-15
|
||||
|
||||
# Addressable 2.0.1 <a name="v2.0.1">
|
||||
- fixed issue with query string assignment
|
||||
- fixed issue with improperly encoded components
|
||||
|
||||
# Addressable 2.0.0 <a name="v2.0.0">
|
||||
- the initialize method now takes an options hash as its only parameter
|
||||
- added query_values method to URI class
|
||||
- completely replaced IDNA implementation with pure Ruby
|
||||
- renamed Addressable::ADDRESSABLE_VERSION to Addressable::VERSION
|
||||
- completely reworked the Rakefile
|
||||
- changed the behavior of the port method significantly
|
||||
- Addressable::URI.encode_segment, Addressable::URI.unencode_segment renamed
|
||||
- documentation is now in YARD format
|
||||
- more rigorous type checking
|
||||
- to_str method implemented, implicit conversion to Strings now allowed
|
||||
- Addressable::URI#omit method added, Addressable::URI#merge method replaced
|
||||
- updated URI Template code to match v 03 of the draft spec
|
||||
- added a bunch of new specifications
|
||||
|
||||
# Addressable 1.0.4 <a name="v1.0.4">
|
||||
- switched to using RSpec's pending system for specs that rely on IDN
|
||||
- fixed issue with creating URIs with paths that are not prefixed with '/'
|
||||
|
||||
# Addressable 1.0.3 <a name="v1.0.3">
|
||||
- implemented a hash method
|
||||
|
||||
# Addressable 1.0.2 <a name="v1.0.2">
|
||||
- fixed minor bug with the extract_mapping method
|
||||
|
||||
# Addressable 1.0.1 <a name="v1.0.1">
|
||||
- fixed minor bug with the extract_mapping method
|
||||
|
||||
# Addressable 1.0.0 <a name="v1.0.0">
|
||||
- heuristic parse method added
|
||||
- parsing is slightly more strict
|
||||
- replaced to_h with to_hash
|
||||
- fixed routing methods
|
||||
- improved specifications
|
||||
- improved heckle rake task
|
||||
- no surviving heckle mutations
|
||||
|
||||
# Addressable 0.1.2 <a name="v0.1.2">
|
||||
- improved normalization
|
||||
- fixed bug in joining algorithm
|
||||
- updated specifications
|
||||
|
||||
# Addressable 0.1.1 <a name="v0.1.1">
|
||||
- updated documentation
|
||||
- added URI Template variable extraction
|
||||
|
||||
# Addressable 0.1.0 <a name="v0.1.0">
|
||||
- initial release
|
||||
- implementation based on RFC 3986, 3987
|
||||
- support for IRIs via libidn
|
||||
- support for the URI Template draft spec
|
||||
@@ -0,0 +1,31 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gemspec
|
||||
|
||||
group :test do
|
||||
gem 'bigdecimal' if RUBY_VERSION > '2.4'
|
||||
gem 'rspec', '~> 3.8'
|
||||
gem 'rspec-its', '~> 1.3'
|
||||
end
|
||||
|
||||
group :coverage do
|
||||
gem "coveralls", "> 0.7", require: false, platforms: :mri
|
||||
gem "simplecov", require: false
|
||||
end
|
||||
|
||||
group :development do
|
||||
gem 'launchy', '~> 2.4', '>= 2.4.3'
|
||||
gem 'redcarpet', :platform => :mri_19
|
||||
gem 'yard'
|
||||
end
|
||||
|
||||
group :test, :development do
|
||||
gem 'memory_profiler'
|
||||
gem "rake", ">= 12.3.3"
|
||||
end
|
||||
|
||||
unless ENV["IDNA_MODE"] == "pure"
|
||||
gem "idn-ruby", platform: :mri
|
||||
end
|
||||
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,121 @@
|
||||
# Addressable
|
||||
|
||||
<dl>
|
||||
<dt>Homepage</dt><dd><a href="https://github.com/sporkmonger/addressable">github.com/sporkmonger/addressable</a></dd>
|
||||
<dt>Author</dt><dd><a href="mailto:bob@sporkmonger.com">Bob Aman</a></dd>
|
||||
<dt>Copyright</dt><dd>Copyright © Bob Aman</dd>
|
||||
<dt>License</dt><dd>Apache 2.0</dd>
|
||||
</dl>
|
||||
|
||||
[][gem]
|
||||
[][actions]
|
||||
[][coveralls]
|
||||
[][inch]
|
||||
|
||||
[gem]: https://rubygems.org/gems/addressable
|
||||
[actions]: https://github.com/sporkmonger/addressable/actions
|
||||
[coveralls]: https://coveralls.io/r/sporkmonger/addressable
|
||||
[inch]: https://inch-ci.org/github/sporkmonger/addressable
|
||||
|
||||
# Description
|
||||
|
||||
Addressable is an alternative implementation to the URI implementation
|
||||
that is part of Ruby's standard library. It is flexible, offers heuristic
|
||||
parsing, and additionally provides extensive support for IRIs and URI templates.
|
||||
|
||||
Addressable closely conforms to RFC 3986, RFC 3987, and RFC 6570 (level 4).
|
||||
|
||||
# Reference
|
||||
|
||||
- {Addressable::URI}
|
||||
- {Addressable::Template}
|
||||
|
||||
# Example usage
|
||||
|
||||
```ruby
|
||||
require "addressable/uri"
|
||||
|
||||
uri = Addressable::URI.parse("http://example.com/path/to/resource/")
|
||||
uri.scheme
|
||||
#=> "http"
|
||||
uri.host
|
||||
#=> "example.com"
|
||||
uri.path
|
||||
#=> "/path/to/resource/"
|
||||
|
||||
uri = Addressable::URI.parse("http://www.詹姆斯.com/")
|
||||
uri.normalize
|
||||
#=> #<Addressable::URI:0xc9a4c8 URI:http://www.xn--8ws00zhy3a.com/>
|
||||
```
|
||||
|
||||
|
||||
# URI Templates
|
||||
|
||||
For more details, see [RFC 6570](https://www.rfc-editor.org/rfc/rfc6570.txt).
|
||||
|
||||
|
||||
```ruby
|
||||
|
||||
require "addressable/template"
|
||||
|
||||
template = Addressable::Template.new("http://example.com/{?query*}")
|
||||
template.expand({
|
||||
"query" => {
|
||||
'foo' => 'bar',
|
||||
'color' => 'red'
|
||||
}
|
||||
})
|
||||
#=> #<Addressable::URI:0xc9d95c URI:http://example.com/?foo=bar&color=red>
|
||||
|
||||
template = Addressable::Template.new("http://example.com/{?one,two,three}")
|
||||
template.partial_expand({"one" => "1", "three" => 3}).pattern
|
||||
#=> "http://example.com/?one=1{&two}&three=3"
|
||||
|
||||
template = Addressable::Template.new(
|
||||
"http://{host}{/segments*}/{?one,two,bogus}{#fragment}"
|
||||
)
|
||||
uri = Addressable::URI.parse(
|
||||
"http://example.com/a/b/c/?one=1&two=2#foo"
|
||||
)
|
||||
template.extract(uri)
|
||||
#=>
|
||||
# {
|
||||
# "host" => "example.com",
|
||||
# "segments" => ["a", "b", "c"],
|
||||
# "one" => "1",
|
||||
# "two" => "2",
|
||||
# "fragment" => "foo"
|
||||
# }
|
||||
```
|
||||
|
||||
# Install
|
||||
|
||||
```console
|
||||
$ gem install addressable
|
||||
```
|
||||
|
||||
You may optionally turn on native IDN support by installing libidn and the
|
||||
idn gem:
|
||||
|
||||
```console
|
||||
$ sudo apt-get install libidn11-dev # Debian/Ubuntu
|
||||
$ brew install libidn # OS X
|
||||
$ gem install idn-ruby
|
||||
```
|
||||
|
||||
# Semantic Versioning
|
||||
|
||||
This project uses [Semantic Versioning](https://semver.org/). You can (and should) specify your
|
||||
dependency using a pessimistic version constraint covering the major and minor
|
||||
values:
|
||||
|
||||
```ruby
|
||||
spec.add_dependency 'addressable', '~> 2.7'
|
||||
```
|
||||
|
||||
If you need a specific bug fix, you can also specify minimum tiny versions
|
||||
without preventing updates to the latest minor release:
|
||||
|
||||
```ruby
|
||||
spec.add_dependency 'addressable', '~> 2.3', '>= 2.3.7'
|
||||
```
|
||||
@@ -0,0 +1,37 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubygems'
|
||||
require 'rake'
|
||||
|
||||
require File.join(File.dirname(__FILE__), 'lib', 'addressable', 'version')
|
||||
|
||||
PKG_DISPLAY_NAME = 'Addressable'
|
||||
PKG_NAME = PKG_DISPLAY_NAME.downcase
|
||||
PKG_VERSION = Addressable::VERSION::STRING
|
||||
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
||||
|
||||
RELEASE_NAME = "REL #{PKG_VERSION}"
|
||||
|
||||
PKG_SUMMARY = "URI Implementation"
|
||||
PKG_DESCRIPTION = <<-TEXT
|
||||
Addressable is an alternative implementation to the URI implementation that is
|
||||
part of Ruby's standard library. It is flexible, offers heuristic parsing, and
|
||||
additionally provides extensive support for IRIs and URI templates.
|
||||
TEXT
|
||||
|
||||
PKG_FILES = FileList[
|
||||
"data/**/*",
|
||||
"lib/**/*.rb",
|
||||
"spec/**/*.rb",
|
||||
"tasks/**/*.rake",
|
||||
"addressable.gemspec",
|
||||
"CHANGELOG.md",
|
||||
"Gemfile",
|
||||
"LICENSE.txt",
|
||||
"README.md",
|
||||
"Rakefile",
|
||||
]
|
||||
|
||||
task :default => "spec"
|
||||
|
||||
Dir['tasks/**/*.rake'].each { |rake| load rake }
|
||||
@@ -0,0 +1,28 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# stub: addressable 2.8.7 ruby lib
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = "addressable".freeze
|
||||
s.version = "2.8.7".freeze
|
||||
|
||||
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
||||
s.metadata = { "changelog_uri" => "https://github.com/sporkmonger/addressable/blob/main/CHANGELOG.md#v2.8.7" } if s.respond_to? :metadata=
|
||||
s.require_paths = ["lib".freeze]
|
||||
s.authors = ["Bob Aman".freeze]
|
||||
s.date = "2024-06-21"
|
||||
s.description = "Addressable is an alternative implementation to the URI implementation that is\npart of Ruby's standard library. It is flexible, offers heuristic parsing, and\nadditionally provides extensive support for IRIs and URI templates.\n".freeze
|
||||
s.email = "bob@sporkmonger.com".freeze
|
||||
s.extra_rdoc_files = ["README.md".freeze]
|
||||
s.files = ["CHANGELOG.md".freeze, "Gemfile".freeze, "LICENSE.txt".freeze, "README.md".freeze, "Rakefile".freeze, "addressable.gemspec".freeze, "data/unicode.data".freeze, "lib/addressable.rb".freeze, "lib/addressable/idna.rb".freeze, "lib/addressable/idna/native.rb".freeze, "lib/addressable/idna/pure.rb".freeze, "lib/addressable/template.rb".freeze, "lib/addressable/uri.rb".freeze, "lib/addressable/version.rb".freeze, "spec/addressable/idna_spec.rb".freeze, "spec/addressable/net_http_compat_spec.rb".freeze, "spec/addressable/security_spec.rb".freeze, "spec/addressable/template_spec.rb".freeze, "spec/addressable/uri_spec.rb".freeze, "spec/spec_helper.rb".freeze, "tasks/clobber.rake".freeze, "tasks/gem.rake".freeze, "tasks/git.rake".freeze, "tasks/metrics.rake".freeze, "tasks/profile.rake".freeze, "tasks/rspec.rake".freeze, "tasks/yard.rake".freeze]
|
||||
s.homepage = "https://github.com/sporkmonger/addressable".freeze
|
||||
s.licenses = ["Apache-2.0".freeze]
|
||||
s.rdoc_options = ["--main".freeze, "README.md".freeze]
|
||||
s.required_ruby_version = Gem::Requirement.new(">= 2.2".freeze)
|
||||
s.rubygems_version = "3.5.11".freeze
|
||||
s.summary = "URI Implementation".freeze
|
||||
|
||||
s.specification_version = 4
|
||||
|
||||
s.add_runtime_dependency(%q<public_suffix>.freeze, [">= 2.0.2".freeze, "< 7.0".freeze])
|
||||
s.add_development_dependency(%q<bundler>.freeze, [">= 1.0".freeze, "< 3.0".freeze])
|
||||
end
|
||||
Binary file not shown.
@@ -0,0 +1,4 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'addressable/uri'
|
||||
require 'addressable/template'
|
||||
@@ -0,0 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#--
|
||||
# Copyright (C) Bob Aman
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#++
|
||||
|
||||
|
||||
begin
|
||||
require "addressable/idna/native"
|
||||
rescue LoadError
|
||||
# libidn or the idn gem was not available, fall back on a pure-Ruby
|
||||
# implementation...
|
||||
require "addressable/idna/pure"
|
||||
end
|
||||
@@ -0,0 +1,66 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#--
|
||||
# Copyright (C) Bob Aman
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#++
|
||||
|
||||
|
||||
require "idn"
|
||||
|
||||
module Addressable
|
||||
module IDNA
|
||||
def self.punycode_encode(value)
|
||||
IDN::Punycode.encode(value.to_s)
|
||||
end
|
||||
|
||||
def self.punycode_decode(value)
|
||||
IDN::Punycode.decode(value.to_s)
|
||||
end
|
||||
|
||||
class << self
|
||||
# @deprecated Use {String#unicode_normalize(:nfkc)} instead
|
||||
def unicode_normalize_kc(value)
|
||||
value.to_s.unicode_normalize(:nfkc)
|
||||
end
|
||||
|
||||
extend Gem::Deprecate
|
||||
deprecate :unicode_normalize_kc, "String#unicode_normalize(:nfkc)", 2023, 4
|
||||
end
|
||||
|
||||
def self.to_ascii(value)
|
||||
value.to_s.split('.', -1).map do |segment|
|
||||
if segment.size > 0 && segment.size < 64
|
||||
IDN::Idna.toASCII(segment, IDN::Idna::ALLOW_UNASSIGNED)
|
||||
elsif segment.size >= 64
|
||||
segment
|
||||
else
|
||||
''
|
||||
end
|
||||
end.join('.')
|
||||
end
|
||||
|
||||
def self.to_unicode(value)
|
||||
value.to_s.split('.', -1).map do |segment|
|
||||
if segment.size > 0 && segment.size < 64
|
||||
IDN::Idna.toUnicode(segment, IDN::Idna::ALLOW_UNASSIGNED)
|
||||
elsif segment.size >= 64
|
||||
segment
|
||||
else
|
||||
''
|
||||
end
|
||||
end.join('.')
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,505 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#--
|
||||
# Copyright (C) Bob Aman
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#++
|
||||
|
||||
|
||||
module Addressable
|
||||
module IDNA
|
||||
# This module is loosely based on idn_actionmailer by Mick Staugaard,
|
||||
# the unicode library by Yoshida Masato, and the punycode implementation
|
||||
# by Kazuhiro Nishiyama. Most of the code was copied verbatim, but
|
||||
# some reformatting was done, and some translation from C was done.
|
||||
#
|
||||
# Without their code to work from as a base, we'd all still be relying
|
||||
# on the presence of libidn. Which nobody ever seems to have installed.
|
||||
#
|
||||
# Original sources:
|
||||
# http://github.com/staugaard/idn_actionmailer
|
||||
# http://www.yoshidam.net/Ruby.html#unicode
|
||||
# http://rubyforge.org/frs/?group_id=2550
|
||||
|
||||
|
||||
UNICODE_TABLE = File.expand_path(
|
||||
File.join(File.dirname(__FILE__), '../../..', 'data/unicode.data')
|
||||
)
|
||||
|
||||
ACE_PREFIX = "xn--"
|
||||
|
||||
UTF8_REGEX = /\A(?:
|
||||
[\x09\x0A\x0D\x20-\x7E] # ASCII
|
||||
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
|
||||
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
|
||||
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
|
||||
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
|
||||
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
|
||||
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4nil5
|
||||
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
|
||||
)*\z/mnx
|
||||
|
||||
UTF8_REGEX_MULTIBYTE = /(?:
|
||||
[\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
|
||||
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
|
||||
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
|
||||
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
|
||||
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
|
||||
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4nil5
|
||||
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
|
||||
)/mnx
|
||||
|
||||
# :startdoc:
|
||||
|
||||
# Converts from a Unicode internationalized domain name to an ASCII
|
||||
# domain name as described in RFC 3490.
|
||||
def self.to_ascii(input)
|
||||
input = input.to_s unless input.is_a?(String)
|
||||
input = input.dup.force_encoding(Encoding::UTF_8).unicode_normalize(:nfkc)
|
||||
if input.respond_to?(:force_encoding)
|
||||
input.force_encoding(Encoding::ASCII_8BIT)
|
||||
end
|
||||
if input =~ UTF8_REGEX && input =~ UTF8_REGEX_MULTIBYTE
|
||||
parts = unicode_downcase(input).split('.')
|
||||
parts.map! do |part|
|
||||
if part.respond_to?(:force_encoding)
|
||||
part.force_encoding(Encoding::ASCII_8BIT)
|
||||
end
|
||||
if part =~ UTF8_REGEX && part =~ UTF8_REGEX_MULTIBYTE
|
||||
ACE_PREFIX + punycode_encode(part)
|
||||
else
|
||||
part
|
||||
end
|
||||
end
|
||||
parts.join('.')
|
||||
else
|
||||
input
|
||||
end
|
||||
end
|
||||
|
||||
# Converts from an ASCII domain name to a Unicode internationalized
|
||||
# domain name as described in RFC 3490.
|
||||
def self.to_unicode(input)
|
||||
input = input.to_s unless input.is_a?(String)
|
||||
parts = input.split('.')
|
||||
parts.map! do |part|
|
||||
if part =~ /^#{ACE_PREFIX}(.+)/
|
||||
begin
|
||||
punycode_decode(part[/^#{ACE_PREFIX}(.+)/, 1])
|
||||
rescue Addressable::IDNA::PunycodeBadInput
|
||||
# toUnicode is explicitly defined as never-fails by the spec
|
||||
part
|
||||
end
|
||||
else
|
||||
part
|
||||
end
|
||||
end
|
||||
output = parts.join('.')
|
||||
if output.respond_to?(:force_encoding)
|
||||
output.force_encoding(Encoding::UTF_8)
|
||||
end
|
||||
output
|
||||
end
|
||||
|
||||
class << self
|
||||
# @deprecated Use {String#unicode_normalize(:nfkc)} instead
|
||||
def unicode_normalize_kc(value)
|
||||
value.to_s.unicode_normalize(:nfkc)
|
||||
end
|
||||
|
||||
extend Gem::Deprecate
|
||||
deprecate :unicode_normalize_kc, "String#unicode_normalize(:nfkc)", 2023, 4
|
||||
end
|
||||
|
||||
##
|
||||
# Unicode aware downcase method.
|
||||
#
|
||||
# @api private
|
||||
# @param [String] input
|
||||
# The input string.
|
||||
# @return [String] The downcased result.
|
||||
def self.unicode_downcase(input)
|
||||
input = input.to_s unless input.is_a?(String)
|
||||
unpacked = input.unpack("U*")
|
||||
unpacked.map! { |codepoint| lookup_unicode_lowercase(codepoint) }
|
||||
return unpacked.pack("U*")
|
||||
end
|
||||
private_class_method :unicode_downcase
|
||||
|
||||
def self.lookup_unicode_lowercase(codepoint)
|
||||
codepoint_data = UNICODE_DATA[codepoint]
|
||||
(codepoint_data ?
|
||||
(codepoint_data[UNICODE_DATA_LOWERCASE] || codepoint) :
|
||||
codepoint)
|
||||
end
|
||||
private_class_method :lookup_unicode_lowercase
|
||||
|
||||
UNICODE_DATA_COMBINING_CLASS = 0
|
||||
UNICODE_DATA_EXCLUSION = 1
|
||||
UNICODE_DATA_CANONICAL = 2
|
||||
UNICODE_DATA_COMPATIBILITY = 3
|
||||
UNICODE_DATA_UPPERCASE = 4
|
||||
UNICODE_DATA_LOWERCASE = 5
|
||||
UNICODE_DATA_TITLECASE = 6
|
||||
|
||||
begin
|
||||
if defined?(FakeFS)
|
||||
fakefs_state = FakeFS.activated?
|
||||
FakeFS.deactivate!
|
||||
end
|
||||
# This is a sparse Unicode table. Codepoints without entries are
|
||||
# assumed to have the value: [0, 0, nil, nil, nil, nil, nil]
|
||||
UNICODE_DATA = File.open(UNICODE_TABLE, "rb") do |file|
|
||||
Marshal.load(file.read)
|
||||
end
|
||||
ensure
|
||||
if defined?(FakeFS)
|
||||
FakeFS.activate! if fakefs_state
|
||||
end
|
||||
end
|
||||
|
||||
COMPOSITION_TABLE = {}
|
||||
UNICODE_DATA.each do |codepoint, data|
|
||||
canonical = data[UNICODE_DATA_CANONICAL]
|
||||
exclusion = data[UNICODE_DATA_EXCLUSION]
|
||||
|
||||
if canonical && exclusion == 0
|
||||
COMPOSITION_TABLE[canonical.unpack("C*")] = codepoint
|
||||
end
|
||||
end
|
||||
|
||||
UNICODE_MAX_LENGTH = 256
|
||||
ACE_MAX_LENGTH = 256
|
||||
|
||||
PUNYCODE_BASE = 36
|
||||
PUNYCODE_TMIN = 1
|
||||
PUNYCODE_TMAX = 26
|
||||
PUNYCODE_SKEW = 38
|
||||
PUNYCODE_DAMP = 700
|
||||
PUNYCODE_INITIAL_BIAS = 72
|
||||
PUNYCODE_INITIAL_N = 0x80
|
||||
PUNYCODE_DELIMITER = 0x2D
|
||||
|
||||
PUNYCODE_MAXINT = 1 << 64
|
||||
|
||||
PUNYCODE_PRINT_ASCII =
|
||||
"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" +
|
||||
"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" +
|
||||
" !\"\#$%&'()*+,-./" +
|
||||
"0123456789:;<=>?" +
|
||||
"@ABCDEFGHIJKLMNO" +
|
||||
"PQRSTUVWXYZ[\\]^_" +
|
||||
"`abcdefghijklmno" +
|
||||
"pqrstuvwxyz{|}~\n"
|
||||
|
||||
# Input is invalid.
|
||||
class PunycodeBadInput < StandardError; end
|
||||
# Output would exceed the space provided.
|
||||
class PunycodeBigOutput < StandardError; end
|
||||
# Input needs wider integers to process.
|
||||
class PunycodeOverflow < StandardError; end
|
||||
|
||||
def self.punycode_encode(unicode)
|
||||
unicode = unicode.to_s unless unicode.is_a?(String)
|
||||
input = unicode.unpack("U*")
|
||||
output = [0] * (ACE_MAX_LENGTH + 1)
|
||||
input_length = input.size
|
||||
output_length = [ACE_MAX_LENGTH]
|
||||
|
||||
# Initialize the state
|
||||
n = PUNYCODE_INITIAL_N
|
||||
delta = out = 0
|
||||
max_out = output_length[0]
|
||||
bias = PUNYCODE_INITIAL_BIAS
|
||||
|
||||
# Handle the basic code points:
|
||||
input_length.times do |j|
|
||||
if punycode_basic?(input[j])
|
||||
if max_out - out < 2
|
||||
raise PunycodeBigOutput,
|
||||
"Output would exceed the space provided."
|
||||
end
|
||||
output[out] = input[j]
|
||||
out += 1
|
||||
end
|
||||
end
|
||||
|
||||
h = b = out
|
||||
|
||||
# h is the number of code points that have been handled, b is the
|
||||
# number of basic code points, and out is the number of characters
|
||||
# that have been output.
|
||||
|
||||
if b > 0
|
||||
output[out] = PUNYCODE_DELIMITER
|
||||
out += 1
|
||||
end
|
||||
|
||||
# Main encoding loop:
|
||||
|
||||
while h < input_length
|
||||
# All non-basic code points < n have been
|
||||
# handled already. Find the next larger one:
|
||||
|
||||
m = PUNYCODE_MAXINT
|
||||
input_length.times do |j|
|
||||
m = input[j] if (n...m) === input[j]
|
||||
end
|
||||
|
||||
# Increase delta enough to advance the decoder's
|
||||
# <n,i> state to <m,0>, but guard against overflow:
|
||||
|
||||
if m - n > (PUNYCODE_MAXINT - delta) / (h + 1)
|
||||
raise PunycodeOverflow, "Input needs wider integers to process."
|
||||
end
|
||||
delta += (m - n) * (h + 1)
|
||||
n = m
|
||||
|
||||
input_length.times do |j|
|
||||
# Punycode does not need to check whether input[j] is basic:
|
||||
if input[j] < n
|
||||
delta += 1
|
||||
if delta == 0
|
||||
raise PunycodeOverflow,
|
||||
"Input needs wider integers to process."
|
||||
end
|
||||
end
|
||||
|
||||
if input[j] == n
|
||||
# Represent delta as a generalized variable-length integer:
|
||||
|
||||
q = delta; k = PUNYCODE_BASE
|
||||
while true
|
||||
if out >= max_out
|
||||
raise PunycodeBigOutput,
|
||||
"Output would exceed the space provided."
|
||||
end
|
||||
t = (
|
||||
if k <= bias
|
||||
PUNYCODE_TMIN
|
||||
elsif k >= bias + PUNYCODE_TMAX
|
||||
PUNYCODE_TMAX
|
||||
else
|
||||
k - bias
|
||||
end
|
||||
)
|
||||
break if q < t
|
||||
output[out] =
|
||||
punycode_encode_digit(t + (q - t) % (PUNYCODE_BASE - t))
|
||||
out += 1
|
||||
q = (q - t) / (PUNYCODE_BASE - t)
|
||||
k += PUNYCODE_BASE
|
||||
end
|
||||
|
||||
output[out] = punycode_encode_digit(q)
|
||||
out += 1
|
||||
bias = punycode_adapt(delta, h + 1, h == b)
|
||||
delta = 0
|
||||
h += 1
|
||||
end
|
||||
end
|
||||
|
||||
delta += 1
|
||||
n += 1
|
||||
end
|
||||
|
||||
output_length[0] = out
|
||||
|
||||
outlen = out
|
||||
outlen.times do |j|
|
||||
c = output[j]
|
||||
unless c >= 0 && c <= 127
|
||||
raise StandardError, "Invalid output char."
|
||||
end
|
||||
unless PUNYCODE_PRINT_ASCII[c]
|
||||
raise PunycodeBadInput, "Input is invalid."
|
||||
end
|
||||
end
|
||||
|
||||
output[0..outlen].map { |x| x.chr }.join("").sub(/\0+\z/, "")
|
||||
end
|
||||
private_class_method :punycode_encode
|
||||
|
||||
def self.punycode_decode(punycode)
|
||||
input = []
|
||||
output = []
|
||||
|
||||
if ACE_MAX_LENGTH * 2 < punycode.size
|
||||
raise PunycodeBigOutput, "Output would exceed the space provided."
|
||||
end
|
||||
punycode.each_byte do |c|
|
||||
unless c >= 0 && c <= 127
|
||||
raise PunycodeBadInput, "Input is invalid."
|
||||
end
|
||||
input.push(c)
|
||||
end
|
||||
|
||||
input_length = input.length
|
||||
output_length = [UNICODE_MAX_LENGTH]
|
||||
|
||||
# Initialize the state
|
||||
n = PUNYCODE_INITIAL_N
|
||||
|
||||
out = i = 0
|
||||
max_out = output_length[0]
|
||||
bias = PUNYCODE_INITIAL_BIAS
|
||||
|
||||
# Handle the basic code points: Let b be the number of input code
|
||||
# points before the last delimiter, or 0 if there is none, then
|
||||
# copy the first b code points to the output.
|
||||
|
||||
b = 0
|
||||
input_length.times do |j|
|
||||
b = j if punycode_delimiter?(input[j])
|
||||
end
|
||||
if b > max_out
|
||||
raise PunycodeBigOutput, "Output would exceed the space provided."
|
||||
end
|
||||
|
||||
b.times do |j|
|
||||
unless punycode_basic?(input[j])
|
||||
raise PunycodeBadInput, "Input is invalid."
|
||||
end
|
||||
output[out] = input[j]
|
||||
out+=1
|
||||
end
|
||||
|
||||
# Main decoding loop: Start just after the last delimiter if any
|
||||
# basic code points were copied; start at the beginning otherwise.
|
||||
|
||||
in_ = b > 0 ? b + 1 : 0
|
||||
while in_ < input_length
|
||||
|
||||
# in_ is the index of the next character to be consumed, and
|
||||
# out is the number of code points in the output array.
|
||||
|
||||
# Decode a generalized variable-length integer into delta,
|
||||
# which gets added to i. The overflow checking is easier
|
||||
# if we increase i as we go, then subtract off its starting
|
||||
# value at the end to obtain delta.
|
||||
|
||||
oldi = i; w = 1; k = PUNYCODE_BASE
|
||||
while true
|
||||
if in_ >= input_length
|
||||
raise PunycodeBadInput, "Input is invalid."
|
||||
end
|
||||
digit = punycode_decode_digit(input[in_])
|
||||
in_+=1
|
||||
if digit >= PUNYCODE_BASE
|
||||
raise PunycodeBadInput, "Input is invalid."
|
||||
end
|
||||
if digit > (PUNYCODE_MAXINT - i) / w
|
||||
raise PunycodeOverflow, "Input needs wider integers to process."
|
||||
end
|
||||
i += digit * w
|
||||
t = (
|
||||
if k <= bias
|
||||
PUNYCODE_TMIN
|
||||
elsif k >= bias + PUNYCODE_TMAX
|
||||
PUNYCODE_TMAX
|
||||
else
|
||||
k - bias
|
||||
end
|
||||
)
|
||||
break if digit < t
|
||||
if w > PUNYCODE_MAXINT / (PUNYCODE_BASE - t)
|
||||
raise PunycodeOverflow, "Input needs wider integers to process."
|
||||
end
|
||||
w *= PUNYCODE_BASE - t
|
||||
k += PUNYCODE_BASE
|
||||
end
|
||||
|
||||
bias = punycode_adapt(i - oldi, out + 1, oldi == 0)
|
||||
|
||||
# I was supposed to wrap around from out + 1 to 0,
|
||||
# incrementing n each time, so we'll fix that now:
|
||||
|
||||
if i / (out + 1) > PUNYCODE_MAXINT - n
|
||||
raise PunycodeOverflow, "Input needs wider integers to process."
|
||||
end
|
||||
n += i / (out + 1)
|
||||
i %= out + 1
|
||||
|
||||
# Insert n at position i of the output:
|
||||
|
||||
# not needed for Punycode:
|
||||
# raise PUNYCODE_INVALID_INPUT if decode_digit(n) <= base
|
||||
if out >= max_out
|
||||
raise PunycodeBigOutput, "Output would exceed the space provided."
|
||||
end
|
||||
|
||||
#memmove(output + i + 1, output + i, (out - i) * sizeof *output)
|
||||
output[i + 1, out - i] = output[i, out - i]
|
||||
output[i] = n
|
||||
i += 1
|
||||
|
||||
out += 1
|
||||
end
|
||||
|
||||
output_length[0] = out
|
||||
|
||||
output.pack("U*")
|
||||
end
|
||||
private_class_method :punycode_decode
|
||||
|
||||
def self.punycode_basic?(codepoint)
|
||||
codepoint < 0x80
|
||||
end
|
||||
private_class_method :punycode_basic?
|
||||
|
||||
def self.punycode_delimiter?(codepoint)
|
||||
codepoint == PUNYCODE_DELIMITER
|
||||
end
|
||||
private_class_method :punycode_delimiter?
|
||||
|
||||
def self.punycode_encode_digit(d)
|
||||
d + 22 + 75 * ((d < 26) ? 1 : 0)
|
||||
end
|
||||
private_class_method :punycode_encode_digit
|
||||
|
||||
# Returns the numeric value of a basic codepoint
|
||||
# (for use in representing integers) in the range 0 to
|
||||
# base - 1, or PUNYCODE_BASE if codepoint does not represent a value.
|
||||
def self.punycode_decode_digit(codepoint)
|
||||
if codepoint - 48 < 10
|
||||
codepoint - 22
|
||||
elsif codepoint - 65 < 26
|
||||
codepoint - 65
|
||||
elsif codepoint - 97 < 26
|
||||
codepoint - 97
|
||||
else
|
||||
PUNYCODE_BASE
|
||||
end
|
||||
end
|
||||
private_class_method :punycode_decode_digit
|
||||
|
||||
# Bias adaptation method
|
||||
def self.punycode_adapt(delta, numpoints, firsttime)
|
||||
delta = firsttime ? delta / PUNYCODE_DAMP : delta >> 1
|
||||
# delta >> 1 is a faster way of doing delta / 2
|
||||
delta += delta / numpoints
|
||||
difference = PUNYCODE_BASE - PUNYCODE_TMIN
|
||||
|
||||
k = 0
|
||||
while delta > (difference * PUNYCODE_TMAX) / 2
|
||||
delta /= difference
|
||||
k += PUNYCODE_BASE
|
||||
end
|
||||
|
||||
k + (difference + 1) * delta / (delta + PUNYCODE_SKEW)
|
||||
end
|
||||
private_class_method :punycode_adapt
|
||||
end
|
||||
# :startdoc:
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,31 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#--
|
||||
# Copyright (C) Bob Aman
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#++
|
||||
|
||||
|
||||
# Used to prevent the class/module from being loaded more than once
|
||||
if !defined?(Addressable::VERSION)
|
||||
module Addressable
|
||||
module VERSION
|
||||
MAJOR = 2
|
||||
MINOR = 8
|
||||
TINY = 7
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,302 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Copyright (C) Bob Aman
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
# Have to use RubyGems to load the idn gem.
|
||||
require "rubygems"
|
||||
|
||||
require "addressable/idna"
|
||||
|
||||
shared_examples_for "converting from unicode to ASCII" do
|
||||
it "should convert 'www.google.com' correctly" do
|
||||
expect(Addressable::IDNA.to_ascii("www.google.com")).to eq("www.google.com")
|
||||
end
|
||||
|
||||
long = 'AcinusFallumTrompetumNullunCreditumVisumEstAtCuadLongumEtCefallum.com'
|
||||
it "should convert '#{long}' correctly" do
|
||||
expect(Addressable::IDNA.to_ascii(long)).to eq(long)
|
||||
end
|
||||
|
||||
it "should convert 'www.詹姆斯.com' correctly" do
|
||||
expect(Addressable::IDNA.to_ascii(
|
||||
"www.詹姆斯.com"
|
||||
)).to eq("www.xn--8ws00zhy3a.com")
|
||||
end
|
||||
|
||||
it "also accepts unicode strings encoded as ascii-8bit" do
|
||||
expect(Addressable::IDNA.to_ascii(
|
||||
"www.詹姆斯.com".b
|
||||
)).to eq("www.xn--8ws00zhy3a.com")
|
||||
end
|
||||
|
||||
it "should convert 'www.Iñtërnâtiônàlizætiøn.com' correctly" do
|
||||
"www.Iñtërnâtiônàlizætiøn.com"
|
||||
expect(Addressable::IDNA.to_ascii(
|
||||
"www.I\xC3\xB1t\xC3\xABrn\xC3\xA2ti\xC3\xB4" +
|
||||
"n\xC3\xA0liz\xC3\xA6ti\xC3\xB8n.com"
|
||||
)).to eq("www.xn--itrntinliztin-vdb0a5exd8ewcye.com")
|
||||
end
|
||||
|
||||
it "should convert 'www.Iñtërnâtiônàlizætiøn.com' correctly" do
|
||||
expect(Addressable::IDNA.to_ascii(
|
||||
"www.In\xCC\x83te\xCC\x88rna\xCC\x82tio\xCC\x82n" +
|
||||
"a\xCC\x80liz\xC3\xA6ti\xC3\xB8n.com"
|
||||
)).to eq("www.xn--itrntinliztin-vdb0a5exd8ewcye.com")
|
||||
end
|
||||
|
||||
it "should convert " +
|
||||
"'www.ほんとうにながいわけのわからないどめいんめいのらべるまだながくしないとたりない.w3.mag.keio.ac.jp' " +
|
||||
"correctly" do
|
||||
expect(Addressable::IDNA.to_ascii(
|
||||
"www.\343\201\273\343\202\223\343\201\250\343\201\206\343\201\253\343" +
|
||||
"\201\252\343\201\214\343\201\204\343\202\217\343\201\221\343\201\256" +
|
||||
"\343\202\217\343\201\213\343\202\211\343\201\252\343\201\204\343\201" +
|
||||
"\251\343\202\201\343\201\204\343\202\223\343\202\201\343\201\204\343" +
|
||||
"\201\256\343\202\211\343\201\271\343\202\213\343\201\276\343\201\240" +
|
||||
"\343\201\252\343\201\214\343\201\217\343\201\227\343\201\252\343\201" +
|
||||
"\204\343\201\250\343\201\237\343\202\212\343\201\252\343\201\204." +
|
||||
"w3.mag.keio.ac.jp"
|
||||
)).to eq(
|
||||
"www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3" +
|
||||
"fg11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp"
|
||||
)
|
||||
end
|
||||
|
||||
it "should convert " +
|
||||
"'www.ほんとうにながいわけのわからないどめいんめいのらべるまだながくしないとたりない.w3.mag.keio.ac.jp' " +
|
||||
"correctly" do
|
||||
expect(Addressable::IDNA.to_ascii(
|
||||
"www.\343\201\273\343\202\223\343\201\250\343\201\206\343\201\253\343" +
|
||||
"\201\252\343\201\213\343\202\231\343\201\204\343\202\217\343\201\221" +
|
||||
"\343\201\256\343\202\217\343\201\213\343\202\211\343\201\252\343\201" +
|
||||
"\204\343\201\250\343\202\231\343\202\201\343\201\204\343\202\223\343" +
|
||||
"\202\201\343\201\204\343\201\256\343\202\211\343\201\270\343\202\231" +
|
||||
"\343\202\213\343\201\276\343\201\237\343\202\231\343\201\252\343\201" +
|
||||
"\213\343\202\231\343\201\217\343\201\227\343\201\252\343\201\204\343" +
|
||||
"\201\250\343\201\237\343\202\212\343\201\252\343\201\204." +
|
||||
"w3.mag.keio.ac.jp"
|
||||
)).to eq(
|
||||
"www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3" +
|
||||
"fg11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp"
|
||||
)
|
||||
end
|
||||
|
||||
it "should convert '点心和烤鸭.w3.mag.keio.ac.jp' correctly" do
|
||||
expect(Addressable::IDNA.to_ascii(
|
||||
"点心和烤鸭.w3.mag.keio.ac.jp"
|
||||
)).to eq("xn--0trv4xfvn8el34t.w3.mag.keio.ac.jp")
|
||||
end
|
||||
|
||||
it "should convert '가각갂갃간갅갆갇갈갉힢힣.com' correctly" do
|
||||
expect(Addressable::IDNA.to_ascii(
|
||||
"가각갂갃간갅갆갇갈갉힢힣.com"
|
||||
)).to eq("xn--o39acdefghijk5883jma.com")
|
||||
end
|
||||
|
||||
it "should convert " +
|
||||
"'\347\242\274\346\250\231\346\272\226\350" +
|
||||
"\220\254\345\234\213\347\242\274.com' correctly" do
|
||||
expect(Addressable::IDNA.to_ascii(
|
||||
"\347\242\274\346\250\231\346\272\226\350" +
|
||||
"\220\254\345\234\213\347\242\274.com"
|
||||
)).to eq("xn--9cs565brid46mda086o.com")
|
||||
end
|
||||
|
||||
it "should convert 'リ宠퐱〹.com' correctly" do
|
||||
expect(Addressable::IDNA.to_ascii(
|
||||
"\357\276\230\345\256\240\355\220\261\343\200\271.com"
|
||||
)).to eq("xn--eek174hoxfpr4k.com")
|
||||
end
|
||||
|
||||
it "should convert 'リ宠퐱卄.com' correctly" do
|
||||
expect(Addressable::IDNA.to_ascii(
|
||||
"\343\203\252\345\256\240\355\220\261\345\215\204.com"
|
||||
)).to eq("xn--eek174hoxfpr4k.com")
|
||||
end
|
||||
|
||||
it "should convert 'ᆵ' correctly" do
|
||||
expect(Addressable::IDNA.to_ascii(
|
||||
"\341\206\265"
|
||||
)).to eq("xn--4ud")
|
||||
end
|
||||
|
||||
it "should convert 'ᆵ' correctly" do
|
||||
expect(Addressable::IDNA.to_ascii(
|
||||
"\357\276\257"
|
||||
)).to eq("xn--4ud")
|
||||
end
|
||||
|
||||
it "should convert '🌹🌹🌹.ws' correctly" do
|
||||
expect(Addressable::IDNA.to_ascii(
|
||||
"\360\237\214\271\360\237\214\271\360\237\214\271.ws"
|
||||
)).to eq("xn--2h8haa.ws")
|
||||
end
|
||||
|
||||
it "should handle two adjacent '.'s correctly" do
|
||||
expect(Addressable::IDNA.to_ascii(
|
||||
"example..host"
|
||||
)).to eq("example..host")
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples_for "converting from ASCII to unicode" do
|
||||
long = 'AcinusFallumTrompetumNullunCreditumVisumEstAtCuadLongumEtCefallum.com'
|
||||
it "should convert '#{long}' correctly" do
|
||||
expect(Addressable::IDNA.to_unicode(long)).to eq(long)
|
||||
end
|
||||
|
||||
it "should return the identity conversion when punycode decode fails" do
|
||||
expect(Addressable::IDNA.to_unicode("xn--zckp1cyg1.sblo.jp")).to eq(
|
||||
"xn--zckp1cyg1.sblo.jp")
|
||||
end
|
||||
|
||||
it "should return the identity conversion when the ACE prefix has no suffix" do
|
||||
expect(Addressable::IDNA.to_unicode("xn--...-")).to eq("xn--...-")
|
||||
end
|
||||
|
||||
it "should convert 'www.google.com' correctly" do
|
||||
expect(Addressable::IDNA.to_unicode("www.google.com")).to eq(
|
||||
"www.google.com")
|
||||
end
|
||||
|
||||
it "should convert 'www.詹姆斯.com' correctly" do
|
||||
expect(Addressable::IDNA.to_unicode(
|
||||
"www.xn--8ws00zhy3a.com"
|
||||
)).to eq("www.詹姆斯.com")
|
||||
end
|
||||
|
||||
it "should convert '詹姆斯.com' correctly" do
|
||||
expect(Addressable::IDNA.to_unicode(
|
||||
"xn--8ws00zhy3a.com"
|
||||
)).to eq("詹姆斯.com")
|
||||
end
|
||||
|
||||
it "should convert 'www.iñtërnâtiônàlizætiøn.com' correctly" do
|
||||
expect(Addressable::IDNA.to_unicode(
|
||||
"www.xn--itrntinliztin-vdb0a5exd8ewcye.com"
|
||||
)).to eq("www.iñtërnâtiônàlizætiøn.com")
|
||||
end
|
||||
|
||||
it "should convert 'iñtërnâtiônàlizætiøn.com' correctly" do
|
||||
expect(Addressable::IDNA.to_unicode(
|
||||
"xn--itrntinliztin-vdb0a5exd8ewcye.com"
|
||||
)).to eq("iñtërnâtiônàlizætiøn.com")
|
||||
end
|
||||
|
||||
it "should convert " +
|
||||
"'www.ほんとうにながいわけのわからないどめいんめいのらべるまだながくしないとたりない.w3.mag.keio.ac.jp' " +
|
||||
"correctly" do
|
||||
expect(Addressable::IDNA.to_unicode(
|
||||
"www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3" +
|
||||
"fg11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp"
|
||||
)).to eq(
|
||||
"www.ほんとうにながいわけのわからないどめいんめいのらべるまだながくしないとたりない.w3.mag.keio.ac.jp"
|
||||
)
|
||||
end
|
||||
|
||||
it "should convert '点心和烤鸭.w3.mag.keio.ac.jp' correctly" do
|
||||
expect(Addressable::IDNA.to_unicode(
|
||||
"xn--0trv4xfvn8el34t.w3.mag.keio.ac.jp"
|
||||
)).to eq("点心和烤鸭.w3.mag.keio.ac.jp")
|
||||
end
|
||||
|
||||
it "should convert '가각갂갃간갅갆갇갈갉힢힣.com' correctly" do
|
||||
expect(Addressable::IDNA.to_unicode(
|
||||
"xn--o39acdefghijk5883jma.com"
|
||||
)).to eq("가각갂갃간갅갆갇갈갉힢힣.com")
|
||||
end
|
||||
|
||||
it "should convert " +
|
||||
"'\347\242\274\346\250\231\346\272\226\350" +
|
||||
"\220\254\345\234\213\347\242\274.com' correctly" do
|
||||
expect(Addressable::IDNA.to_unicode(
|
||||
"xn--9cs565brid46mda086o.com"
|
||||
)).to eq(
|
||||
"\347\242\274\346\250\231\346\272\226\350" +
|
||||
"\220\254\345\234\213\347\242\274.com"
|
||||
)
|
||||
end
|
||||
|
||||
it "should convert 'リ宠퐱卄.com' correctly" do
|
||||
expect(Addressable::IDNA.to_unicode(
|
||||
"xn--eek174hoxfpr4k.com"
|
||||
)).to eq("\343\203\252\345\256\240\355\220\261\345\215\204.com")
|
||||
end
|
||||
|
||||
it "should convert 'ᆵ' correctly" do
|
||||
expect(Addressable::IDNA.to_unicode(
|
||||
"xn--4ud"
|
||||
)).to eq("\341\206\265")
|
||||
end
|
||||
|
||||
it "should convert '🌹🌹🌹.ws' correctly" do
|
||||
expect(Addressable::IDNA.to_unicode(
|
||||
"xn--2h8haa.ws"
|
||||
)).to eq("\360\237\214\271\360\237\214\271\360\237\214\271.ws")
|
||||
end
|
||||
|
||||
it "should handle two adjacent '.'s correctly" do
|
||||
expect(Addressable::IDNA.to_unicode(
|
||||
"example..host"
|
||||
)).to eq("example..host")
|
||||
end
|
||||
end
|
||||
|
||||
describe Addressable::IDNA, "when using the pure-Ruby implementation" do
|
||||
before do
|
||||
Addressable.send(:remove_const, :IDNA)
|
||||
load "addressable/idna/pure.rb"
|
||||
end
|
||||
|
||||
it_should_behave_like "converting from unicode to ASCII"
|
||||
it_should_behave_like "converting from ASCII to unicode"
|
||||
|
||||
begin
|
||||
require "fiber"
|
||||
|
||||
it "should not blow up inside fibers" do
|
||||
f = Fiber.new do
|
||||
Addressable.send(:remove_const, :IDNA)
|
||||
load "addressable/idna/pure.rb"
|
||||
end
|
||||
f.resume
|
||||
end
|
||||
rescue LoadError
|
||||
# Fibers aren't supported in this version of Ruby, skip this test.
|
||||
warn('Fibers unsupported.')
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
require "idn"
|
||||
|
||||
describe Addressable::IDNA, "when using the native-code implementation" do
|
||||
before do
|
||||
Addressable.send(:remove_const, :IDNA)
|
||||
load "addressable/idna/native.rb"
|
||||
end
|
||||
|
||||
it_should_behave_like "converting from unicode to ASCII"
|
||||
it_should_behave_like "converting from ASCII to unicode"
|
||||
end
|
||||
rescue LoadError => error
|
||||
raise error if ENV["CI"] && TestHelper.native_supported?
|
||||
|
||||
# Cannot test the native implementation without libidn support.
|
||||
warn('Could not load native IDN implementation.')
|
||||
end
|
||||
@@ -0,0 +1,29 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Copyright (C) Bob Aman
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
require "addressable/uri"
|
||||
require "net/http"
|
||||
|
||||
describe Net::HTTP do
|
||||
it "should be compatible with Addressable" do
|
||||
response_body =
|
||||
Net::HTTP.get(Addressable::URI.parse('http://www.google.com/'))
|
||||
expect(response_body).not_to be_nil
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,58 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Copyright (C) Bob Aman
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
require "addressable/uri"
|
||||
|
||||
describe Addressable::URI, "when created with a URI known to cause crashes " +
|
||||
"in certain browsers" do
|
||||
it "should parse correctly" do
|
||||
uri = Addressable::URI.parse('%%30%30')
|
||||
expect(uri.path).to eq('%%30%30')
|
||||
expect(uri.normalize.path).to eq('%2500')
|
||||
end
|
||||
|
||||
it "should parse correctly as a full URI" do
|
||||
uri = Addressable::URI.parse('http://www.example.com/%%30%30')
|
||||
expect(uri.path).to eq('/%%30%30')
|
||||
expect(uri.normalize.path).to eq('/%2500')
|
||||
end
|
||||
end
|
||||
|
||||
describe Addressable::URI, "when created with a URI known to cause crashes " +
|
||||
"in certain browsers" do
|
||||
it "should parse correctly" do
|
||||
uri = Addressable::URI.parse('لُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ 冗')
|
||||
expect(uri.path).to eq('لُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ 冗')
|
||||
expect(uri.normalize.path).to eq(
|
||||
'%D9%84%D9%8F%D8%B5%D9%91%D8%A8%D9%8F%D9%84%D9%8F%D9%84%D8%B5%D9%91' +
|
||||
'%D8%A8%D9%8F%D8%B1%D8%B1%D9%8B%20%E0%A5%A3%20%E0%A5%A3h%20%E0%A5' +
|
||||
'%A3%20%E0%A5%A3%20%E5%86%97'
|
||||
)
|
||||
end
|
||||
|
||||
it "should parse correctly as a full URI" do
|
||||
uri = Addressable::URI.parse('http://www.example.com/لُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ 冗')
|
||||
expect(uri.path).to eq('/لُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ 冗')
|
||||
expect(uri.normalize.path).to eq(
|
||||
'/%D9%84%D9%8F%D8%B5%D9%91%D8%A8%D9%8F%D9%84%D9%8F%D9%84%D8%B5%D9%91' +
|
||||
'%D8%A8%D9%8F%D8%B1%D8%B1%D9%8B%20%E0%A5%A3%20%E0%A5%A3h%20%E0%A5' +
|
||||
'%A3%20%E0%A5%A3%20%E5%86%97'
|
||||
)
|
||||
end
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,33 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'bundler/setup'
|
||||
require 'rspec/its'
|
||||
|
||||
begin
|
||||
require 'coveralls'
|
||||
Coveralls.wear! do
|
||||
add_filter "spec/"
|
||||
add_filter "vendor/"
|
||||
end
|
||||
rescue LoadError
|
||||
warn "warning: coveralls gem not found; skipping Coveralls"
|
||||
require 'simplecov'
|
||||
SimpleCov.start do
|
||||
add_filter "spec/"
|
||||
add_filter "vendor/"
|
||||
end
|
||||
end if Gem.loaded_specs.key?("simplecov")
|
||||
|
||||
class TestHelper
|
||||
def self.native_supported?
|
||||
mri = RUBY_ENGINE == "ruby"
|
||||
windows = RUBY_PLATFORM.include?("mingw")
|
||||
|
||||
mri && !windows
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.warnings = true
|
||||
config.filter_run_when_matching :focus
|
||||
end
|
||||
@@ -0,0 +1,4 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
desc "Remove all build products"
|
||||
task "clobber"
|
||||
@@ -0,0 +1,95 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "rubygems/package_task"
|
||||
|
||||
namespace :gem do
|
||||
GEM_SPEC = Gem::Specification.new do |s|
|
||||
s.name = PKG_NAME
|
||||
s.version = PKG_VERSION
|
||||
s.summary = PKG_SUMMARY
|
||||
s.description = PKG_DESCRIPTION
|
||||
|
||||
s.files = PKG_FILES.to_a
|
||||
|
||||
s.extra_rdoc_files = %w( README.md )
|
||||
s.rdoc_options.concat ["--main", "README.md"]
|
||||
|
||||
if !s.respond_to?(:add_development_dependency)
|
||||
puts "Cannot build Gem with this version of RubyGems."
|
||||
exit(1)
|
||||
end
|
||||
|
||||
s.required_ruby_version = ">= 2.2"
|
||||
|
||||
s.add_runtime_dependency "public_suffix", ">= 2.0.2", "< 7.0"
|
||||
s.add_development_dependency "bundler", ">= 1.0", "< 3.0"
|
||||
|
||||
s.require_path = "lib"
|
||||
|
||||
s.author = "Bob Aman"
|
||||
s.email = "bob@sporkmonger.com"
|
||||
s.homepage = "https://github.com/sporkmonger/addressable"
|
||||
s.license = "Apache-2.0"
|
||||
s.metadata = {
|
||||
"changelog_uri" => "https://github.com/sporkmonger/addressable/blob/main/CHANGELOG.md#v#{PKG_VERSION}"
|
||||
}
|
||||
end
|
||||
|
||||
Gem::PackageTask.new(GEM_SPEC) do |p|
|
||||
p.gem_spec = GEM_SPEC
|
||||
p.need_tar = true
|
||||
p.need_zip = true
|
||||
end
|
||||
|
||||
desc "Generates .gemspec file"
|
||||
task :gemspec do
|
||||
spec_string = GEM_SPEC.to_ruby
|
||||
File.open("#{GEM_SPEC.name}.gemspec", "w") do |file|
|
||||
file.write spec_string
|
||||
end
|
||||
end
|
||||
|
||||
desc "Show information about the gem"
|
||||
task :debug do
|
||||
puts GEM_SPEC.to_ruby
|
||||
end
|
||||
|
||||
desc "Install the gem"
|
||||
task :install => ["clobber", "gem:package"] do
|
||||
sh "gem install --local ./pkg/#{GEM_SPEC.full_name}.gem"
|
||||
end
|
||||
|
||||
desc "Uninstall the gem"
|
||||
task :uninstall do
|
||||
installed_list = Gem.source_index.find_name(PKG_NAME)
|
||||
if installed_list &&
|
||||
(installed_list.collect { |s| s.version.to_s}.include?(PKG_VERSION))
|
||||
sh(
|
||||
"gem uninstall --version '#{PKG_VERSION}' " +
|
||||
"--ignore-dependencies --executables #{PKG_NAME}"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
desc "Reinstall the gem"
|
||||
task :reinstall => [:uninstall, :install]
|
||||
|
||||
desc "Package for release"
|
||||
task :release => ["gem:package", "gem:gemspec"] do |t|
|
||||
v = ENV["VERSION"] or abort "Must supply VERSION=x.y.z"
|
||||
abort "Versions don't match #{v} vs #{PROJ.version}" if v != PKG_VERSION
|
||||
pkg = "pkg/#{GEM_SPEC.full_name}"
|
||||
|
||||
changelog = File.open("CHANGELOG.md") { |file| file.read }
|
||||
|
||||
puts "Releasing #{PKG_NAME} v. #{PKG_VERSION}"
|
||||
Rake::Task["git:tag:create"].invoke
|
||||
end
|
||||
end
|
||||
|
||||
desc "Alias to gem:package"
|
||||
task "gem" => "gem:package"
|
||||
|
||||
task "gem:release" => "gem:gemspec"
|
||||
|
||||
task "clobber" => ["gem:clobber_package"]
|
||||
@@ -0,0 +1,47 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
namespace :git do
|
||||
namespace :tag do
|
||||
desc "List tags from the Git repository"
|
||||
task :list do
|
||||
tags = `git tag -l`
|
||||
tags.gsub!("\r", "")
|
||||
tags = tags.split("\n").sort {|a, b| b <=> a }
|
||||
puts tags.join("\n")
|
||||
end
|
||||
|
||||
desc "Create a new tag in the Git repository"
|
||||
task :create do
|
||||
changelog = File.open("CHANGELOG.md", "r") { |file| file.read }
|
||||
puts "-" * 80
|
||||
puts changelog
|
||||
puts "-" * 80
|
||||
puts
|
||||
|
||||
v = ENV["VERSION"] or abort "Must supply VERSION=x.y.z"
|
||||
abort "Versions don't match #{v} vs #{PKG_VERSION}" if v != PKG_VERSION
|
||||
|
||||
git_status = `git status`
|
||||
if git_status !~ /^nothing to commit/
|
||||
abort "Working directory isn't clean."
|
||||
end
|
||||
|
||||
tag = "#{PKG_NAME}-#{PKG_VERSION}"
|
||||
msg = "Release #{PKG_NAME}-#{PKG_VERSION}"
|
||||
|
||||
existing_tags = `git tag -l #{PKG_NAME}-*`.split('\n')
|
||||
if existing_tags.include?(tag)
|
||||
warn("Tag already exists, deleting...")
|
||||
unless system "git tag -d #{tag}"
|
||||
abort "Tag deletion failed."
|
||||
end
|
||||
end
|
||||
puts "Creating git tag '#{tag}'..."
|
||||
unless system "git tag -a -m \"#{msg}\" #{tag}"
|
||||
abort "Tag creation failed."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
task "gem:release" => "git:tag:create"
|
||||
@@ -0,0 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
namespace :metrics do
|
||||
task :lines do
|
||||
lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
|
||||
for file_name in FileList["lib/**/*.rb"]
|
||||
f = File.open(file_name)
|
||||
while line = f.gets
|
||||
lines += 1
|
||||
next if line =~ /^\s*$/
|
||||
next if line =~ /^\s*#/
|
||||
codelines += 1
|
||||
end
|
||||
puts "L: #{sprintf("%4d", lines)}, " +
|
||||
"LOC #{sprintf("%4d", codelines)} | #{file_name}"
|
||||
total_lines += lines
|
||||
total_codelines += codelines
|
||||
|
||||
lines, codelines = 0, 0
|
||||
end
|
||||
|
||||
puts "Total: Lines #{total_lines}, LOC #{total_codelines}"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,72 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
namespace :profile do
|
||||
desc "Profile Template match memory allocations"
|
||||
task :template_match_memory do
|
||||
require "memory_profiler"
|
||||
require "addressable/template"
|
||||
|
||||
start_at = Time.now.to_f
|
||||
template = Addressable::Template.new("http://example.com/{?one,two,three}")
|
||||
report = MemoryProfiler.report do
|
||||
30_000.times do
|
||||
template.match(
|
||||
"http://example.com/?one=one&two=floo&three=me"
|
||||
)
|
||||
end
|
||||
end
|
||||
end_at = Time.now.to_f
|
||||
print_options = { scale_bytes: true, normalize_paths: true }
|
||||
puts "\n\n"
|
||||
|
||||
if ENV["CI"]
|
||||
report.pretty_print(print_options)
|
||||
else
|
||||
t_allocated = report.scale_bytes(report.total_allocated_memsize)
|
||||
t_retained = report.scale_bytes(report.total_retained_memsize)
|
||||
|
||||
puts "Total allocated: #{t_allocated} (#{report.total_allocated} objects)"
|
||||
puts "Total retained: #{t_retained} (#{report.total_retained} objects)"
|
||||
puts "Took #{end_at - start_at} seconds"
|
||||
|
||||
FileUtils.mkdir_p("tmp")
|
||||
report.pretty_print(to_file: "tmp/memprof.txt", **print_options)
|
||||
end
|
||||
end
|
||||
|
||||
desc "Profile URI parse memory allocations"
|
||||
task :memory do
|
||||
require "memory_profiler"
|
||||
require "addressable/uri"
|
||||
if ENV["IDNA_MODE"] == "pure"
|
||||
Addressable.send(:remove_const, :IDNA)
|
||||
load "addressable/idna/pure.rb"
|
||||
end
|
||||
|
||||
start_at = Time.now.to_f
|
||||
report = MemoryProfiler.report do
|
||||
30_000.times do
|
||||
Addressable::URI.parse(
|
||||
"http://google.com/stuff/../?with_lots=of¶ms=asdff#!stuff"
|
||||
).normalize
|
||||
end
|
||||
end
|
||||
end_at = Time.now.to_f
|
||||
print_options = { scale_bytes: true, normalize_paths: true }
|
||||
puts "\n\n"
|
||||
|
||||
if ENV["CI"]
|
||||
report.pretty_print(**print_options)
|
||||
else
|
||||
t_allocated = report.scale_bytes(report.total_allocated_memsize)
|
||||
t_retained = report.scale_bytes(report.total_retained_memsize)
|
||||
|
||||
puts "Total allocated: #{t_allocated} (#{report.total_allocated} objects)"
|
||||
puts "Total retained: #{t_retained} (#{report.total_retained} objects)"
|
||||
puts "Took #{end_at - start_at} seconds"
|
||||
|
||||
FileUtils.mkdir_p("tmp")
|
||||
report.pretty_print(to_file: "tmp/memprof.txt", **print_options)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,23 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "rspec/core/rake_task"
|
||||
|
||||
namespace :spec do
|
||||
RSpec::Core::RakeTask.new(:simplecov) do |t|
|
||||
t.pattern = FileList['spec/**/*_spec.rb']
|
||||
t.rspec_opts = %w[--color --format documentation] unless ENV["CI"]
|
||||
end
|
||||
|
||||
namespace :simplecov do
|
||||
desc "Browse the code coverage report."
|
||||
task :browse => "spec:simplecov" do
|
||||
require "launchy"
|
||||
Launchy.open("coverage/index.html")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
desc "Alias to spec:simplecov"
|
||||
task "spec" => "spec:simplecov"
|
||||
|
||||
task "clobber" => ["spec:clobber_simplecov"]
|
||||
@@ -0,0 +1,29 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "rake"
|
||||
|
||||
begin
|
||||
require "yard"
|
||||
require "yard/rake/yardoc_task"
|
||||
|
||||
namespace :doc do
|
||||
desc "Generate Yardoc documentation"
|
||||
YARD::Rake::YardocTask.new do |yardoc|
|
||||
yardoc.name = "yard"
|
||||
yardoc.options = ["--verbose", "--markup", "markdown"]
|
||||
yardoc.files = FileList[
|
||||
"lib/**/*.rb", "ext/**/*.c",
|
||||
"README.md", "CHANGELOG.md", "LICENSE.txt"
|
||||
].exclude(/idna/)
|
||||
end
|
||||
end
|
||||
|
||||
task "clobber" => ["doc:clobber_yard"]
|
||||
|
||||
desc "Alias to doc:yard"
|
||||
task "doc" => "doc:yard"
|
||||
rescue LoadError
|
||||
# If yard isn't available, it's not the end of the world
|
||||
desc "Alias to doc:rdoc"
|
||||
task "doc" => "doc:rdoc"
|
||||
end
|
||||
Reference in New Issue
Block a user