addressable-2.3.8/ 0000755 0001750 0001750 00000000000 12522441742 013000 5 ustar lucas lucas addressable-2.3.8/LICENSE.txt 0000644 0001750 0001750 00000025143 12522441742 014630 0 ustar lucas lucas
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.
addressable-2.3.8/lib/ 0000755 0001750 0001750 00000000000 12522441742 013546 5 ustar lucas lucas addressable-2.3.8/lib/addressable/ 0000755 0001750 0001750 00000000000 12522441742 016017 5 ustar lucas lucas addressable-2.3.8/lib/addressable/idna.rb 0000644 0001750 0001750 00000001470 12522441742 017261 0 ustar lucas lucas # encoding:utf-8
#--
# Copyright (C) 2006-2015 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
addressable-2.3.8/lib/addressable/template.rb 0000644 0001750 0001750 00000077777 12522441742 020210 0 ustar lucas lucas # encoding:utf-8
#--
# Copyright (C) 2006-2015 Bob Aman
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#++
require "addressable/version"
require "addressable/uri"
module Addressable
##
# This is an implementation of a URI template based on
# RFC 6570 (http://tools.ietf.org/html/rfc6570).
class Template
# Constants used throughout the template code.
anything =
Addressable::URI::CharacterClasses::RESERVED +
Addressable::URI::CharacterClasses::UNRESERVED
variable_char_class =
Addressable::URI::CharacterClasses::ALPHA +
Addressable::URI::CharacterClasses::DIGIT + '_'
var_char =
"(?:(?:[#{variable_char_class}]|%[a-fA-F0-9][a-fA-F0-9])+)"
RESERVED =
"(?:[#{anything}]|%[a-fA-F0-9][a-fA-F0-9])"
UNRESERVED =
"(?:[#{
Addressable::URI::CharacterClasses::UNRESERVED
}]|%[a-fA-F0-9][a-fA-F0-9])"
variable =
"(?:#{var_char}(?:\\.?#{var_char})*)"
varspec =
"(?:(#{variable})(\\*|:\\d+)?)"
VARNAME =
/^#{variable}$/
VARSPEC =
/^#{varspec}$/
VARIABLE_LIST =
/^#{varspec}(?:,#{varspec})*$/
operator =
"+#./;?&=,!@|"
EXPRESSION =
/\{([#{operator}])?(#{varspec}(?:,#{varspec})*)\}/
LEADERS = {
'?' => '?',
'/' => '/',
'#' => '#',
'.' => '.',
';' => ';',
'&' => '&'
}
JOINERS = {
'?' => '&',
'.' => '.',
';' => ';',
'&' => '&',
'/' => '/'
}
##
# Raised if an invalid template value is supplied.
class InvalidTemplateValueError < StandardError
end
##
# Raised if an invalid template operator is used in a pattern.
class InvalidTemplateOperatorError < StandardError
end
##
# Raised if an invalid template operator is used in a pattern.
class TemplateOperatorAbortedError < StandardError
end
##
# This class represents the data that is extracted when a Template
# is matched against a URI.
class MatchData
##
# Creates a new MatchData object.
# MatchData objects should never be instantiated directly.
#
# @param [Addressable::URI] uri
# The URI that the template was matched against.
def initialize(uri, template, mapping)
@uri = uri.dup.freeze
@template = template
@mapping = mapping.dup.freeze
end
##
# @return [Addressable::URI]
# The URI that the Template was matched against.
attr_reader :uri
##
# @return [Addressable::Template]
# The Template used for the match.
attr_reader :template
##
# @return [Hash]
# The mapping that resulted from the match.
# Note that this mapping does not include keys or values for
# variables that appear in the Template, but are not present
# in the URI.
attr_reader :mapping
##
# @return [Array]
# The list of variables that were present in the Template.
# Note that this list will include variables which do not appear
# in the mapping because they were not present in URI.
def variables
self.template.variables
end
alias_method :keys, :variables
alias_method :names, :variables
##
# @return [Array]
# The list of values that were captured by the Template.
# Note that this list will include nils for any variables which
# were in the Template, but did not appear in the URI.
def values
@values ||= self.variables.inject([]) do |accu, key|
accu << self.mapping[key]
accu
end
end
alias_method :captures, :values
##
# Accesses captured values by name or by index.
#
# @param [String, Symbol, Fixnum] key
# Capture index or name. Note that when accessing by with index
# of 0, the full URI will be returned. The intention is to mimic
# the ::MatchData#[] behavior.
#
# @param [#to_int, nil] len
# If provided, an array of values will be returend with the given
# parameter used as length.
#
# @return [Array, String, nil]
# The captured value corresponding to the index or name. If the
# value was not provided or the key is unknown, nil will be
# returned.
#
# If the second parameter is provided, an array of that length will
# be returned instead.
def [](key, len = nil)
if len
to_a[key, len]
elsif String === key or Symbol === key
mapping[key.to_s]
else
to_a[key]
end
end
##
# @return [Array]
# Array with the matched URI as first element followed by the captured
# values.
def to_a
[to_s, *values]
end
##
# @return [String]
# The matched URI as String.
def to_s
uri.to_s
end
alias_method :string, :to_s
# Returns multiple captured values at once.
#
# @param [String, Symbol, Fixnum] *indexes
# Indices of the captures to be returned
#
# @return [Array]
# Values corresponding to given indices.
#
# @see Addressable::Template::MatchData#[]
def values_at(*indexes)
indexes.map { |i| self[i] }
end
##
# Returns a String representation of the MatchData's state.
#
# @return [String] The MatchData's state, as a String.
def inspect
sprintf("#<%s:%#0x RESULT:%s>",
self.class.to_s, self.object_id, self.mapping.inspect)
end
##
# Dummy method for code expecting a ::MatchData instance
#
# @return [String] An empty string.
def pre_match
""
end
alias_method :post_match, :pre_match
end
##
# Creates a new Addressable::Template object.
#
# @param [#to_str] pattern The URI Template pattern.
#
# @return [Addressable::Template] The initialized Template object.
def initialize(pattern)
if !pattern.respond_to?(:to_str)
raise TypeError, "Can't convert #{pattern.class} into String."
end
@pattern = pattern.to_str.freeze
end
##
# @return [String] The Template object's pattern.
attr_reader :pattern
##
# Returns a String representation of the Template object's state.
#
# @return [String] The Template object's state, as a String.
def inspect
sprintf("#<%s:%#0x PATTERN:%s>",
self.class.to_s, self.object_id, self.pattern)
end
##
# Returns true
if the Template objects are equal. This method
# does NOT normalize either Template before doing the comparison.
#
# @param [Object] template The Template to compare.
#
# @return [TrueClass, FalseClass]
# true
if the Templates are equivalent, false
# otherwise.
def ==(template)
return false unless template.kind_of?(Template)
return self.pattern == template.pattern
end
##
# Addressable::Template makes no distinction between `==` and `eql?`.
#
# @see #==
alias_method :eql?, :==
##
# Extracts a mapping from the URI using a URI Template pattern.
#
# @param [Addressable::URI, #to_str] uri
# The URI to extract from.
#
# @param [#restore, #match] processor
# A template processor object may optionally be supplied.
#
# The object should respond to either the restore or
# match messages or both. The restore method should
# take two parameters: `[String] name` and `[String] value`.
# The restore method should reverse any transformations that
# have been performed on the value to ensure a valid URI.
# The match method should take a single
# parameter: `[String] name`. The match method should return
# a String containing a regular expression capture group for
# matching on that particular variable. The default value is `".*?"`.
# The match method has no effect on multivariate operator
# expansions.
#
# @return [Hash, NilClass]
# The Hash mapping that was extracted from the URI, or
# nil if the URI didn't match the template.
#
# @example
# class ExampleProcessor
# def self.restore(name, value)
# return value.gsub(/\+/, " ") if name == "query"
# return value
# end
#
# def self.match(name)
# return ".*?" if name == "first"
# return ".*"
# end
# end
#
# uri = Addressable::URI.parse(
# "http://example.com/search/an+example+search+query/"
# )
# Addressable::Template.new(
# "http://example.com/search/{query}/"
# ).extract(uri, ExampleProcessor)
# #=> {"query" => "an example search query"}
#
# uri = Addressable::URI.parse("http://example.com/a/b/c/")
# Addressable::Template.new(
# "http://example.com/{first}/{second}/"
# ).extract(uri, ExampleProcessor)
# #=> {"first" => "a", "second" => "b/c"}
#
# uri = Addressable::URI.parse("http://example.com/a/b/c/")
# Addressable::Template.new(
# "http://example.com/{first}/{-list|/|second}/"
# ).extract(uri)
# #=> {"first" => "a", "second" => ["b", "c"]}
def extract(uri, processor=nil)
match_data = self.match(uri, processor)
return (match_data ? match_data.mapping : nil)
end
##
# Extracts match data from the URI using a URI Template pattern.
#
# @param [Addressable::URI, #to_str] uri
# The URI to extract from.
#
# @param [#restore, #match] processor
# A template processor object may optionally be supplied.
#
# The object should respond to either the restore or
# match messages or both. The restore method should
# take two parameters: `[String] name` and `[String] value`.
# The restore method should reverse any transformations that
# have been performed on the value to ensure a valid URI.
# The match method should take a single
# parameter: `[String] name`. The match method should return
# a String containing a regular expression capture group for
# matching on that particular variable. The default value is `".*?"`.
# The match method has no effect on multivariate operator
# expansions.
#
# @return [Hash, NilClass]
# The Hash mapping that was extracted from the URI, or
# nil if the URI didn't match the template.
#
# @example
# class ExampleProcessor
# def self.restore(name, value)
# return value.gsub(/\+/, " ") if name == "query"
# return value
# end
#
# def self.match(name)
# return ".*?" if name == "first"
# return ".*"
# end
# end
#
# uri = Addressable::URI.parse(
# "http://example.com/search/an+example+search+query/"
# )
# match = Addressable::Template.new(
# "http://example.com/search/{query}/"
# ).match(uri, ExampleProcessor)
# match.variables
# #=> ["query"]
# match.captures
# #=> ["an example search query"]
#
# uri = Addressable::URI.parse("http://example.com/a/b/c/")
# match = Addressable::Template.new(
# "http://example.com/{first}/{+second}/"
# ).match(uri, ExampleProcessor)
# match.variables
# #=> ["first", "second"]
# match.captures
# #=> ["a", "b/c"]
#
# uri = Addressable::URI.parse("http://example.com/a/b/c/")
# match = Addressable::Template.new(
# "http://example.com/{first}{/second*}/"
# ).match(uri)
# match.variables
# #=> ["first", "second"]
# match.captures
# #=> ["a", ["b", "c"]]
def match(uri, processor=nil)
uri = Addressable::URI.parse(uri)
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.
#
# 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)
result = self.pattern.dup
mapping = normalize_keys(mapping)
result.gsub!( EXPRESSION ) do |capture|
transform_partial_capture(mapping, capture, processor)
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.
#
# 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)
result = self.pattern.dup
mapping = normalize_keys(mapping)
result.gsub!( EXPRESSION ) do |capture|
transform_capture(mapping, capture, processor)
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
##
# 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
private
def ordered_variable_defaults
@ordered_variable_defaults ||= (
expansions, _ = parse_template_pattern(pattern)
expansions.map do |capture|
_, _, varlist = *capture.match(EXPRESSION)
varlist.split(',').map do |varspec|
varspec[VARSPEC, 1]
end
end.flatten
)
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.
#
# 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)
_, operator, varlist = *capture.match(EXPRESSION)
is_first = true
varlist.split(',').inject('') do |acc, varspec|
_, name, _ = *varspec.match(VARSPEC)
value = mapping[name]
if value
operator = '&' if !is_first && operator == '?'
acc << transform_capture(mapping, "{#{operator}#{varspec}}", processor)
else
operator = '&' if !is_first && operator == '?'
acc << "{#{operator}#{varspec}}"
end
is_first = false
acc
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.
#
# 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)
_, 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 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)
transformed_value = normalize_value(transformed_value)
end
end
acc << [name, transformed_value]
end
acc
end
return "" if return_value.empty?
join_values(operator, return_value)
end
##
# Takes a set of values, and joins them together based on the
# operator.
#
# @param [String, Nil] operator One of the operators from the set
# (?,&,+,#,;,/,.), or nil if there wasn't one.
# @param [Array] return_value
# The set of return values (as [variable_name, value] tuples) that will
# be joined together.
#
# @return [String] The transformed mapped value
def join_values(operator, return_value)
leader = LEADERS.fetch(operator, '')
joiner = JOINERS.fetch(operator, ',')
case operator
when '&', '?'
leader + return_value.map{|k,v|
if v.is_a?(Array) && v.first =~ /=/
v.join(joiner)
elsif v.is_a?(Array)
v.map{|inner_value| "#{k}=#{inner_value}"}.join(joiner)
else
"#{k}=#{v}"
end
}.join(joiner)
when ';'
return_value.map{|k,v|
if v.is_a?(Array) && v.first =~ /=/
';' + v.join(";")
elsif v.is_a?(Array)
';' + v.map{|inner_value| "#{k}=#{inner_value}"}.join(";")
else
v && v != '' ? ";#{k}=#{v}" : ";#{k}"
end
}.join
else
leader + return_value.map{|k,v| v}.join(joiner)
end
end
##
# Takes a set of values, and joins them together based on the
# operator.
#
# @param [Hash, Array, String] value
# Normalizes keys and values with IDNA#unicode_normalize_kc
#
# @return [Hash, Array, String] The normalized values
def normalize_value(value)
unless value.is_a?(Hash)
value = value.respond_to?(:to_ary) ? value.to_ary : value.to_str
end
# Handle unicode normalization
if value.kind_of?(Array)
value.map! { |val| Addressable::IDNA.unicode_normalize_kc(val) }
elsif value.kind_of?(Hash)
value = value.inject({}) { |acc, (k, v)|
acc[Addressable::IDNA.unicode_normalize_kc(k)] =
Addressable::IDNA.unicode_normalize_kc(v)
acc
}
else
value = Addressable::IDNA.unicode_normalize_kc(value)
end
value
end
##
# Generates a hash with string keys
#
# @param [Hash] mapping A mapping hash to normalize
#
# @return [Hash]
# A hash with stringified keys
def normalize_keys(mapping)
return mapping.inject({}) do |accu, pair|
name, value = pair
if Symbol === name
name = name.to_s
elsif name.respond_to?(:to_str)
name = name.to_str
else
raise TypeError,
"Can't convert #{name.class} into String."
end
accu[name] = value
accu
end
end
##
# Generates the Regexp that parses a template pattern.
#
# @param [String] pattern The URI template pattern.
# @param [#match] processor The template processor to use.
#
# @return [Regexp]
# A regular expression which may be used to parse a template pattern.
def parse_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
"(#{ 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 == '*'
"(#{group}(?:#{joiner}?#{group})*)?"
else
"(#{group})?"
end
end
end.join("#{joiner}?")
"(?:|#{leader}#{combined})"
end
# Ensure that the regular expression matches the whole URI.
regexp_string = "^#{regexp_string}$"
return expansions, Regexp.new(regexp_string)
end
end
end
addressable-2.3.8/lib/addressable/idna/ 0000755 0001750 0001750 00000000000 12522441742 016732 5 ustar lucas lucas addressable-2.3.8/lib/addressable/idna/pure.rb 0000644 0001750 0001750 00000050134 12522441742 020235 0 ustar lucas lucas # encoding:utf-8
#--
# Copyright (C) 2006-2015 Bob Aman
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#++
module Addressable
module IDNA
# This module is loosely based on idn_actionmailer by Mick Staugaard,
# the unicode library by Yoshida Masato, and the punycode implementation
# by Kazuhiro Nishiyama. Most of the code was copied verbatim, but
# some reformatting was done, and some translation from C was done.
#
# Without their code to work from as a base, we'd all still be relying
# on the presence of libidn. Which nobody ever seems to have installed.
#
# Original sources:
# http://github.com/staugaard/idn_actionmailer
# http://www.yoshidam.net/Ruby.html#unicode
# http://rubyforge.org/frs/?group_id=2550
UNICODE_TABLE = File.expand_path(
File.join(File.dirname(__FILE__), '../../..', 'data/unicode.data')
)
ACE_PREFIX = "xn--"
UTF8_REGEX = /\A(?:
[\x09\x0A\x0D\x20-\x7E] # ASCII
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4nil5
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)*\z/mnx
UTF8_REGEX_MULTIBYTE = /(?:
[\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4nil5
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)/mnx
# :startdoc:
# Converts from a Unicode internationalized domain name to an ASCII
# domain name as described in RFC 3490.
def self.to_ascii(input)
input = input.to_s unless input.is_a?(String)
input = input.dup
if input.respond_to?(:force_encoding)
input.force_encoding(Encoding::ASCII_8BIT)
end
if input =~ UTF8_REGEX && input =~ UTF8_REGEX_MULTIBYTE
parts = unicode_downcase(input).split('.')
parts.map! do |part|
if part.respond_to?(:force_encoding)
part.force_encoding(Encoding::ASCII_8BIT)
end
if part =~ UTF8_REGEX && part =~ UTF8_REGEX_MULTIBYTE
ACE_PREFIX + punycode_encode(unicode_normalize_kc(part))
else
part
end
end
parts.join('.')
else
input
end
end
# Converts from an ASCII domain name to a Unicode internationalized
# domain name as described in RFC 3490.
def self.to_unicode(input)
input = input.to_s unless input.is_a?(String)
parts = input.split('.')
parts.map! do |part|
if part =~ /^#{ACE_PREFIX}/
punycode_decode(part[/^#{ACE_PREFIX}(.+)/, 1])
else
part
end
end
output = parts.join('.')
if output.respond_to?(:force_encoding)
output.force_encoding(Encoding::UTF_8)
end
output
end
# Unicode normalization form KC.
def self.unicode_normalize_kc(input)
input = input.to_s unless input.is_a?(String)
unpacked = input.unpack("U*")
unpacked =
unicode_compose(unicode_sort_canonical(unicode_decompose(unpacked)))
return unpacked.pack("U*")
end
##
# Unicode aware downcase method.
#
# @api private
# @param [String] input
# The input string.
# @return [String] The downcased result.
def self.unicode_downcase(input)
input = input.to_s unless input.is_a?(String)
unpacked = input.unpack("U*")
unpacked.map! { |codepoint| lookup_unicode_lowercase(codepoint) }
return unpacked.pack("U*")
end
(class <= HANGUL_LBASE && ch_one < HANGUL_LBASE + HANGUL_LCOUNT &&
ch_two >= HANGUL_VBASE && ch_two < HANGUL_VBASE + HANGUL_VCOUNT
# Hangul L + V
return HANGUL_SBASE + (
(ch_one - HANGUL_LBASE) * HANGUL_VCOUNT + (ch_two - HANGUL_VBASE)
) * HANGUL_TCOUNT
elsif ch_one >= HANGUL_SBASE &&
ch_one < HANGUL_SBASE + HANGUL_SCOUNT &&
(ch_one - HANGUL_SBASE) % HANGUL_TCOUNT == 0 &&
ch_two >= HANGUL_TBASE && ch_two < HANGUL_TBASE + HANGUL_TCOUNT
# Hangul LV + T
return ch_one + (ch_two - HANGUL_TBASE)
end
p = []
ucs4_to_utf8 = lambda do |ch|
# For some reason, rcov likes to drop BUS errors here.
if ch < 128
p << ch
elsif ch < 2048
p << (ch >> 6 | 192)
p << (ch & 63 | 128)
elsif ch < 0x10000
p << (ch >> 12 | 224)
p << (ch >> 6 & 63 | 128)
p << (ch & 63 | 128)
elsif ch < 0x200000
p << (ch >> 18 | 240)
p << (ch >> 12 & 63 | 128)
p << (ch >> 6 & 63 | 128)
p << (ch & 63 | 128)
elsif ch < 0x4000000
p << (ch >> 24 | 248)
p << (ch >> 18 & 63 | 128)
p << (ch >> 12 & 63 | 128)
p << (ch >> 6 & 63 | 128)
p << (ch & 63 | 128)
elsif ch < 0x80000000
p << (ch >> 30 | 252)
p << (ch >> 24 & 63 | 128)
p << (ch >> 18 & 63 | 128)
p << (ch >> 12 & 63 | 128)
p << (ch >> 6 & 63 | 128)
p << (ch & 63 | 128)
end
end
ucs4_to_utf8.call(ch_one)
ucs4_to_utf8.call(ch_two)
return lookup_unicode_composition(p)
end
(class < cc
unpacked[i] = last
unpacked[i-1] = ch
i -= 1 if i > 1
else
i += 1
end
end
return unpacked
end
(class <= HANGUL_SBASE && cp < HANGUL_SBASE + HANGUL_SCOUNT
l, v, t = unicode_decompose_hangul(cp)
unpacked_result << l
unpacked_result << v if v
unpacked_result << t if t
else
dc = lookup_unicode_compatibility(cp)
unless dc
unpacked_result << cp
else
unpacked_result.concat(unicode_decompose(dc.unpack("U*")))
end
end
end
return unpacked_result
end
(class <= HANGUL_SCOUNT
l = codepoint
v = t = nil
return l, v, t
end
l = HANGUL_LBASE + sindex / HANGUL_NCOUNT
v = HANGUL_VBASE + (sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT
t = HANGUL_TBASE + sindex % HANGUL_TCOUNT
if t == HANGUL_TBASE
t = nil
end
return l, v, t
end
(class <?" +
"@ABCDEFGHIJKLMNO" +
"PQRSTUVWXYZ[\\]^_" +
"`abcdefghijklmno" +
"pqrstuvwxyz{|}~\n"
# Input is invalid.
class PunycodeBadInput < StandardError; end
# Output would exceed the space provided.
class PunycodeBigOutput < StandardError; end
# Input needs wider integers to process.
class PunycodeOverflow < StandardError; end
def self.punycode_encode(unicode)
unicode = unicode.to_s unless unicode.is_a?(String)
input = unicode.unpack("U*")
output = [0] * (ACE_MAX_LENGTH + 1)
input_length = input.size
output_length = [ACE_MAX_LENGTH]
# Initialize the state
n = PUNYCODE_INITIAL_N
delta = out = 0
max_out = output_length[0]
bias = PUNYCODE_INITIAL_BIAS
# Handle the basic code points:
input_length.times do |j|
if punycode_basic?(input[j])
if max_out - out < 2
raise PunycodeBigOutput,
"Output would exceed the space provided."
end
output[out] = input[j]
out += 1
end
end
h = b = out
# h is the number of code points that have been handled, b is the
# number of basic code points, and out is the number of characters
# that have been output.
if b > 0
output[out] = PUNYCODE_DELIMITER
out += 1
end
# Main encoding loop:
while h < input_length
# All non-basic code points < n have been
# handled already. Find the next larger one:
m = PUNYCODE_MAXINT
input_length.times do |j|
m = input[j] if (n...m) === input[j]
end
# Increase delta enough to advance the decoder's
# state to , but guard against overflow:
if m - n > (PUNYCODE_MAXINT - delta) / (h + 1)
raise PunycodeOverflow, "Input needs wider integers to process."
end
delta += (m - n) * (h + 1)
n = m
input_length.times do |j|
# Punycode does not need to check whether input[j] is basic:
if input[j] < n
delta += 1
if delta == 0
raise PunycodeOverflow,
"Input needs wider integers to process."
end
end
if input[j] == n
# Represent delta as a generalized variable-length integer:
q = delta; k = PUNYCODE_BASE
while true
if out >= max_out
raise PunycodeBigOutput,
"Output would exceed the space provided."
end
t = (
if k <= bias
PUNYCODE_TMIN
elsif k >= bias + PUNYCODE_TMAX
PUNYCODE_TMAX
else
k - bias
end
)
break if q < t
output[out] =
punycode_encode_digit(t + (q - t) % (PUNYCODE_BASE - t))
out += 1
q = (q - t) / (PUNYCODE_BASE - t)
k += PUNYCODE_BASE
end
output[out] = punycode_encode_digit(q)
out += 1
bias = punycode_adapt(delta, h + 1, h == b)
delta = 0
h += 1
end
end
delta += 1
n += 1
end
output_length[0] = out
outlen = out
outlen.times do |j|
c = output[j]
unless c >= 0 && c <= 127
raise Exception, "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
(class <= 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
(class <> 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
(class <