Class: ActionController::Routing::Route
- Defined in:
- lib/action_controller/routing.rb
Overview
:nodoc:
Instance Attribute Summary collapse
-
#defaults ⇒ Object
readonly
The defaults hash.
Instance Method Summary collapse
-
#generate(options, defaults = {}) ⇒ Object
Generate a URL given the provided options.
-
#initialize(path, hash = {}) ⇒ Route
constructor
A new instance of Route.
- #inspect ⇒ Object
-
#recognize(components, options = {}) ⇒ Object
Recognize the provided path, returning a hash of recognized values, or [nil, reason] if the path isn’t recognized.
Constructor Details
#initialize(path, hash = {}) ⇒ Route
Returns a new instance of Route.
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/action_controller/routing.rb', line 7 def initialize(path, hash={}) raise ArgumentError, "Second argument must be a hash!" unless hash.kind_of?(Hash) @defaults = hash[:defaults].kind_of?(Hash) ? hash.delete(:defaults) : {} @requirements = hash[:requirements].kind_of?(Hash) ? hash.delete(:requirements) : {} self.items = path hash.each do |k, v| raise TypeError, "Hash keys must be symbols!" unless k.kind_of? Symbol if v.kind_of? Regexp raise ArgumentError, "Regexp requirement on #{k}, but #{k} is not in this route's path!" unless @items.include? k @requirements[k] = v else (@items.include?(k) ? @defaults : @requirements)[k] = (v.nil? ? nil : v.to_s) end end @defaults.each do |k, v| raise ArgumentError, "A default has been specified for #{k}, but #{k} is not in the path!" unless @items.include? k @defaults[k] = v.to_s unless v.kind_of?(String) || v.nil? end @requirements.each {|k, v| raise ArgumentError, "A Regexp requirement has been specified for #{k}, but #{k} is not in the path!" if v.kind_of?(Regexp) && ! @items.include?(k)} # Add in defaults for :action and :id. [[:action, 'index'], [:id, nil]].each do |name, default| @defaults[name] = default if @items.include?(name) && ! (@requirements.key?(name) || @defaults.key?(name)) end end |
Instance Attribute Details
#defaults ⇒ Object (readonly)
The defaults hash
5 6 7 |
# File 'lib/action_controller/routing.rb', line 5 def defaults @defaults end |
Instance Method Details
#generate(options, defaults = {}) ⇒ Object
Generate a URL given the provided options. All values in options should be symbols. Returns the path and the unused names in a 2 element array. If generation fails, [nil, nil] is returned Generation can fail because of a missing value, or because an equality check fails.
Generate urls will be as short as possible. If the last component of a url is equal to the default value, then that component is removed. This is applied as many times as possible. So, your index controller’s index action will generate []
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/action_controller/routing.rb', line 43 def generate(, defaults={}) non_matching = @requirements.keys.select {|name| ! passes_requirements?(name, [name] || defaults[name])} non_matching.collect! {|name| requirements_for(name)} return nil, "Mismatching option#{'s' if non_matching.length > 1}:\n #{non_matching.join '\n '}" unless non_matching.empty? used_names = @requirements.inject({}) {|hash, (k, v)| hash[k] = true; hash} # Mark requirements as used so they don't get put in the query params components = @items.collect do |item| if item.kind_of? Symbol collection = false if /^\*/ =~ item.to_s collection = true item = item.to_s.sub(/^\*/,"").intern end used_names[item] = true value = [item] || defaults[item] || @defaults[item] return nil, requirements_for(item) unless passes_requirements?(item, value) defaults = {} unless defaults == {} || value == defaults[item] # Stop using defaults if this component isn't the same as the default. if value.nil? || item == :controller value elsif collection if value.kind_of?(Array) value = value.collect {|v| Routing.extract_parameter_value(v)}.join('/') else value = Routing.extract_parameter_value(value).gsub(/%2F/, "/") end value else Routing.extract_parameter_value(value) end else item end end @items.reverse_each do |item| # Remove default components from the end of the generated url. break unless item.kind_of?(Symbol) && @defaults[item] == components.last components.pop end # If we have any nil components then we can't proceed. # This might need to be changed. In some cases we may be able to return all componets after nil as extras. missing = []; components.each_with_index {|c, i| missing << @items[i] if c.nil?} return nil, "No values provided for component#{'s' if missing.length > 1} #{missing.join ', '} but values are required due to use of later components" unless missing.empty? # how wide is your screen? unused = (.keys - used_names.keys).inject({}) do |unused, key| unused[key] = [key] if [key] != @defaults[key] unused end components.collect! {|c| c.to_s} return components, unused end |
#inspect ⇒ Object
151 152 153 154 155 |
# File 'lib/action_controller/routing.rb', line 151 def inspect when_str = @requirements.empty? ? "" : " when #{@requirements.inspect}" default_str = @defaults.empty? ? "" : " || #{@defaults.inspect}" "<#{self.class.to_s} #{@items.collect{|c| c.kind_of?(String) ? c : c.inspect}.join('/').inspect}#{default_str}#{when_str}>" end |
#recognize(components, options = {}) ⇒ Object
Recognize the provided path, returning a hash of recognized values, or [nil, reason] if the path isn’t recognized. The path should be a list of component strings. Options is a hash of the ?k=v pairs
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/action_controller/routing.rb', line 104 def recognize(components, ={}) = .clone components = components.clone controller_class = nil @items.each do |item| if item == :controller # Special case for controller if components.empty? && @defaults[:controller] controller_class, leftover = eat_path_to_controller(@defaults[:controller].split('/')) raise RoutingError, "Default controller does not exist: #{@defaults[:controller]}" if controller_class.nil? || leftover.empty? == false else controller_class, remaining_components = eat_path_to_controller(components) return nil, "No controller found at subpath #{components.join('/')}" if controller_class.nil? components = remaining_components end [:controller] = controller_class.controller_path return nil, requirements_for(:controller) unless passes_requirements?(:controller, [:controller]) elsif /^\*/ =~ item.to_s value = components.empty? ? @defaults[item].clone : components.clone value.collect! {|c| CGI.unescape c} components = [] def value.to_s() self.join('/') end [item.to_s.sub(/^\*/,"").intern] = value elsif item.kind_of? Symbol value = components.shift || @defaults[item] return nil, requirements_for(item) unless passes_requirements?(item, value) [item] = value.nil? ? value : CGI.unescape(value) else return nil, "No value available for component #{item.inspect}" if components.empty? component = components.shift return nil, "Value for component #{item.inspect} doesn't match #{component}" if component != item end end if controller_class.nil? && @requirements[:controller] # Load a default controller controller_class, extras = eat_path_to_controller(@requirements[:controller].split('/')) raise RoutingError, "Illegal controller path for route default: #{@requirements[:controller]}" unless controller_class && extras.empty? [:controller] = controller_class.controller_path end @requirements.each {|k,v| [k] ||= v unless v.kind_of?(Regexp)} return nil, "Route recognition didn't find a controller class!" unless controller_class return nil, "Unused components were left: #{components.join '/'}" unless components.empty? .delete_if {|k, v| v.nil?} # Remove nil values. return controller_class, end |