class Addressable::Template

This is an implementation of a URI template based on RFC 6570 (tools.ietf.org/html/rfc6570).

Constants

EXPRESSION
JOINERS
LEADERS
RESERVED
UNRESERVED
VARIABLE_LIST
VARNAME
VARSPEC

Attributes

pattern[R]

@return [String] The Template object’s pattern.

Public Class Methods

new(pattern) click to toggle source

Creates a new Addressable::Template object.

@param [#to_str] pattern The URI Template pattern.

@return [Addressable::Template] The initialized Template object.

# File lib/addressable/template.rb, line 234
def initialize(pattern)
  if !pattern.respond_to?(:to_str)
    raise TypeError, "Can't convert #{pattern.class} into String."
  end
  @pattern = pattern.to_str.dup.freeze
end

Public Instance Methods

==(template) click to toggle source

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]

<code>true</code> if the Templates are equivalent, <code>false</code>
otherwise.
# File lib/addressable/template.rb, line 274
def ==(template)
  return false unless template.kind_of?(Template)
  return self.pattern == template.pattern
end
Also aliased as: eql?
eql?(template)

Addressable::Template makes no distinction between ‘==` and `eql?`.

@see ==

Alias for: ==
expand(mapping, processor=nil, normalize_values=true) click to toggle source

Expands a URI template into a full URI.

@param [Hash] mapping The mapping that corresponds to the pattern. @param [#validate, transform] processor

An optional processor object may be supplied.

@param [Boolean] normalize_values

Optional flag to enable/disable unicode normalization. Default: true

The object should respond to either the validate or transform messages or both. Both the validate and transform methods should take two parameters: name and value. The validate method should return true or false; true if the value of the variable is valid, false otherwise. An InvalidTemplateValueError exception will be raised if the value is invalid. The transform method should return the transformed variable value as a String. If a transform method is used, the value will not be percent encoded automatically. Unicode normalization will be performed both before and after sending the value to the transform method.

@return [Addressable::URI] The expanded URI template.

@example

class ExampleProcessor
  def self.validate(name, value)
    return !!(value =~ /^[\w ]+$/) if name == "query"
    return true
  end

  def self.transform(name, value)
    return value.gsub(/ /, "+") if name == "query"
    return value
  end
end

Addressable::Template.new(
  "http://example.com/search/{query}/"
).expand(
  {"query" => "an example search query"},
  ExampleProcessor
).to_str
#=> "http://example.com/search/an+example+search+query/"

Addressable::Template.new(
  "http://example.com/search/{query}/"
).expand(
  {"query" => "an example search query"}
).to_str
#=> "http://example.com/search/an%20example%20search%20query/"

Addressable::Template.new(
  "http://example.com/search/{query}/"
).expand(
  {"query" => "bogus!"},
  ExampleProcessor
).to_str
#=> Addressable::Template::InvalidTemplateValueError
# File lib/addressable/template.rb, line 591
def expand(mapping, processor=nil, normalize_values=true)
  result = self.pattern.dup
  mapping = normalize_keys(mapping)
  result.gsub!( EXPRESSION ) do |capture|
    transform_capture(mapping, capture, processor, normalize_values)
  end
  return Addressable::URI.parse(result)
end
extract(uri, processor=nil) click to toggle source

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 <tt>restore</tt> or
<tt>match</tt> messages or both. The <tt>restore</tt> method should
take two parameters: `[String] name` and `[String] value`.
The <tt>restore</tt> method should reverse any transformations that
have been performed on the value to ensure a valid URI.
The <tt>match</tt> method should take a single
parameter: `[String] name`.  The <tt>match</tt> method should return
a <tt>String</tt> containing a regular expression capture group for
matching on that particular variable. The default value is `".*?"`.
The <tt>match</tt> method has no effect on multivariate operator
expansions.

@return [Hash, NilClass]

The <tt>Hash</tt> mapping that was extracted from the URI, or
<tt>nil</tt> 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"]}
# File lib/addressable/template.rb, line 342
def extract(uri, processor=nil)
  match_data = self.match(uri, processor)
  return (match_data ? match_data.mapping : nil)
end
freeze() click to toggle source

Freeze URI, initializing instance variables.

@return [Addressable::URI] The frozen URI object.

Calls superclass method
# File lib/addressable/template.rb, line 245
def freeze
  self.variables
  self.variable_defaults
  self.named_captures
  super
end
inspect() click to toggle source

Returns a String representation of the Template object’s state.

@return [String] The Template object’s state, as a String.

# File lib/addressable/template.rb, line 260
def inspect
  sprintf("#<%s:%#0x PATTERN:%s>",
    self.class.to_s, self.object_id, self.pattern)
end
keys()
Alias for: variables
match(uri, processor=nil) click to toggle source

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 <tt>restore</tt> or
<tt>match</tt> messages or both. The <tt>restore</tt> method should
take two parameters: `[String] name` and `[String] value`.
The <tt>restore</tt> method should reverse any transformations that
have been performed on the value to ensure a valid URI.
The <tt>match</tt> method should take a single
parameter: `[String] name`. The <tt>match</tt> method should return
a <tt>String</tt> containing a regular expression capture group for
matching on that particular variable. The default value is `".*?"`.
The <tt>match</tt> method has no effect on multivariate operator
expansions.

@return [Hash, NilClass]

The <tt>Hash</tt> mapping that was extracted from the URI, or
<tt>nil</tt> 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"]]
# File lib/addressable/template.rb, line 413
def match(uri, processor=nil)
  uri = Addressable::URI.parse(uri) unless uri.is_a?(Addressable::URI)
  mapping = {}

  # First, we need to process the pattern, and extract the values.
  expansions, expansion_regexp =
    parse_template_pattern(pattern, processor)

  return nil unless uri.to_str.match(expansion_regexp)
  unparsed_values = uri.to_str.scan(expansion_regexp).flatten

  if uri.to_str == pattern
    return Addressable::Template::MatchData.new(uri, self, mapping)
  elsif expansions.size > 0
    index = 0
    expansions.each do |expansion|
      _, operator, varlist = *expansion.match(EXPRESSION)
      varlist.split(',').each do |varspec|
        _, name, modifier = *varspec.match(VARSPEC)
        mapping[name] ||= nil
        case operator
        when nil, '+', '#', '/', '.'
          unparsed_value = unparsed_values[index]
          name = varspec[VARSPEC, 1]
          value = unparsed_value
          value = value.split(JOINERS[operator]) if value && modifier == '*'
        when ';', '?', '&'
          if modifier == '*'
            if unparsed_values[index]
              value = unparsed_values[index].split(JOINERS[operator])
              value = value.inject({}) do |acc, v|
                key, val = v.split('=')
                val = "" if val.nil?
                acc[key] = val
                acc
              end
            end
          else
            if (unparsed_values[index])
              name, value = unparsed_values[index].split('=')
              value = "" if value.nil?
            end
          end
        end
        if processor != nil && processor.respond_to?(:restore)
          value = processor.restore(name, value)
        end
        if processor == nil
          if value.is_a?(Hash)
            value = value.inject({}){|acc, (k, v)|
              acc[Addressable::URI.unencode_component(k)] =
                Addressable::URI.unencode_component(v)
              acc
            }
          elsif value.is_a?(Array)
            value = value.map{|v| Addressable::URI.unencode_component(v) }
          else
            value = Addressable::URI.unencode_component(value)
          end
        end
        if !mapping.has_key?(name) || mapping[name].nil?
          # Doesn't exist, set to value (even if value is nil)
          mapping[name] = value
        end
        index = index + 1
      end
    end
    return Addressable::Template::MatchData.new(uri, self, mapping)
  else
    return nil
  end
end
named_captures() click to toggle source

Returns the named captures of the coerced ‘Regexp`.

@return [Hash] The named captures of the ‘Regexp` given by {#to_regexp}.

@api private

# File lib/addressable/template.rb, line 651
def named_captures
  self.to_regexp.named_captures
end
names()
Alias for: variables
partial_expand(mapping, processor=nil, normalize_values=true) click to toggle source

Expands a URI template into another URI template.

@param [Hash] mapping The mapping that corresponds to the pattern. @param [#validate, transform] processor

An optional processor object may be supplied.

@param [Boolean] normalize_values

Optional flag to enable/disable unicode normalization. Default: true

The object should respond to either the validate or transform messages or both. Both the validate and transform methods should take two parameters: name and value. The validate method should return true or false; true if the value of the variable is valid, false otherwise. An InvalidTemplateValueError exception will be raised if the value is invalid. The transform method should return the transformed variable value as a String. If a transform method is used, the value will not be percent encoded automatically. Unicode normalization will be performed both before and after sending the value to the transform method.

@return [Addressable::Template] The partially expanded URI template.

@example

Addressable::Template.new(
  "http://example.com/{one}/{two}/"
).partial_expand({"one" => "1"}).pattern
#=> "http://example.com/1/{two}/"

Addressable::Template.new(
  "http://example.com/{?one,two}/"
).partial_expand({"one" => "1"}).pattern
#=> "http://example.com/?one=1{&two}/"

Addressable::Template.new(
  "http://example.com/{?one,two,three}/"
).partial_expand({"one" => "1", "three" => 3}).pattern
#=> "http://example.com/?one=1{&two}&three=3"
# File lib/addressable/template.rb, line 524
def partial_expand(mapping, processor=nil, normalize_values=true)
  result = self.pattern.dup
  mapping = normalize_keys(mapping)
  result.gsub!( EXPRESSION ) do |capture|
    transform_partial_capture(mapping, capture, processor, normalize_values)
  end
  return Addressable::Template.new(result)
end
source() click to toggle source

Returns the source of the coerced ‘Regexp`.

@return [String] The source of the ‘Regexp` given by {#to_regexp}.

@api private

# File lib/addressable/template.rb, line 641
def source
  self.to_regexp.source
end
to_regexp() click to toggle source

Coerces a template into a ‘Regexp` object. This regular expression will behave very similarly to the actual template, and should match the same URI values, but it cannot fully handle, for example, values that would extract to an `Array`.

@return [Regexp] A regular expression which should match the template.

# File lib/addressable/template.rb, line 630
def to_regexp
  _, source = parse_template_pattern(pattern)
  Regexp.new(source)
end
variable_defaults() click to toggle source

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

# File lib/addressable/template.rb, line 618
def variable_defaults
  @variable_defaults ||=
    Hash[*ordered_variable_defaults.reject { |k, v| v.nil? }.flatten]
end
variables() click to toggle source

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.

# File lib/addressable/template.rb, line 607
def variables
  @variables ||= ordered_variable_defaults.map { |var, val| var }.uniq
end
Also aliased as: keys, names

Private Instance Methods

join_values(operator, return_value) click to toggle source

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

# File lib/addressable/template.rb, line 861
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
normalize_keys(mapping) click to toggle source

Generates a hash with string keys

@param [Hash] mapping A mapping hash to normalize

@return [Hash]

A hash with stringified keys
# File lib/addressable/template.rb, line 925
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
normalize_value(value) click to toggle source

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

# File lib/addressable/template.rb, line 898
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
ordered_variable_defaults() click to toggle source
# File lib/addressable/template.rb, line 656
def ordered_variable_defaults
  @ordered_variable_defaults ||= begin
    expansions, _ = parse_template_pattern(pattern)
    expansions.flat_map do |capture|
      _, _, varlist = *capture.match(EXPRESSION)
      varlist.split(',').map do |varspec|
        varspec[VARSPEC, 1]
      end
    end
  end
end
parse_new_template_pattern(pattern, processor = nil) click to toggle source

Generates the Regexp that parses a template pattern.

@param [String] pattern The URI template pattern. @param [#match] processor The template processor to use.

@return [Array, Regexp]

An array of expansion variables nad a regular expression which may be
used to parse a template pattern
# File lib/addressable/template.rb, line 969
def parse_new_template_pattern(pattern, processor = nil)
  # Escape the pattern. The two gsubs restore the escaped curly braces
  # back to their original form. Basically, escape everything that isn't
  # within an expansion.
  escaped_pattern = Regexp.escape(
    pattern
  ).gsub(/\\\{(.*?)\\\}/) do |escaped|
    escaped.gsub(/\\(.)/, "\\1")
  end

  expansions = []

  # Create a regular expression that captures the values of the
  # variables in the URI.
  regexp_string = escaped_pattern.gsub( EXPRESSION ) do |expansion|

    expansions << expansion
    _, operator, varlist = *expansion.match(EXPRESSION)
    leader = Regexp.escape(LEADERS.fetch(operator, ''))
    joiner = Regexp.escape(JOINERS.fetch(operator, ','))
    combined = varlist.split(',').map do |varspec|
      _, name, modifier = *varspec.match(VARSPEC)

      result = processor && processor.respond_to?(:match) ? processor.match(name) : nil
      if result
        "(?<#{name}>#{ result })"
      else
        group = case operator
        when '+'
          "#{ RESERVED }*?"
        when '#'
          "#{ RESERVED }*?"
        when '/'
          "#{ UNRESERVED }*?"
        when '.'
          "#{ UNRESERVED.gsub('\.', '') }*?"
        when ';'
          "#{ UNRESERVED }*=?#{ UNRESERVED }*?"
        when '?'
          "#{ UNRESERVED }*=#{ UNRESERVED }*?"
        when '&'
          "#{ UNRESERVED }*=#{ UNRESERVED }*?"
        else
          "#{ UNRESERVED }*?"
        end
        if modifier == '*'
          "(?<#{name}>#{group}(?:#{joiner}?#{group})*)?"
        else
          "(?<#{name}>#{group})?"
        end
      end
    end.join("#{joiner}?")
    "(?:|#{leader}#{combined})"
  end

  # Ensure that the regular expression matches the whole URI.
  regexp_string = "\\A#{regexp_string}\\z"
  return expansions, Regexp.new(regexp_string)
end
parse_template_pattern(pattern, processor = nil) click to toggle source

Generates the Regexp that parses a template pattern. Memoizes the value if template processor not set (processors may not be deterministic)

@param [String] pattern The URI template pattern. @param [#match] processor The template processor to use.

@return [Array, Regexp]

An array of expansion variables nad a regular expression which may be
used to parse a template pattern
# File lib/addressable/template.rb, line 951
def parse_template_pattern(pattern, processor = nil)
  if processor.nil? && pattern == @pattern
    @cached_template_parse ||=
      parse_new_template_pattern(pattern, processor)
  else
    parse_new_template_pattern(pattern, processor)
  end
end
transform_capture(mapping, capture, processor=nil, normalize_values=true) click to toggle source

Transforms a mapped value so that values can be substituted into the template.

@param [Hash] mapping The mapping to replace captures @param [String] capture

The expression to replace

@param [#validate, transform] processor

An optional processor object may be supplied.

@param [Boolean] normalize_values

Optional flag to enable/disable unicode normalization. Default: true

The object should respond to either the validate or transform messages or both. Both the validate and transform methods should take two parameters: name and value. The validate method should return true or false; true if the value of the variable is valid, false otherwise. An InvalidTemplateValueError exception will be raised if the value is invalid. The transform method should return the transformed variable value as a String. If a transform method is used, the value will not be percent encoded automatically. Unicode normalization will be performed both before and after sending the value to the transform method.

@return [String] The expanded expression

# File lib/addressable/template.rb, line 753
def transform_capture(mapping, capture, processor=nil,
                      normalize_values=true)
  _, operator, varlist = *capture.match(EXPRESSION)
  return_value = varlist.split(',').inject([]) do |acc, varspec|
    _, name, modifier = *varspec.match(VARSPEC)
    value = mapping[name]
    unless value == nil || value == {}
      allow_reserved = %w(+ #).include?(operator)
      # Common primitives where the .to_s output is well-defined
      if Numeric === value || Symbol === value ||
          value == true || value == false
        value = value.to_s
      end
      length = modifier.gsub(':', '').to_i if modifier =~ /^:\d+/

      unless (Hash === value) ||
        value.respond_to?(:to_ary) || value.respond_to?(:to_str)
        raise TypeError,
          "Can't convert #{value.class} into String or Array."
      end

      value = normalize_value(value) if normalize_values

      if processor == nil || !processor.respond_to?(:transform)
        # Handle percent escaping
        if allow_reserved
          encode_map =
            Addressable::URI::CharacterClasses::RESERVED +
            Addressable::URI::CharacterClasses::UNRESERVED
        else
          encode_map = Addressable::URI::CharacterClasses::UNRESERVED
        end
        if value.kind_of?(Array)
          transformed_value = value.map do |val|
            if length
              Addressable::URI.encode_component(val[0...length], encode_map)
            else
              Addressable::URI.encode_component(val, encode_map)
            end
          end
          unless modifier == "*"
            transformed_value = transformed_value.join(',')
          end
        elsif value.kind_of?(Hash)
          transformed_value = value.map do |key, val|
            if modifier == "*"
              "#{
                Addressable::URI.encode_component( key, encode_map)
              }=#{
                Addressable::URI.encode_component( val, encode_map)
              }"
            else
              "#{
                Addressable::URI.encode_component( key, encode_map)
              },#{
                Addressable::URI.encode_component( val, encode_map)
              }"
            end
          end
          unless modifier == "*"
            transformed_value = transformed_value.join(',')
          end
        else
          if length
            transformed_value = Addressable::URI.encode_component(
              value[0...length], encode_map)
          else
            transformed_value = Addressable::URI.encode_component(
              value, encode_map)
          end
        end
      end

      # Process, if we've got a processor
      if processor != nil
        if processor.respond_to?(:validate)
          if !processor.validate(name, value)
            display_value = value.kind_of?(Array) ? value.inspect : value
            raise InvalidTemplateValueError,
              "#{name}=#{display_value} is an invalid template value."
          end
        end
        if processor.respond_to?(:transform)
          transformed_value = processor.transform(name, value)
          if normalize_values
            transformed_value = normalize_value(transformed_value)
          end
        end
      end
      acc << [name, transformed_value]
    end
    acc
  end
  return "" if return_value.empty?
  join_values(operator, return_value)
end
transform_partial_capture(mapping, capture, processor = nil, normalize_values = true) click to toggle source

Loops through each capture and expands any values available in mapping

@param [Hash] mapping

Set of keys to expand

@param [String] capture

The expression to expand

@param [#validate, transform] processor

An optional processor object may be supplied.

@param [Boolean] normalize_values

Optional flag to enable/disable unicode normalization. Default: true

The object should respond to either the validate or transform messages or both. Both the validate and transform methods should take two parameters: name and value. The validate method should return true or false; true if the value of the variable is valid, false otherwise. An InvalidTemplateValueError exception will be raised if the value is invalid. The transform method should return the transformed variable value as a String. If a transform method is used, the value will not be percent encoded automatically. Unicode normalization will be performed both before and after sending the value to the transform method.

@return [String] The expanded expression

# File lib/addressable/template.rb, line 694
def transform_partial_capture(mapping, capture, processor = nil,
                              normalize_values = true)
  _, operator, varlist = *capture.match(EXPRESSION)

  vars = varlist.split(",")

  if operator == "?"
    # partial expansion of form style query variables sometimes requires a
    # slight reordering of the variables to produce a valid url.
    first_to_expand = vars.find { |varspec|
      _, name, _ =  *varspec.match(VARSPEC)
      mapping.key?(name) && !mapping[name].nil?
    }

    vars = [first_to_expand] + vars.reject {|varspec| varspec == first_to_expand}  if first_to_expand
  end

  vars.
    inject("".dup) do |acc, varspec|
      _, name, _ =  *varspec.match(VARSPEC)
      next_val = if mapping.key? name
                   transform_capture(mapping, "{#{operator}#{varspec}}",
                                     processor, normalize_values)
                 else
                   "{#{operator}#{varspec}}"
                 end
      # If we've already expanded at least one '?' operator with non-empty
      # value, change to '&'
      operator = "&" if (operator == "?") && (next_val != "")
      acc << next_val
  end
end