~michaelforrest/use-case-mapper/trunk

« back to all changes in this revision

Viewing changes to vendor/rails/actionpack/lib/action_controller/url_rewriter.rb

  • Committer: Michael Forrest
  • Date: 2010-10-15 16:28:50 UTC
  • Revision ID: michael.forrest@canonical.com-20101015162850-tj2vchanv0kr0dun
refrozeĀ gems

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
module ActionController
 
2
  # In <b>routes.rb</b> one defines URL-to-controller mappings, but the reverse
 
3
  # is also possible: an URL can be generated from one of your routing definitions.
 
4
  # URL generation functionality is centralized in this module.
 
5
  #
 
6
  # See ActionController::Routing and ActionController::Resources for general
 
7
  # information about routing and routes.rb.
 
8
  #
 
9
  # <b>Tip:</b> If you need to generate URLs from your models or some other place,
 
10
  # then ActionController::UrlWriter is what you're looking for. Read on for
 
11
  # an introduction.
 
12
  #
 
13
  # == URL generation from parameters
 
14
  #
 
15
  # As you may know, some functions - such as ActionController::Base#url_for
 
16
  # and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set
 
17
  # of parameters. For example, you've probably had the chance to write code
 
18
  # like this in one of your views:
 
19
  #
 
20
  #   <%= link_to('Click here', :controller => 'users',
 
21
  #           :action => 'new', :message => 'Welcome!') %>
 
22
  #
 
23
  #   #=> Generates a link to: /users/new?message=Welcome%21
 
24
  #
 
25
  # link_to, and all other functions that require URL generation functionality,
 
26
  # actually use ActionController::UrlWriter under the hood. And in particular,
 
27
  # they use the ActionController::UrlWriter#url_for method. One can generate
 
28
  # the same path as the above example by using the following code:
 
29
  #
 
30
  #   include UrlWriter
 
31
  #   url_for(:controller => 'users',
 
32
  #           :action => 'new',
 
33
  #           :message => 'Welcome!',
 
34
  #           :only_path => true)
 
35
  #   # => "/users/new?message=Welcome%21"
 
36
  #
 
37
  # Notice the <tt>:only_path => true</tt> part. This is because UrlWriter has no
 
38
  # information about the website hostname that your Rails app is serving. So if you
 
39
  # want to include the hostname as well, then you must also pass the <tt>:host</tt>
 
40
  # argument:
 
41
  #
 
42
  #   include UrlWriter
 
43
  #   url_for(:controller => 'users',
 
44
  #           :action => 'new',
 
45
  #           :message => 'Welcome!',
 
46
  #           :host => 'www.example.com')        # Changed this.
 
47
  #   # => "http://www.example.com/users/new?message=Welcome%21"
 
48
  #
 
49
  # By default, all controllers and views have access to a special version of url_for,
 
50
  # that already knows what the current hostname is. So if you use url_for in your
 
51
  # controllers or your views, then you don't need to explicitly pass the <tt>:host</tt>
 
52
  # argument.
 
53
  #
 
54
  # For convenience reasons, mailers provide a shortcut for ActionController::UrlWriter#url_for.
 
55
  # So within mailers, you only have to type 'url_for' instead of 'ActionController::UrlWriter#url_for'
 
56
  # in full. However, mailers don't have hostname information, and what's why you'll still
 
57
  # have to specify the <tt>:host</tt> argument when generating URLs in mailers.
 
58
  #
 
59
  #
 
60
  # == URL generation for named routes
 
61
  #
 
62
  # UrlWriter also allows one to access methods that have been auto-generated from
 
63
  # named routes. For example, suppose that you have a 'users' resource in your
 
64
  # <b>routes.rb</b>:
 
65
  #
 
66
  #   map.resources :users
 
67
  #
 
68
  # This generates, among other things, the method <tt>users_path</tt>. By default,
 
69
  # this method is accessible from your controllers, views and mailers. If you need
 
70
  # to access this auto-generated method from other places (such as a model), then
 
71
  # you can do that in two ways.
 
72
  #
 
73
  # The first way is to include ActionController::UrlWriter in your class:
 
74
  #
 
75
  #   class User < ActiveRecord::Base
 
76
  #     include ActionController::UrlWriter         # !!!
 
77
  #
 
78
  #     def name=(value)
 
79
  #       write_attribute('name', value)
 
80
  #       write_attribute('base_uri', users_path)   # !!!
 
81
  #     end
 
82
  #   end
 
83
  #
 
84
  # The second way is to access them through ActionController::UrlWriter.
 
85
  # The autogenerated named routes methods are available as class methods:
 
86
  #
 
87
  #   class User < ActiveRecord::Base
 
88
  #     def name=(value)
 
89
  #       write_attribute('name', value)
 
90
  #       path = ActionController::UrlWriter.users_path   # !!!
 
91
  #       write_attribute('base_uri', path)               # !!!
 
92
  #     end
 
93
  #   end
 
94
  module UrlWriter
 
95
    def self.included(base) #:nodoc:
 
96
      ActionController::Routing::Routes.install_helpers(base)
 
97
      base.mattr_accessor :default_url_options
 
98
 
 
99
      # The default options for urls written by this writer. Typically a <tt>:host</tt> pair is provided.
 
100
      base.default_url_options ||= {}
 
101
    end
 
102
 
 
103
    # Generate a url based on the options provided, default_url_options and the
 
104
    # routes defined in routes.rb.  The following options are supported:
 
105
    #
 
106
    # * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
 
107
    # * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
 
108
    # * <tt>:host</tt> - Specifies the host the link should be targetted at.
 
109
    #   If <tt>:only_path</tt> is false, this option must be
 
110
    #   provided either explicitly, or via +default_url_options+.
 
111
    # * <tt>:port</tt> - Optionally specify the port to connect to.
 
112
    # * <tt>:anchor</tt> - An anchor name to be appended to the path.
 
113
    # * <tt>:skip_relative_url_root</tt> - If true, the url is not constructed using the
 
114
    #   +relative_url_root+ set in ActionController::Base.relative_url_root.
 
115
    # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
 
116
    #
 
117
    # Any other key (<tt>:controller</tt>, <tt>:action</tt>, etc.) given to
 
118
    # +url_for+ is forwarded to the Routes module.
 
119
    #
 
120
    # Examples:
 
121
    #
 
122
    #    url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :port=>'8080'    # => 'http://somehost.org:8080/tasks/testing'
 
123
    #    url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :anchor => 'ok', :only_path => true    # => '/tasks/testing#ok'
 
124
    #    url_for :controller => 'tasks', :action => 'testing', :trailing_slash=>true  # => 'http://somehost.org/tasks/testing/'
 
125
    #    url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :number => '33'  # => 'http://somehost.org/tasks/testing?number=33'
 
126
    def url_for(options)
 
127
      options = self.class.default_url_options.merge(options)
 
128
 
 
129
      url = ''
 
130
 
 
131
      unless options.delete(:only_path)
 
132
        url << (options.delete(:protocol) || 'http')
 
133
        url << '://' unless url.match("://")
 
134
 
 
135
        raise "Missing host to link to! Please provide :host parameter or set default_url_options[:host]" unless options[:host]
 
136
 
 
137
        url << options.delete(:host)
 
138
        url << ":#{options.delete(:port)}" if options.key?(:port)
 
139
      else
 
140
        # Delete the unused options to prevent their appearance in the query string.
 
141
        [:protocol, :host, :port, :skip_relative_url_root].each { |k| options.delete(k) }
 
142
      end
 
143
      trailing_slash = options.delete(:trailing_slash) if options.key?(:trailing_slash)
 
144
      url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root]
 
145
      anchor = "##{CGI.escape options.delete(:anchor).to_param.to_s}" if options[:anchor]
 
146
      generated = Routing::Routes.generate(options, {})
 
147
      url << (trailing_slash ? generated.sub(/\?|\z/) { "/" + $& } : generated)
 
148
      url << anchor if anchor
 
149
 
 
150
      url
 
151
    end
 
152
  end
 
153
 
 
154
  # Rewrites URLs for Base.redirect_to and Base.url_for in the controller.
 
155
  class UrlRewriter #:nodoc:
 
156
    RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :port, :trailing_slash, :skip_relative_url_root]
 
157
    def initialize(request, parameters)
 
158
      @request, @parameters = request, parameters
 
159
    end
 
160
 
 
161
    def rewrite(options = {})
 
162
      rewrite_url(options)
 
163
    end
 
164
 
 
165
    def to_str
 
166
      "#{@request.protocol}, #{@request.host_with_port}, #{@request.path}, #{@parameters[:controller]}, #{@parameters[:action]}, #{@request.parameters.inspect}"
 
167
    end
 
168
 
 
169
    alias_method :to_s, :to_str
 
170
 
 
171
    private
 
172
      # Given a path and options, returns a rewritten URL string
 
173
      def rewrite_url(options)
 
174
        rewritten_url = ""
 
175
 
 
176
        unless options[:only_path]
 
177
          rewritten_url << (options[:protocol] || @request.protocol)
 
178
          rewritten_url << "://" unless rewritten_url.match("://")
 
179
          rewritten_url << rewrite_authentication(options)
 
180
          rewritten_url << (options[:host] || @request.host_with_port)
 
181
          rewritten_url << ":#{options.delete(:port)}" if options.key?(:port)
 
182
        end
 
183
 
 
184
        path = rewrite_path(options)
 
185
        rewritten_url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root]
 
186
        rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
 
187
        rewritten_url << "##{CGI.escape(options[:anchor].to_param.to_s)}" if options[:anchor]
 
188
 
 
189
        rewritten_url
 
190
      end
 
191
 
 
192
      # Given a Hash of options, generates a route
 
193
      def rewrite_path(options)
 
194
        options = options.symbolize_keys
 
195
        options.update(options[:params].symbolize_keys) if options[:params]
 
196
 
 
197
        if (overwrite = options.delete(:overwrite_params))
 
198
          options.update(@parameters.symbolize_keys)
 
199
          options.update(overwrite.symbolize_keys)
 
200
        end
 
201
 
 
202
        RESERVED_OPTIONS.each { |k| options.delete(k) }
 
203
 
 
204
        # Generates the query string, too
 
205
        Routing::Routes.generate(options, @request.symbolized_path_parameters)
 
206
      end
 
207
 
 
208
      def rewrite_authentication(options)
 
209
        if options[:user] && options[:password]
 
210
          "#{CGI.escape(options.delete(:user))}:#{CGI.escape(options.delete(:password))}@"
 
211
        else
 
212
          ""
 
213
        end
 
214
      end
 
215
  end
 
216
end