Parent

Files

Addressable::Template

This is an implementation of a URI template based on URI">tinyurl.com/uritemplatedraft03">URI Template draft 03.

Constants

OPERATOR_EXPANSION
VARIABLE_EXPANSION

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 [#] pattern The URI Template pattern.

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

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

Public Instance Methods

expand(mapping, processor=nil) click to toggle source

Expands a URI template into a full URI.

@param [Hash] mapping The mapping that corresponds to the pattern. @param [#, #] 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/{-list|+|query}/"
  ).expand(
    {"query" => "an example search query".split(" ")}
  ).to_str
  #=> "http://example.com/search/an+example+search+query/"

  Addressable::Template.new(
    "http://example.com/search/{query}/"
  ).expand(
    {"query" => "bogus!"},
    ExampleProcessor
  ).to_str
  #=> Addressable::Template::InvalidTemplateValueError
     # File lib/addressable/template.rb, line 456
456:     def expand(mapping, processor=nil)
457:       result = self.pattern.dup
458:       transformed_mapping = transform_mapping(mapping, processor)
459:       result.gsub!(
460:         /#{OPERATOR_EXPANSION}|#{VARIABLE_EXPANSION}/
461:       ) do |capture|
462:         if capture =~ OPERATOR_EXPANSION
463:           operator, argument, variables, default_mapping =
464:             parse_template_expansion(capture, transformed_mapping)
465:           expand_method = "expand_#{operator}_operator"
466:           if ([expand_method, expand_method.to_sym] & private_methods).empty?
467:             raise InvalidTemplateOperatorError,
468:               "Invalid template operator: #{operator}"
469:           else
470:             send(expand_method.to_sym, argument, variables, default_mapping)
471:           end
472:         else
473:           varname, _, vardefault = capture.scan(/^\{(.+?)(=(.*))?\}$/)[0]
474:           transformed_mapping[varname] || vardefault
475:         end
476:       end
477:       return Addressable::URI.parse(result)
478:     end
extract(uri, processor=nil) click to toggle source

Extracts a mapping from the URI using a URI Template pattern.

@param [Addressable::URI, #] uri

  The URI to extract from.

@param [#, #] 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"]}
     # File lib/addressable/template.rb, line 204
204:     def extract(uri, processor=nil)
205:       match_data = self.match(uri, processor)
206:       return (match_data ? match_data.mapping : nil)
207:     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 144
144:     def inspect
145:       sprintf("#<%s:%#0x PATTERN:%s>",
146:         self.class.to_s, self.object_id, self.pattern)
147:     end
keys() click to toggle source
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, #] uri

  The URI to extract from.

@param [#, #] 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}/{-list|/|second}/"
  ).match(uri)
  match.variables
  #=> ["first", "second"]
  match.captures
  #=> ["a", ["b", "c"]]
     # File lib/addressable/template.rb, line 273
273:     def match(uri, processor=nil)
274:       uri = Addressable::URI.parse(uri)
275:       mapping = {}
276: 
277:       # First, we need to process the pattern, and extract the values.
278:       expansions, expansion_regexp =
279:         parse_template_pattern(pattern, processor)
280:       unparsed_values = uri.to_str.scan(expansion_regexp).flatten
281: 
282:       if uri.to_str == pattern
283:         return Addressable::Template::MatchData.new(uri, self, mapping)
284:       elsif expansions.size > 0 && expansions.size == unparsed_values.size
285:         expansions.each_with_index do |expansion, index|
286:           unparsed_value = unparsed_values[index]
287:           if expansion =~ OPERATOR_EXPANSION
288:             operator, argument, variables =
289:               parse_template_expansion(expansion)
290:             extract_method = "extract_#{operator}_operator"
291:             if ([extract_method, extract_method.to_sym] &
292:                 private_methods).empty?
293:               raise InvalidTemplateOperatorError,
294:                 "Invalid template operator: #{operator}"
295:             else
296:               begin
297:                 send(
298:                   extract_method.to_sym, unparsed_value, processor,
299:                   argument, variables, mapping
300:                 )
301:               rescue TemplateOperatorAbortedError
302:                 return nil
303:               end
304:             end
305:           else
306:             name = expansion[VARIABLE_EXPANSION, 1]
307:             value = unparsed_value
308:             if processor != nil && processor.respond_to?(:restore)
309:               value = processor.restore(name, value)
310:             end
311:             if mapping[name] == nil || mapping[name] == value
312:               mapping[name] = value
313:             else
314:               return nil
315:             end
316:           end
317:         end
318:         return Addressable::Template::MatchData.new(uri, self, mapping)
319:       else
320:         return nil
321:       end
322:     end
partial_expand(mapping, processor=nil) click to toggle source

Expands a URI template into another URI template.

@param [Hash] mapping The mapping that corresponds to the pattern. @param [#, #] 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/search/{-list|+|query}/"
  ).partial_expand(
    {"query" => "an example search query".split(" ")}
  ).pattern
  #=> "http://example.com/search/an+example+search+query/"

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

  Addressable::Template.new(
    "http://example.com/{-join|&|one,two,three}/"
  ).partial_expand({"one" => "1", "three" => 3}).pattern
  #=> "http://example.com/?one=1{-prefix|&two=|two}&three=3"
     # File lib/addressable/template.rb, line 367
367:     def partial_expand(mapping, processor=nil)
368:       result = self.pattern.dup
369:       transformed_mapping = transform_mapping(mapping, processor)
370:       result.gsub!(
371:         /#{OPERATOR_EXPANSION}|#{VARIABLE_EXPANSION}/
372:       ) do |capture|
373:         if capture =~ OPERATOR_EXPANSION
374:           operator, argument, variables, default_mapping =
375:             parse_template_expansion(capture, transformed_mapping)
376:           expand_method = "expand_#{operator}_operator"
377:           if ([expand_method, expand_method.to_sym] & private_methods).empty?
378:             raise InvalidTemplateOperatorError,
379:               "Invalid template operator: #{operator}"
380:           else
381:             send(
382:               expand_method.to_sym, argument, variables,
383:               default_mapping, true
384:             )
385:           end
386:         else
387:           varname, _, vardefault = capture.scan(/^\{(.+?)(=(.*))?\}$/)[0]
388:           if transformed_mapping[varname]
389:             transformed_mapping[varname]
390:           elsif vardefault
391:             "{#{varname}=#{vardefault}}"
392:           else
393:             "{#{varname}}"
394:           end
395:         end
396:       end
397:       return Addressable::Template.new(result)
398:     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 497
497:     def variable_defaults
498:       @variable_defaults ||=
499:         Hash[*ordered_variable_defaults.reject { |k, v| v.nil? }.flatten]
500:     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 487
487:     def variables
488:       @variables ||= ordered_variable_defaults.map { |var, val| var }.uniq
489:     end
Also aliased as: keys

Private Instance Methods

expand_join_operator(argument, variables, mapping, partial=false) click to toggle source

Expands a URI Template join operator.

@param [String] argument The argument to the operator. @param [Array] variables The variables the operator is working on. @param [Hash] mapping The mapping of variables to values.

@return [String] The expanded result.

     # File lib/addressable/template.rb, line 710
710:     def expand_join_operator(argument, variables, mapping, partial=false)
711:       if !partial
712:         variable_values = variables.inject([]) do |accu, variable|
713:           if !mapping[variable].kind_of?(Array)
714:             if mapping[variable]
715:               accu << variable + "=" + (mapping[variable])
716:             end
717:           else
718:             raise InvalidTemplateOperatorError,
719:               "Template operator 'join' does not accept Array values."
720:           end
721:           accu
722:         end
723:         variable_values.join(argument)
724:       else
725:         buffer = ""
726:         state = :suffix
727:         variables.each_with_index do |variable, index|
728:           if !mapping[variable].kind_of?(Array)
729:             if mapping[variable]
730:               if buffer.empty? || buffer[1..1] == "}"
731:                 buffer << (variable + "=" + (mapping[variable]))
732:               elsif state == :suffix
733:                 buffer << argument
734:                 buffer << (variable + "=" + (mapping[variable]))
735:               else
736:                 buffer << (variable + "=" + (mapping[variable]))
737:               end
738:             else
739:               if !buffer.empty? && (buffer[1..1] != "}" || state == :prefix)
740:                 buffer << "{-opt|#{argument}|#{variable}}"
741:                 state = :prefix
742:               end
743:               if buffer.empty? && variables.size == 1
744:                 # Evaluates back to itself
745:                 buffer << "{-join|#{argument}|#{variable}}"
746:               else
747:                 buffer << "{-prefix|#{variable}=|#{variable}}"
748:               end
749:               if (index != (variables.size - 1) && state == :suffix)
750:                 buffer << "{-opt|#{argument}|#{variable}}"
751:               elsif index != (variables.size - 1) &&
752:                   mapping[variables[index + 1]]
753:                 buffer << argument
754:                 state = :prefix
755:               end
756:             end
757:           else
758:             raise InvalidTemplateOperatorError,
759:               "Template operator 'join' does not accept Array values."
760:           end
761:         end
762:         buffer
763:       end
764:     end
expand_list_operator(argument, variables, mapping, partial=false) click to toggle source

Expands a URI Template list operator.

@param [String] argument The argument to the operator. @param [Array] variables The variables the operator is working on. @param [Hash] mapping The mapping of variables to values.

@return [String] The expanded result.

     # File lib/addressable/template.rb, line 774
774:     def expand_list_operator(argument, variables, mapping, partial=false)
775:       if variables.size != 1
776:         raise InvalidTemplateOperatorError,
777:           "Template operator 'list' takes exactly one variable."
778:       end
779:       if !partial || mapping[variables.first]
780:         values = mapping[variables.first]
781:         if values
782:           if values.kind_of?(Array)
783:             values.join(argument)
784:           else
785:             raise InvalidTemplateOperatorError,
786:               "Template operator 'list' only accepts Array values."
787:           end
788:         end
789:       else
790:         "{-list|#{argument}|#{variables.first}}"
791:       end
792:     end
expand_neg_operator(argument, variables, mapping, partial=false) click to toggle source

Expands a URI Template neg operator.

@param [String] argument The argument to the operator. @param [Array] variables The variables the operator is working on. @param [Hash] mapping The mapping of variables to values.

@return [String] The expanded result.

     # File lib/addressable/template.rb, line 638
638:     def expand_neg_operator(argument, variables, mapping, partial=false)
639:       variables_present = variables.any? do |variable|
640:         mapping[variable] != [] &&
641:         mapping[variable]
642:       end
643:       if partial && !variables_present
644:         "{-neg|#{argument}|#{variables.join(",")}}"
645:       elsif variables_present
646:         ""
647:       else
648:         argument
649:       end
650:     end
expand_opt_operator(argument, variables, mapping, partial=false) click to toggle source

Expands a URI Template opt operator.

@param [String] argument The argument to the operator. @param [Array] variables The variables the operator is working on. @param [Hash] mapping The mapping of variables to values.

@return [String] The expanded result.

     # File lib/addressable/template.rb, line 616
616:     def expand_opt_operator(argument, variables, mapping, partial=false)
617:       variables_present = variables.any? do |variable|
618:         mapping[variable] != [] &&
619:         mapping[variable]
620:       end
621:       if partial && !variables_present
622:         "{-opt|#{argument}|#{variables.join(",")}}"
623:       elsif variables_present
624:         argument
625:       else
626:         ""
627:       end
628:     end
expand_prefix_operator(argument, variables, mapping, partial=false) click to toggle source

Expands a URI Template prefix operator.

@param [String] argument The argument to the operator. @param [Array] variables The variables the operator is working on. @param [Hash] mapping The mapping of variables to values.

@return [String] The expanded result.

     # File lib/addressable/template.rb, line 660
660:     def expand_prefix_operator(argument, variables, mapping, partial=false)
661:       if variables.size != 1
662:         raise InvalidTemplateOperatorError,
663:           "Template operator 'prefix' takes exactly one variable."
664:       end
665:       value = mapping[variables.first]
666:       if !partial || value
667:         if value.kind_of?(Array)
668:           (value.map { |list_value| argument + list_value }).join("")
669:         elsif value
670:           argument + value.to_s
671:         end
672:       else
673:         "{-prefix|#{argument}|#{variables.first}}"
674:       end
675:     end
expand_suffix_operator(argument, variables, mapping, partial=false) click to toggle source

Expands a URI Template suffix operator.

@param [String] argument The argument to the operator. @param [Array] variables The variables the operator is working on. @param [Hash] mapping The mapping of variables to values.

@return [String] The expanded result.

     # File lib/addressable/template.rb, line 685
685:     def expand_suffix_operator(argument, variables, mapping, partial=false)
686:       if variables.size != 1
687:         raise InvalidTemplateOperatorError,
688:           "Template operator 'suffix' takes exactly one variable."
689:       end
690:       value = mapping[variables.first]
691:       if !partial || value
692:         if value.kind_of?(Array)
693:           (value.map { |list_value| list_value + argument }).join("")
694:         elsif value
695:           value.to_s + argument
696:         end
697:       else
698:         "{-suffix|#{argument}|#{variables.first}}"
699:       end
700:     end
extract_join_operator(value, processor, argument, variables, mapping) click to toggle source

Extracts a URI Template join operator.

@param [String] value The unparsed value to extract from. @param [#] processor The processor object. @param [String] argument The argument to the operator. @param [Array] variables The variables the operator is working on. @param [Hash] mapping The mapping of variables to values.

@return [String] The extracted result.

      # File lib/addressable/template.rb, line 991
 991:     def extract_join_operator(value, processor, argument, variables, mapping)
 992:       unparsed_values = value.split(argument)
 993:       parsed_variables = []
 994:       for unparsed_value in unparsed_values
 995:         name = unparsed_value[/^(.+?)=(.+)$/, 1]
 996:         parsed_variables << name
 997:         parsed_value = unparsed_value[/^(.+?)=(.+)$/, 2]
 998:         if processor && processor.respond_to?(:restore)
 999:           parsed_value = processor.restore(name, parsed_value)
1000:         end
1001:         if mapping[name] == nil || mapping[name] == parsed_value
1002:           mapping[name] = parsed_value
1003:         else
1004:           raise TemplateOperatorAbortedError,
1005:             "Value mismatch for repeated variable."
1006:         end
1007:       end
1008:       for variable in variables
1009:         if !parsed_variables.include?(variable) && mapping[variable] != nil
1010:           raise TemplateOperatorAbortedError,
1011:             "Value mismatch for repeated variable."
1012:         end
1013:       end
1014:       if (parsed_variables & variables) != parsed_variables
1015:         raise TemplateOperatorAbortedError,
1016:           "Template operator 'join' variable mismatch: " +
1017:           "#{parsed_variables.inspect}, #{variables.inspect}"
1018:       end
1019:     end
extract_list_operator(value, processor, argument, variables, mapping) click to toggle source

Extracts a URI Template list operator.

@param [String] value The unparsed value to extract from. @param [#] processor The processor object. @param [String] argument The argument to the operator. @param [Array] variables The variables the operator is working on. @param [Hash] mapping The mapping of variables to values.

@return [String] The extracted result.

      # File lib/addressable/template.rb, line 1031
1031:     def extract_list_operator(value, processor, argument, variables, mapping)
1032:       if variables.size != 1
1033:         raise InvalidTemplateOperatorError,
1034:           "Template operator 'list' takes exactly one variable."
1035:       end
1036:       values = value.split(argument, 1)
1037:       values.pop if values[1] == ""
1038:       if processor && processor.respond_to?(:restore)
1039:         values.map! { |value| processor.restore(variables.first, value) }
1040:       end
1041:       if mapping[variables.first] == nil || mapping[variables.first] == values
1042:         mapping[variables.first] = values
1043:       else
1044:         raise TemplateOperatorAbortedError,
1045:           "Value mismatch for repeated variable."
1046:       end
1047:     end
extract_neg_operator( value, processor, argument, variables, mapping) click to toggle source

Extracts a URI Template neg operator.

@param [String] value The unparsed value to extract from. @param [#] processor The processor object. @param [String] argument The argument to the operator. @param [Array] variables The variables the operator is working on. @param [Hash] mapping The mapping of variables to values.

@return [String] The extracted result.

     # File lib/addressable/template.rb, line 902
902:     def extract_neg_operator(
903:         value, processor, argument, variables, mapping)
904:       if value != "" && value != argument
905:         raise TemplateOperatorAbortedError,
906:           "Value for template operator 'neg' was unexpected."
907:       end
908:     end
extract_opt_operator( value, processor, argument, variables, mapping) click to toggle source

Extracts a URI Template opt operator.

@param [String] value The unparsed value to extract from. @param [#] processor The processor object. @param [String] argument The argument to the operator. @param [Array] variables The variables the operator is working on. @param [Hash] mapping The mapping of variables to values.

@return [String] The extracted result.

     # File lib/addressable/template.rb, line 884
884:     def extract_opt_operator(
885:         value, processor, argument, variables, mapping)
886:       if value != "" && value != argument
887:         raise TemplateOperatorAbortedError,
888:           "Value for template operator 'opt' was unexpected."
889:       end
890:     end
extract_prefix_operator( value, processor, argument, variables, mapping) click to toggle source

Extracts a URI Template prefix operator.

@param [String] value The unparsed value to extract from. @param [#] processor The processor object. @param [String] argument The argument to the operator. @param [Array] variables The variables the operator is working on. @param [Hash] mapping The mapping of variables to values.

@return [String] The extracted result.

     # File lib/addressable/template.rb, line 920
920:     def extract_prefix_operator(
921:         value, processor, argument, variables, mapping)
922:       if variables.size != 1
923:         raise InvalidTemplateOperatorError,
924:           "Template operator 'prefix' takes exactly one variable."
925:       end
926:       if value[0...argument.size] != argument
927:         raise TemplateOperatorAbortedError,
928:           "Value for template operator 'prefix' missing expected prefix."
929:       end
930:       values = value.split(argument, 1)
931:       values << "" if value[-argument.size..1] == argument
932:       values.shift if values[0] == ""
933:       values.pop if values[1] == ""
934: 
935:       if processor && processor.respond_to?(:restore)
936:         values.map! { |value| processor.restore(variables.first, value) }
937:       end
938:       values = values.first if values.size == 1
939:       if mapping[variables.first] == nil || mapping[variables.first] == values
940:         mapping[variables.first] = values
941:       else
942:         raise TemplateOperatorAbortedError,
943:           "Value mismatch for repeated variable."
944:       end
945:     end
extract_suffix_operator( value, processor, argument, variables, mapping) click to toggle source

Extracts a URI Template suffix operator.

@param [String] value The unparsed value to extract from. @param [#] processor The processor object. @param [String] argument The argument to the operator. @param [Array] variables The variables the operator is working on. @param [Hash] mapping The mapping of variables to values.

@return [String] The extracted result.

     # File lib/addressable/template.rb, line 957
957:     def extract_suffix_operator(
958:         value, processor, argument, variables, mapping)
959:       if variables.size != 1
960:         raise InvalidTemplateOperatorError,
961:           "Template operator 'suffix' takes exactly one variable."
962:       end
963:       if value[-argument.size..1] != argument
964:         raise TemplateOperatorAbortedError,
965:           "Value for template operator 'suffix' missing expected suffix."
966:       end
967:       values = value.split(argument, 1)
968:       values.pop if values[1] == ""
969:       if processor && processor.respond_to?(:restore)
970:         values.map! { |value| processor.restore(variables.first, value) }
971:       end
972:       values = values.first if values.size == 1
973:       if mapping[variables.first] == nil || mapping[variables.first] == values
974:         mapping[variables.first] = values
975:       else
976:         raise TemplateOperatorAbortedError,
977:           "Value mismatch for repeated variable."
978:       end
979:     end
ordered_variable_defaults() click to toggle source
     # File lib/addressable/template.rb, line 503
503:     def ordered_variable_defaults
504:       @ordered_variable_defaults ||= (begin
505:         expansions, expansion_regexp = parse_template_pattern(pattern)
506: 
507:         expansions.inject([]) do |result, expansion|
508:           case expansion
509:           when OPERATOR_EXPANSION
510:             _, _, variables, mapping = parse_template_expansion(expansion)
511:             result.concat variables.map { |var| [var, mapping[var]] }
512:           when VARIABLE_EXPANSION
513:             result << [$1, $2]
514:           end
515:           result
516:         end
517:       end)
518:     end
parse_template_expansion(capture, mapping={}) click to toggle source

Parses a URI template expansion String.

@param [String] expansion The operator String. @param [Hash] mapping An optional mapping to merge defaults into.

@return [Array]

  A tuple of the operator, argument, variables, and mapping.
     # File lib/addressable/template.rb, line 802
802:     def parse_template_expansion(capture, mapping={})
803:       operator, argument, variables = capture[1...1].split("|", 1)
804:       operator.gsub!(/^\-/, "")
805:       variables = variables.split(",", 1)
806:       mapping = (variables.inject({}) do |accu, var|
807:         varname, _, vardefault = var.scan(/^(.+?)(=(.*))?$/)[0]
808:         accu[varname] = vardefault
809:         accu
810:       end).merge(mapping)
811:       variables = variables.map { |var| var.gsub(/=.*$/, "") }
812:       return operator, argument, variables, mapping
813:     end
parse_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 [#] processor The template processor to use.

@return [Regexp]

  A regular expression which may be used to parse a template pattern.
     # File lib/addressable/template.rb, line 823
823:     def parse_template_pattern(pattern, processor=nil)
824:       # Escape the pattern. The two gsubs restore the escaped curly braces
825:       # back to their original form. Basically, escape everything that isn't
826:       # within an expansion.
827:       escaped_pattern = Regexp.escape(
828:         pattern
829:       ).gsub(/\\\{(.*?)\\\}/) do |escaped|
830:         escaped.gsub(/\\(.)/, "\\1")
831:       end
832: 
833:       expansions = []
834: 
835:       # Create a regular expression that captures the values of the
836:       # variables in the URI.
837:       regexp_string = escaped_pattern.gsub(
838:         /#{OPERATOR_EXPANSION}|#{VARIABLE_EXPANSION}/
839:       ) do |expansion|
840:         expansions << expansion
841:         if expansion =~ OPERATOR_EXPANSION
842:           capture_group = "(.*)"
843:           operator, argument, names, _ =
844:             parse_template_expansion(expansion)
845:           if processor != nil && processor.respond_to?(:match)
846:             # We can only lookup the match values for single variable
847:             # operator expansions. Besides, ".*" is usually the only
848:             # reasonable value for multivariate operators anyways.
849:             if ["prefix", "suffix", "list"].include?(operator)
850:               capture_group = "(#{processor.match(names.first)})"
851:             end
852:           elsif operator == "prefix"
853:             capture_group = "(#{Regexp.escape(argument)}.*?)"
854:           elsif operator == "suffix"
855:             capture_group = "(.*?#{Regexp.escape(argument)})"
856:           end
857:           capture_group
858:         else
859:           capture_group = "(.*?)"
860:           if processor != nil && processor.respond_to?(:match)
861:             name = expansion[/\{([^\}=]+)(=[^\}]+)?\}/, 1]
862:             capture_group = "(#{processor.match(name)})"
863:           end
864:           capture_group
865:         end
866:       end
867: 
868:       # Ensure that the regular expression matches the whole URI.
869:       regexp_string = "^#{regexp_string}$"
870: 
871:       return expansions, Regexp.new(regexp_string)
872:     end
transform_mapping(mapping, processor=nil) click to toggle source

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

@param [Hash] mapping The mapping of variables to values. @param [#, #] 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 [Hash] The transformed mapping.

     # File lib/addressable/template.rb, line 541
541:     def transform_mapping(mapping, processor=nil)
542:       return mapping.inject({}) do |accu, pair|
543:         name, value = pair
544:         value = value.to_s if Numeric === value || Symbol === value
545: 
546:         unless value.respond_to?(:to_ary) || value.respond_to?(:to_str)
547:           raise TypeError,
548:             "Can't convert #{value.class} into String or Array."
549:         end
550: 
551:         if Symbol === name
552:           name = name.to_s
553:         elsif name.respond_to?(:to_str)
554:           name = name.to_str
555:         else
556:           raise TypeError,
557:             "Can't convert #{name.class} into String."
558:         end
559:         value = value.respond_to?(:to_ary) ? value.to_ary : value.to_str
560: 
561:         # Handle unicode normalization
562:         if value.kind_of?(Array)
563:           value.map! { |val| Addressable::IDNA.unicode_normalize_kc(val) }
564:         else
565:           value = Addressable::IDNA.unicode_normalize_kc(value)
566:         end
567: 
568:         if processor == nil || !processor.respond_to?(:transform)
569:           # Handle percent escaping
570:           if value.kind_of?(Array)
571:             transformed_value = value.map do |val|
572:               Addressable::URI.encode_component(
573:                 val, Addressable::URI::CharacterClasses::UNRESERVED)
574:             end
575:           else
576:             transformed_value = Addressable::URI.encode_component(
577:               value, Addressable::URI::CharacterClasses::UNRESERVED)
578:           end
579:         end
580: 
581:         # Process, if we've got a processor
582:         if processor != nil
583:           if processor.respond_to?(:validate)
584:             if !processor.validate(name, value)
585:               display_value = value.kind_of?(Array) ? value.inspect : value
586:               raise InvalidTemplateValueError,
587:                 "#{name}=#{display_value} is an invalid template value."
588:             end
589:           end
590:           if processor.respond_to?(:transform)
591:             transformed_value = processor.transform(name, value)
592:             if transformed_value.kind_of?(Array)
593:               transformed_value.map! do |val|
594:                 Addressable::IDNA.unicode_normalize_kc(val)
595:               end
596:             else
597:               transformed_value =
598:                 Addressable::IDNA.unicode_normalize_kc(transformed_value)
599:             end
600:           end
601:         end
602: 
603:         accu[name] = transformed_value
604:         accu
605:       end
606:     end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.