<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -6,6 +6,7 @@
   * fixed unicode normalization exception
   * improved specs
   * refactored URI template support out into its own class
+  * added partial template expansion
   * deprecated Addressable::URI.expand_template
   * deprecated Addressable::URI#extract_mapping
   * worked around issue with freezing URIs</diff>
      <filename>CHANGELOG</filename>
    </modified>
    <modified>
      <diff>@@ -28,6 +28,12 @@ Example usage:
   #=&gt; #&lt;Addressable::URI:0xc9d95c URI:http://example.com/an+example+query/&gt;
 
   template = Addressable::Template.new(
+    &quot;http://example.com/{-join|&amp;|one,two,three}/&quot;
+  )
+  template.partial_expand({&quot;one&quot; =&gt; &quot;1&quot;, &quot;three&quot; =&gt; 3}).pattern
+  #=&gt; &quot;http://example.com/?one=1{-prefix|&amp;two=|two}&amp;three=3&quot;
+
+  template = Addressable::Template.new(
     &quot;http://{host}/{-suffix|/|segments}?{-join|&amp;|one,two,bogus}\#{fragment}&quot;
   )
   uri = Addressable::URI.parse(</diff>
      <filename>README</filename>
    </modified>
    <modified>
      <diff>@@ -33,6 +33,14 @@ module Addressable
   # This is an implementation of a URI template based on
   # &lt;a href=&quot;http://tinyurl.com/uritemplatedraft03&quot;&gt;URI Template draft 03&lt;/a&gt;.
   class Template
+    # Constants used throughout the template code.
+    anything =
+      Addressable::URI::CharacterClasses::RESERVED +
+      Addressable::URI::CharacterClasses::UNRESERVED
+    OPERATOR_EXPANSION =
+      /\{-([a-zA-Z]+)\|([#{anything}]+)\|([#{anything}]+)\}/
+    VARIABLE_EXPANSION = /\{([#{anything}]+?)(=([#{anything}]+))?\}/
+
     ##
     # Raised if an invalid template value is supplied.
     class InvalidTemplateValueError &lt; StandardError
@@ -265,13 +273,6 @@ module Addressable
       uri = Addressable::URI.parse(uri)
       mapping = {}
 
-      reserved = Addressable::URI::CharacterClasses::RESERVED
-      unreserved = Addressable::URI::CharacterClasses::UNRESERVED
-      anything = reserved + unreserved
-      operator_expansion =
-        /\{-([a-zA-Z]+)\|([#{anything}]+)\|([#{anything}]+)\}/
-      variable_expansion = /\{([#{anything}]+?)(=([#{anything}]+))?\}/
-
       # First, we need to process the pattern, and extract the values.
       expansions, expansion_regexp =
         parse_template_pattern(pattern, processor)
@@ -282,7 +283,7 @@ module Addressable
       elsif expansions.size &gt; 0 &amp;&amp; expansions.size == unparsed_values.size
         expansions.each_with_index do |expansion, index|
           unparsed_value = unparsed_values[index]
-          if expansion =~ operator_expansion
+          if expansion =~ OPERATOR_EXPANSION
             operator, argument, variables =
               parse_template_expansion(expansion)
             extract_method = &quot;extract_#{operator}_operator&quot;
@@ -301,7 +302,7 @@ module Addressable
               end
             end
           else
-            name = expansion[variable_expansion, 1]
+            name = expansion[VARIABLE_EXPANSION, 1]
             value = unparsed_value
             if processor != nil &amp;&amp; processor.respond_to?(:restore)
               value = processor.restore(name, value)
@@ -316,6 +317,82 @@ module Addressable
     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 &lt;tt&gt;validate&lt;/tt&gt; or &lt;tt&gt;transform&lt;/tt&gt; messages
+    #   or both.  Both the &lt;tt&gt;validate&lt;/tt&gt; and &lt;tt&gt;transform&lt;/tt&gt; methods
+    #   should take two parameters: &lt;tt&gt;name&lt;/tt&gt; and &lt;tt&gt;value&lt;/tt&gt;.  The
+    #   &lt;tt&gt;validate&lt;/tt&gt; method should return &lt;tt&gt;true&lt;/tt&gt; or
+    #   &lt;tt&gt;false&lt;/tt&gt;; &lt;tt&gt;true&lt;/tt&gt; if the value of the variable is valid,
+    #   &lt;tt&gt;false&lt;/tt&gt; otherwise.  An &lt;tt&gt;InvalidTemplateValueError&lt;/tt&gt;
+    #   exception will be raised if the value is invalid.  The
+    #   &lt;tt&gt;transform&lt;/tt&gt; method should return the transformed variable
+    #   value as a &lt;tt&gt;String&lt;/tt&gt;.  If a &lt;tt&gt;transform&lt;/tt&gt; 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(
+    #     &quot;http://example.com/{one}/{two}/&quot;
+    #   ).partial_expand({&quot;one&quot; =&gt; &quot;1&quot;}).pattern
+    #   #=&gt; &quot;http://example.com/1/{two}/&quot;
+    #
+    #   Addressable::Template.new(
+    #     &quot;http://example.com/search/{-list|+|query}/&quot;
+    #   ).partial_expand(
+    #     {&quot;query&quot; =&gt; &quot;an example search query&quot;.split(&quot; &quot;)}
+    #   ).pattern
+    #   #=&gt; &quot;http://example.com/search/an+example+search+query/&quot;
+    #
+    #   Addressable::Template.new(
+    #     &quot;http://example.com/{-join|&amp;|one,two}/&quot;
+    #   ).partial_expand({&quot;one&quot; =&gt; &quot;1&quot;}).pattern
+    #   #=&gt; &quot;http://example.com/?one=1{-prefix|&amp;two=|two}&quot;
+    #
+    #   Addressable::Template.new(
+    #     &quot;http://example.com/{-join|&amp;|one,two,three}/&quot;
+    #   ).partial_expand({&quot;one&quot; =&gt; &quot;1&quot;, &quot;three&quot; =&gt; 3}).pattern
+    #   #=&gt; &quot;http://example.com/?one=1{-prefix|&amp;two=|two}&amp;three=3&quot;
+    def partial_expand(mapping, processor=nil)
+      result = self.pattern.dup
+      transformed_mapping = transform_mapping(mapping, processor)
+      result.gsub!(
+        /#{OPERATOR_EXPANSION}|#{VARIABLE_EXPANSION}/
+      ) do |capture|
+        if capture =~ OPERATOR_EXPANSION
+          operator, argument, variables, default_mapping =
+            parse_template_expansion(capture, transformed_mapping)
+          expand_method = &quot;expand_#{operator}_operator&quot;
+          if ([expand_method, expand_method.to_sym] &amp; private_methods).empty?
+            raise InvalidTemplateOperatorError,
+              &quot;Invalid template operator: #{operator}&quot;
+          else
+            send(
+              expand_method.to_sym, argument, variables,
+              default_mapping, true
+            )
+          end
+        else
+          varname, _, vardefault = capture.scan(/^\{(.+?)(=(.*))?\}$/)[0]
+          if transformed_mapping[varname]
+            transformed_mapping[varname]
+          elsif vardefault
+            &quot;{#{varname}=#{vardefault}}&quot;
+          else
+            &quot;{#{varname}}&quot;
+          end
+        end
+      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.
@@ -373,15 +450,77 @@ module Addressable
     #   #=&gt; Addressable::Template::InvalidTemplateValueError
     def expand(mapping, processor=nil)
       result = self.pattern.dup
+      transformed_mapping = transform_mapping(mapping, processor)
+      result.gsub!(
+        /#{OPERATOR_EXPANSION}|#{VARIABLE_EXPANSION}/
+      ) do |capture|
+        if capture =~ OPERATOR_EXPANSION
+          operator, argument, variables, default_mapping =
+            parse_template_expansion(capture, transformed_mapping)
+          expand_method = &quot;expand_#{operator}_operator&quot;
+          if ([expand_method, expand_method.to_sym] &amp; private_methods).empty?
+            raise InvalidTemplateOperatorError,
+              &quot;Invalid template operator: #{operator}&quot;
+          else
+            send(expand_method.to_sym, argument, variables, default_mapping)
+          end
+        else
+          varname, _, vardefault = capture.scan(/^\{(.+?)(=(.*))?\}$/)[0]
+          transformed_mapping[varname] || vardefault
+        end
+      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 ||= (begin
+        result = []
 
-      reserved = Addressable::URI::CharacterClasses::RESERVED
-      unreserved = Addressable::URI::CharacterClasses::UNRESERVED
-      anything = reserved + unreserved
-      operator_expansion =
-        /\{-([a-zA-Z]+)\|([#{anything}]+)\|([#{anything}]+)\}/
-      variable_expansion = /\{([#{anything}]+?)(=([#{anything}]+))?\}/
+        expansions, expansion_regexp = parse_template_pattern(pattern)
+        expansions.each do |expansion|
+          if expansion =~ OPERATOR_EXPANSION
+            _, _, variables, _ = parse_template_expansion(expansion)
+            result.concat(variables)
+          else
+            result &lt;&lt; expansion[VARIABLE_EXPANSION, 1]
+          end
+        end
+        result.uniq
+      end)
+    end
+    alias_method :keys, :variables
 
-      transformed_mapping = mapping.inject({}) do |accu, pair|
+  private
+    ##
+    # Transforms a mapping so that values can be substituted into the
+    # template.
+    #
+    # @param [Hash] mapping The mapping of variables to values.
+    # @param [#validate, #transform] processor
+    #   An optional processor object may be supplied.  The object should
+    #   respond to either the &lt;tt&gt;validate&lt;/tt&gt; or &lt;tt&gt;transform&lt;/tt&gt; messages
+    #   or both.  Both the &lt;tt&gt;validate&lt;/tt&gt; and &lt;tt&gt;transform&lt;/tt&gt; methods
+    #   should take two parameters: &lt;tt&gt;name&lt;/tt&gt; and &lt;tt&gt;value&lt;/tt&gt;.  The
+    #   &lt;tt&gt;validate&lt;/tt&gt; method should return &lt;tt&gt;true&lt;/tt&gt; or
+    #   &lt;tt&gt;false&lt;/tt&gt;; &lt;tt&gt;true&lt;/tt&gt; if the value of the variable is valid,
+    #   &lt;tt&gt;false&lt;/tt&gt; otherwise.  An &lt;tt&gt;InvalidTemplateValueError&lt;/tt&gt;
+    #   exception will be raised if the value is invalid.  The
+    #   &lt;tt&gt;transform&lt;/tt&gt; method should return the transformed variable
+    #   value as a &lt;tt&gt;String&lt;/tt&gt;.  If a &lt;tt&gt;transform&lt;/tt&gt; 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 [Hash] The transformed mapping.
+    def transform_mapping(mapping, processor=nil)
+      return mapping.inject({}) do |accu, pair|
         name, value = pair
         unless value.respond_to?(:to_ary) || value.respond_to?(:to_str)
           raise TypeError,
@@ -435,59 +574,8 @@ module Addressable
         accu[name] = transformed_value
         accu
       end
-      result.gsub!(
-        /#{operator_expansion}|#{variable_expansion}/
-      ) do |capture|
-        if capture =~ operator_expansion
-          operator, argument, variables, default_mapping =
-            parse_template_expansion(capture, transformed_mapping)
-          expand_method = &quot;expand_#{operator}_operator&quot;
-          if ([expand_method, expand_method.to_sym] &amp; private_methods).empty?
-            raise InvalidTemplateOperatorError,
-              &quot;Invalid template operator: #{operator}&quot;
-          else
-            send(expand_method.to_sym, argument, variables, default_mapping)
-          end
-        else
-          varname, _, vardefault = capture.scan(/^\{(.+?)(=(.*))?\}$/)[0]
-          transformed_mapping[varname] || vardefault
-        end
-      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 ||= (begin
-        result = []
-        reserved = Addressable::URI::CharacterClasses::RESERVED
-        unreserved = Addressable::URI::CharacterClasses::UNRESERVED
-        anything = reserved + unreserved
-        operator_expansion =
-          /\{-([a-zA-Z]+)\|([#{anything}]+)\|([#{anything}]+)\}/
-        variable_expansion = /\{([#{anything}]+?)(=([#{anything}]+))?\}/
-
-        expansions, expansion_regexp = parse_template_pattern(pattern)
-        expansions.each do |expansion|
-          if expansion =~ operator_expansion
-            _, _, variables, _ = parse_template_expansion(expansion)
-            result.concat(variables)
-          else
-            result &lt;&lt; expansion[variable_expansion, 1]
-          end
-        end
-        result.uniq
-      end)
     end
-    alias_method :keys, :variables
 
-  private
     ##
     # Expands a URI Template opt operator.
     #
@@ -496,11 +584,14 @@ module Addressable
     # @param [Hash] mapping The mapping of variables to values.
     #
     # @return [String] The expanded result.
-    def expand_opt_operator(argument, variables, mapping)
-      if (variables.any? do |variable|
+    def expand_opt_operator(argument, variables, mapping, partial=false)
+      variables_present = variables.any? do |variable|
         mapping[variable] != [] &amp;&amp;
         mapping[variable]
-      end)
+      end
+      if partial &amp;&amp; !variables_present
+        &quot;{-opt|#{argument}|#{variables.join(&quot;,&quot;)}}&quot;
+      elsif variables_present
         argument
       else
         &quot;&quot;
@@ -515,11 +606,14 @@ module Addressable
     # @param [Hash] mapping The mapping of variables to values.
     #
     # @return [String] The expanded result.
-    def expand_neg_operator(argument, variables, mapping)
-      if (variables.any? do |variable|
+    def expand_neg_operator(argument, variables, mapping, partial=false)
+      variables_present = variables.any? do |variable|
         mapping[variable] != [] &amp;&amp;
         mapping[variable]
-      end)
+      end
+      if partial &amp;&amp; !variables_present
+        &quot;{-neg|#{argument}|#{variables.join(&quot;,&quot;)}}&quot;
+      elsif variables_present
         &quot;&quot;
       else
         argument
@@ -534,16 +628,20 @@ module Addressable
     # @param [Hash] mapping The mapping of variables to values.
     #
     # @return [String] The expanded result.
-    def expand_prefix_operator(argument, variables, mapping)
+    def expand_prefix_operator(argument, variables, mapping, partial=false)
       if variables.size != 1
         raise InvalidTemplateOperatorError,
           &quot;Template operator 'prefix' takes exactly one variable.&quot;
       end
       value = mapping[variables.first]
-      if value.kind_of?(Array)
-        (value.map { |list_value| argument + list_value }).join(&quot;&quot;)
-      elsif value
-        argument + value.to_s
+      if !partial || value
+        if value.kind_of?(Array)
+          (value.map { |list_value| argument + list_value }).join(&quot;&quot;)
+        elsif value
+          argument + value.to_s
+        end
+      else
+        &quot;{-prefix|#{argument}|#{variables.first}}&quot;
       end
     end
 
@@ -555,16 +653,20 @@ module Addressable
     # @param [Hash] mapping The mapping of variables to values.
     #
     # @return [String] The expanded result.
-    def expand_suffix_operator(argument, variables, mapping)
+    def expand_suffix_operator(argument, variables, mapping, partial=false)
       if variables.size != 1
         raise InvalidTemplateOperatorError,
           &quot;Template operator 'suffix' takes exactly one variable.&quot;
       end
       value = mapping[variables.first]
-      if value.kind_of?(Array)
-        (value.map { |list_value| list_value + argument }).join(&quot;&quot;)
-      elsif value
-        value.to_s + argument
+      if !partial || value
+        if value.kind_of?(Array)
+          (value.map { |list_value| list_value + argument }).join(&quot;&quot;)
+        elsif value
+          value.to_s + argument
+        end
+      else
+        &quot;{-suffix|#{argument}|#{variables.first}}&quot;
       end
     end
 
@@ -576,19 +678,60 @@ module Addressable
     # @param [Hash] mapping The mapping of variables to values.
     #
     # @return [String] The expanded result.
-    def expand_join_operator(argument, variables, mapping)
-      variable_values = variables.inject([]) do |accu, variable|
-        if !mapping[variable].kind_of?(Array)
-          if mapping[variable]
-            accu &lt;&lt; variable + &quot;=&quot; + (mapping[variable])
+    def expand_join_operator(argument, variables, mapping, partial=false)
+      if !partial
+        variable_values = variables.inject([]) do |accu, variable|
+          if !mapping[variable].kind_of?(Array)
+            if mapping[variable]
+              accu &lt;&lt; variable + &quot;=&quot; + (mapping[variable])
+            end
+          else
+            raise InvalidTemplateOperatorError,
+              &quot;Template operator 'join' does not accept Array values.&quot;
           end
-        else
-          raise InvalidTemplateOperatorError,
-            &quot;Template operator 'join' does not accept Array values.&quot;
+          accu
         end
-        accu
+        variable_values.join(argument)
+      else
+        buffer = &quot;&quot;
+        state = :suffix
+        variables.each_with_index do |variable, index|
+          if !mapping[variable].kind_of?(Array)
+            if mapping[variable]
+              if buffer.empty? || buffer[-1..-1] == &quot;}&quot;
+                buffer &lt;&lt; (variable + &quot;=&quot; + (mapping[variable]))
+              elsif state == :suffix
+                buffer &lt;&lt; argument
+                buffer &lt;&lt; (variable + &quot;=&quot; + (mapping[variable]))
+              else
+                buffer &lt;&lt; (variable + &quot;=&quot; + (mapping[variable]))
+              end
+            else
+              if !buffer.empty? &amp;&amp; (buffer[-1..-1] != &quot;}&quot; || state == :prefix)
+                buffer &lt;&lt; &quot;{-opt|#{argument}|#{variable}}&quot;
+                state = :prefix
+              end
+              if buffer.empty? &amp;&amp; variables.size == 1
+                # Evaluates back to itself
+                buffer &lt;&lt; &quot;{-join|#{argument}|#{variable}}&quot;
+              else
+                buffer &lt;&lt; &quot;{-prefix|#{variable}=|#{variable}}&quot;
+              end
+              if (index != (variables.size - 1) &amp;&amp; state == :suffix)
+                buffer &lt;&lt; &quot;{-opt|#{argument}|#{variable}}&quot;
+              elsif index != (variables.size - 1) &amp;&amp;
+                  mapping[variables[index + 1]]
+                buffer &lt;&lt; argument
+                state = :prefix
+              end
+            end
+          else
+            raise InvalidTemplateOperatorError,
+              &quot;Template operator 'join' does not accept Array values.&quot;
+          end
+        end
+        buffer
       end
-      variable_values.join(argument)
     end
 
     ##
@@ -599,13 +742,24 @@ module Addressable
     # @param [Hash] mapping The mapping of variables to values.
     #
     # @return [String] The expanded result.
-    def expand_list_operator(argument, variables, mapping)
+    def expand_list_operator(argument, variables, mapping, partial=false)
       if variables.size != 1
         raise InvalidTemplateOperatorError,
           &quot;Template operator 'list' takes exactly one variable.&quot;
       end
-      values = mapping[variables.first]
-      values.join(argument) if values
+      if !partial || mapping[variables.first]
+        values = mapping[variables.first]
+        if values
+          if values.kind_of?(Array)
+            values.join(argument)
+          else
+            raise InvalidTemplateOperatorError,
+              &quot;Template operator 'list' only accepts Array values.&quot;
+          end
+        end
+      else
+        &quot;{-list|#{argument}|#{variables.first}}&quot;
+      end
     end
 
     ##
@@ -638,13 +792,6 @@ module Addressable
     # @return [Regexp]
     #   A regular expression which may be used to parse a template pattern.
     def parse_template_pattern(pattern, processor=nil)
-      reserved = Addressable::URI::CharacterClasses::RESERVED
-      unreserved = Addressable::URI::CharacterClasses::UNRESERVED
-      anything = reserved + unreserved
-      operator_expansion =
-        /\{-[a-zA-Z]+\|[#{anything}]+\|[#{anything}]+\}/
-      variable_expansion = /\{([#{anything}]+?)(=([#{anything}]+))?\}/
-
       # 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.
@@ -659,10 +806,10 @@ module Addressable
       # Create a regular expression that captures the values of the
       # variables in the URI.
       regexp_string = escaped_pattern.gsub(
-        /#{operator_expansion}|#{variable_expansion}/
+        /#{OPERATOR_EXPANSION}|#{VARIABLE_EXPANSION}/
       ) do |expansion|
         expansions &lt;&lt; expansion
-        if expansion =~ operator_expansion
+        if expansion =~ OPERATOR_EXPANSION
           capture_group = &quot;(.*)&quot;
           if processor != nil &amp;&amp; processor.respond_to?(:match)
             # We can only lookup the match values for single variable</diff>
      <filename>lib/addressable/template.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1199,3 +1199,682 @@ describe Addressable::URI, &quot;when given a pattern with bogus operators&quot; do
     )
   end
 end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/{one}/{two}/&quot;
+    )
+    @partial_template = @initial_template.partial_expand({&quot;one&quot; =&gt; &quot;1&quot;})
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;, &quot;two&quot; =&gt; &quot;2&quot;}).should ==
+      @partial_template.expand({&quot;two&quot; =&gt; &quot;2&quot;})
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/{one}/{two}/&quot;
+    )
+    @partial_template = @initial_template.partial_expand({&quot;two&quot; =&gt; &quot;2&quot;})
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;, &quot;two&quot; =&gt; &quot;2&quot;}).should ==
+      @partial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;})
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/{one}/{two}/&quot;
+    )
+    @partial_template = @initial_template.partial_expand({&quot;two&quot; =&gt; &quot;2&quot;})
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;, &quot;two&quot; =&gt; &quot;2&quot;}).should ==
+      @partial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;})
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/{one=1}/{two=2}/&quot;
+    )
+    @partial_template = @initial_template.partial_expand({&quot;one&quot; =&gt; &quot;3&quot;})
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({&quot;one&quot; =&gt; &quot;3&quot;, &quot;two&quot; =&gt; &quot;4&quot;}).should ==
+      @partial_template.expand({&quot;two&quot; =&gt; &quot;4&quot;})
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).should === &quot;http://example.com/3/2/&quot;
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/{one=1}/{two=2}/&quot;
+    )
+    @partial_template = @initial_template.partial_expand({&quot;two&quot; =&gt; &quot;4&quot;})
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({&quot;one&quot; =&gt; &quot;3&quot;, &quot;two&quot; =&gt; &quot;4&quot;}).should ==
+      @partial_template.expand({&quot;one&quot; =&gt; &quot;3&quot;})
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).should === &quot;http://example.com/1/4/&quot;
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/{-opt|found|one,two,three}&quot;
+    )
+    @partial_template = @initial_template.partial_expand({})
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;}).to_str.should ==
+      @partial_template.expand({&quot;two&quot; =&gt; &quot;2&quot;}).to_str
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).to_str.should == &quot;http://example.com/&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;}).to_str.should ==
+      &quot;http://example.com/found&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;two&quot; =&gt; &quot;2&quot;}).to_str.should ==
+      &quot;http://example.com/found&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;three&quot; =&gt; &quot;3&quot;}).to_str.should ==
+      &quot;http://example.com/found&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;four&quot; =&gt; &quot;4&quot;}).to_str.should ==
+      &quot;http://example.com/&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;, &quot;two&quot; =&gt; &quot;2&quot;}).to_str.should ==
+      &quot;http://example.com/found&quot;
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/{-opt|found|one,two,three}&quot;
+    )
+    @partial_template = @initial_template.partial_expand({&quot;one&quot; =&gt; &quot;1&quot;})
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;}).to_str.should ==
+      @partial_template.expand({&quot;two&quot; =&gt; &quot;2&quot;}).to_str
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).to_str.should == &quot;http://example.com/found&quot;
+  end
+
+  it &quot;should produce the correct pattern&quot; do
+    @partial_template.pattern.should == &quot;http://example.com/found&quot;
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/{-neg|notfound|one,two,three}&quot;
+    )
+    @partial_template = @initial_template.partial_expand({})
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;}).to_str.should ==
+      @partial_template.expand({&quot;two&quot; =&gt; &quot;2&quot;}).to_str
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).to_str.should == &quot;http://example.com/notfound&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;}).to_str.should ==
+      &quot;http://example.com/&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;two&quot; =&gt; &quot;2&quot;}).to_str.should ==
+      &quot;http://example.com/&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;three&quot; =&gt; &quot;3&quot;}).to_str.should ==
+      &quot;http://example.com/&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;four&quot; =&gt; &quot;4&quot;}).to_str.should ==
+      &quot;http://example.com/notfound&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;, &quot;two&quot; =&gt; &quot;2&quot;}).to_str.should ==
+      &quot;http://example.com/&quot;
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/{-neg|notfound|one,two,three}&quot;
+    )
+    @partial_template = @initial_template.partial_expand({&quot;one&quot; =&gt; &quot;1&quot;})
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;}).to_str.should ==
+      @partial_template.expand({&quot;two&quot; =&gt; &quot;2&quot;}).to_str
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).to_str.should == &quot;http://example.com/&quot;
+  end
+
+  it &quot;should produce the correct pattern&quot; do
+    @partial_template.pattern.should == &quot;http://example.com/&quot;
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/?{-prefix|x=|one}&quot;
+    )
+    @partial_template = @initial_template.partial_expand({})
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;}).to_str.should ==
+      @partial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;}).to_str
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).to_str.should == &quot;http://example.com/?&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;}).to_str.should ==
+      &quot;http://example.com/?x=1&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;two&quot; =&gt; &quot;2&quot;}).to_str.should ==
+      &quot;http://example.com/?&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;, &quot;two&quot; =&gt; &quot;2&quot;}).to_str.should ==
+      &quot;http://example.com/?x=1&quot;
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/?{-prefix|x=|one}&quot;
+    )
+    @partial_template = @initial_template.partial_expand({&quot;one&quot; =&gt; &quot;1&quot;})
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;}).to_str.should ==
+      @partial_template.expand({}).to_str
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).to_str.should == &quot;http://example.com/?x=1&quot;
+  end
+
+  it &quot;should produce the correct pattern&quot; do
+    @partial_template.pattern.should == &quot;http://example.com/?x=1&quot;
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/?{-suffix|=x|one}&quot;
+    )
+    @partial_template = @initial_template.partial_expand({})
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;}).to_str.should ==
+      @partial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;}).to_str
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).to_str.should == &quot;http://example.com/?&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;}).to_str.should ==
+      &quot;http://example.com/?1=x&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;two&quot; =&gt; &quot;2&quot;}).to_str.should ==
+      &quot;http://example.com/?&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;, &quot;two&quot; =&gt; &quot;2&quot;}).to_str.should ==
+      &quot;http://example.com/?1=x&quot;
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/?{-suffix|=x|one}&quot;
+    )
+    @partial_template = @initial_template.partial_expand({&quot;one&quot; =&gt; &quot;1&quot;})
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;}).to_str.should ==
+      @partial_template.expand({}).to_str
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).to_str.should == &quot;http://example.com/?1=x&quot;
+  end
+
+  it &quot;should produce the correct pattern&quot; do
+    @partial_template.pattern.should == &quot;http://example.com/?1=x&quot;
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/?{-join|&amp;|one}&quot;
+    )
+    @partial_template = @initial_template.partial_expand({})
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;}).to_str.should ==
+      @partial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;}).to_str
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).to_str.should == &quot;http://example.com/?&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.pattern.should == @initial_template.pattern
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/?{-join|&amp;|one,two}&quot;
+    )
+    @partial_template = @initial_template.partial_expand({&quot;one&quot; =&gt; &quot;1&quot;})
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;, &quot;two&quot; =&gt; &quot;2&quot;}).to_str.should ==
+      @partial_template.expand({&quot;two&quot; =&gt; &quot;2&quot;}).to_str
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).to_str.should == &quot;http://example.com/?one=1&quot;
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/?{-join|&amp;|one,two}&quot;
+    )
+    @partial_template = @initial_template.partial_expand({&quot;two&quot; =&gt; &quot;2&quot;})
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;, &quot;two&quot; =&gt; &quot;2&quot;}).to_str.should ==
+      @partial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;}).to_str
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).to_str.should == &quot;http://example.com/?two=2&quot;
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/?{-join|&amp;|one,two,three}&quot;
+    )
+    @partial_template = @initial_template.partial_expand({&quot;one&quot; =&gt; &quot;1&quot;})
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({
+      &quot;one&quot; =&gt; &quot;1&quot;, &quot;two&quot; =&gt; &quot;2&quot;, &quot;three&quot; =&gt; &quot;3&quot;
+    }).to_str.should ==
+      @partial_template.expand({&quot;two&quot; =&gt; &quot;2&quot;, &quot;three&quot; =&gt; &quot;3&quot;}).to_str
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).to_str.should == &quot;http://example.com/?one=1&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;two&quot; =&gt; &quot;2&quot;}).to_str.should ==
+      &quot;http://example.com/?one=1&amp;two=2&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;three&quot; =&gt; &quot;3&quot;}).to_str.should ==
+      &quot;http://example.com/?one=1&amp;three=3&quot;
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/?{-join|&amp;|one,two,three}&quot;
+    )
+    @partial_template = @initial_template.partial_expand({&quot;two&quot; =&gt; &quot;2&quot;})
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({
+      &quot;one&quot; =&gt; &quot;1&quot;, &quot;two&quot; =&gt; &quot;2&quot;, &quot;three&quot; =&gt; &quot;3&quot;
+    }).to_str.should ==
+      @partial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;, &quot;three&quot; =&gt; &quot;3&quot;}).to_str
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).to_str.should == &quot;http://example.com/?two=2&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;}).to_str.should ==
+      &quot;http://example.com/?one=1&amp;two=2&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;three&quot; =&gt; &quot;3&quot;}).to_str.should ==
+      &quot;http://example.com/?two=2&amp;three=3&quot;
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/?{-join|&amp;|one,two,three}&quot;
+    )
+    @partial_template = @initial_template.partial_expand({&quot;three&quot; =&gt; &quot;3&quot;})
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({
+      &quot;one&quot; =&gt; &quot;1&quot;, &quot;two&quot; =&gt; &quot;2&quot;, &quot;three&quot; =&gt; &quot;3&quot;
+    }).to_str.should ==
+      @partial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;, &quot;two&quot; =&gt; &quot;2&quot;}).to_str
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).to_str.should == &quot;http://example.com/?three=3&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;}).to_str.should ==
+      &quot;http://example.com/?one=1&amp;three=3&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;two&quot; =&gt; &quot;2&quot;}).to_str.should ==
+      &quot;http://example.com/?two=2&amp;three=3&quot;
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/?{-join|&amp;|one,two,three}&quot;
+    )
+    @partial_template = @initial_template.partial_expand({
+      &quot;one&quot; =&gt; &quot;1&quot;, &quot;two&quot; =&gt; &quot;2&quot;
+    })
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({
+      &quot;one&quot; =&gt; &quot;1&quot;, &quot;two&quot; =&gt; &quot;2&quot;, &quot;three&quot; =&gt; &quot;3&quot;
+    }).to_str.should ==
+      @partial_template.expand({&quot;three&quot; =&gt; &quot;3&quot;}).to_str
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).to_str.should ==
+      &quot;http://example.com/?one=1&amp;two=2&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;three&quot; =&gt; &quot;3&quot;}).to_str.should ==
+      &quot;http://example.com/?one=1&amp;two=2&amp;three=3&quot;
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/?{-join|&amp;|one,two,three}&quot;
+    )
+    @partial_template = @initial_template.partial_expand({
+      &quot;one&quot; =&gt; &quot;1&quot;, &quot;three&quot; =&gt; &quot;3&quot;
+    })
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({
+      &quot;one&quot; =&gt; &quot;1&quot;, &quot;two&quot; =&gt; &quot;2&quot;, &quot;three&quot; =&gt; &quot;3&quot;
+    }).to_str.should ==
+      @partial_template.expand({&quot;two&quot; =&gt; &quot;2&quot;}).to_str
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).to_str.should ==
+      &quot;http://example.com/?one=1&amp;three=3&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;two&quot; =&gt; &quot;2&quot;}).to_str.should ==
+      &quot;http://example.com/?one=1&amp;two=2&amp;three=3&quot;
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/?{-join|&amp;|one,two,three}&quot;
+    )
+    @partial_template = @initial_template.partial_expand({
+      &quot;two&quot; =&gt; &quot;2&quot;, &quot;three&quot; =&gt; &quot;3&quot;
+    })
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({
+      &quot;one&quot; =&gt; &quot;1&quot;, &quot;two&quot; =&gt; &quot;2&quot;, &quot;three&quot; =&gt; &quot;3&quot;
+    }).to_str.should ==
+      @partial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;}).to_str
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).to_str.should ==
+      &quot;http://example.com/?two=2&amp;three=3&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({&quot;one&quot; =&gt; &quot;1&quot;}).to_str.should ==
+      &quot;http://example.com/?one=1&amp;two=2&amp;three=3&quot;
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/?{-join|&amp;|one,two,three}&quot;
+    )
+  end
+
+  it &quot;should raise an error when partially expanding a bogus operator&quot; do
+    (lambda do
+      @initial_template.partial_expand({&quot;one&quot; =&gt; [&quot;1&quot;]})
+    end).should raise_error(
+      Addressable::Template::InvalidTemplateOperatorError
+    )
+    (lambda do
+      @initial_template.partial_expand({&quot;two&quot; =&gt; &quot;2&quot;, &quot;three&quot; =&gt; [&quot;3&quot;]})
+    end).should raise_error(
+      Addressable::Template::InvalidTemplateOperatorError
+    )
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/{-list|/|numbers}/{-list|/|letters}/&quot;
+    )
+    @partial_template = @initial_template.partial_expand({})
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({
+      &quot;numbers&quot; =&gt; [&quot;1&quot;, &quot;2&quot;, &quot;3&quot;], &quot;letters&quot; =&gt; [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]
+    }).to_str.should == @partial_template.expand({
+      &quot;numbers&quot; =&gt; [&quot;1&quot;, &quot;2&quot;, &quot;3&quot;], &quot;letters&quot; =&gt; [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]
+    }).to_str
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).to_str.should == &quot;http://example.com///&quot;
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.pattern.should == @initial_template.pattern
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/{-list|/|numbers}/{-list|/|letters}/&quot;
+    )
+    @partial_template = @initial_template.partial_expand({
+      &quot;numbers&quot; =&gt; [&quot;1&quot;, &quot;2&quot;, &quot;3&quot;]
+    })
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({
+      &quot;numbers&quot; =&gt; [&quot;1&quot;, &quot;2&quot;, &quot;3&quot;], &quot;letters&quot; =&gt; [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]
+    }).to_str.should == @partial_template.expand({
+      &quot;letters&quot; =&gt; [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]
+    }).to_str
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).to_str.should == &quot;http://example.com/1/2/3//&quot;
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/{-list|/|numbers}/{-list|/|letters}/&quot;
+    )
+    @partial_template = @initial_template.partial_expand({
+      &quot;letters&quot; =&gt; [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]
+    })
+  end
+
+  it &quot;should produce the same result when fully expanded&quot; do
+    @initial_template.expand({
+      &quot;numbers&quot; =&gt; [&quot;1&quot;, &quot;2&quot;, &quot;3&quot;], &quot;letters&quot; =&gt; [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]
+    }).to_str.should == @partial_template.expand({
+      &quot;numbers&quot; =&gt; [&quot;1&quot;, &quot;2&quot;, &quot;3&quot;]
+    }).to_str
+  end
+
+  it &quot;should produce the correct result when fully expanded&quot; do
+    @partial_template.expand({}).to_str.should == &quot;http://example.com//a/b/c/&quot;
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/{-list|/|numbers}/{-list|/|letters}/&quot;
+    )
+  end
+
+  it &quot;should raise an error when partially expanding a bogus operator&quot; do
+    (lambda do
+      @initial_template.partial_expand({&quot;numbers&quot; =&gt; &quot;1&quot;})
+    end).should raise_error(
+      Addressable::Template::InvalidTemplateOperatorError
+    )
+    (lambda do
+      @initial_template.partial_expand({&quot;letters&quot; =&gt; &quot;a&quot;})
+    end).should raise_error(
+      Addressable::Template::InvalidTemplateOperatorError
+    )
+  end
+end
+
+describe Addressable::Template, &quot;with a partially expanded template&quot; do
+  before do
+    @initial_template = Addressable::Template.new(
+      &quot;http://example.com/{-bogus|/|one,two}/&quot;
+    )
+  end
+
+  it &quot;should raise an error when partially expanding a bogus operator&quot; do
+    (lambda do
+      @initial_template.partial_expand({&quot;one&quot; =&gt; &quot;1&quot;})
+    end).should raise_error(
+      Addressable::Template::InvalidTemplateOperatorError
+    )
+  end
+end</diff>
      <filename>spec/addressable/template_spec.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>ad45047ad60bdc18803a61b67fb412d45ee193f3</id>
    </parent>
  </parents>
  <author>
    <name>Bob Aman</name>
    <email>bob@sporkmonger.com</email>
  </author>
  <url>http://github.com/sporkmonger/addressable/commit/ac09893e98ef6922ac2bf6962de1b757b5058d51</url>
  <id>ac09893e98ef6922ac2bf6962de1b757b5058d51</id>
  <committed-date>2009-04-28T15:10:19-07:00</committed-date>
  <authored-date>2009-04-28T15:10:19-07:00</authored-date>
  <message>Implemented partial template expansions.</message>
  <tree>882dccdb292aacf162e081eba6b67913f743b01e</tree>
  <committer>
    <name>Bob Aman</name>
    <email>bob@sporkmonger.com</email>
  </committer>
</commit>
