actionpack 3.1.0.rc4 → 3.1.0.rc5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (40) hide show
  1. data/CHANGELOG +6 -0
  2. data/lib/abstract_controller/asset_paths.rb +1 -1
  3. data/lib/abstract_controller/layouts.rb +10 -8
  4. data/lib/action_controller/base.rb +13 -11
  5. data/lib/action_controller/caching/sweeping.rb +1 -0
  6. data/lib/action_controller/metal/redirecting.rb +1 -0
  7. data/lib/action_controller/metal/request_forgery_protection.rb +10 -9
  8. data/lib/action_controller/metal/responder.rb +5 -0
  9. data/lib/action_controller/metal/streaming.rb +6 -14
  10. data/lib/action_controller/railtie.rb +0 -1
  11. data/lib/action_controller/test_case.rb +19 -0
  12. data/lib/action_dispatch/http/upload.rb +11 -1
  13. data/lib/action_dispatch/middleware/cookies.rb +5 -0
  14. data/lib/action_dispatch/routing/mapper.rb +10 -9
  15. data/lib/action_dispatch/routing/polymorphic_routes.rb +4 -9
  16. data/lib/action_dispatch/testing/test_request.rb +1 -1
  17. data/lib/action_pack/version.rb +1 -1
  18. data/lib/action_view.rb +1 -0
  19. data/lib/action_view/asset_paths.rb +148 -0
  20. data/lib/action_view/base.rb +1 -1
  21. data/lib/action_view/helpers/asset_paths.rb +2 -78
  22. data/lib/action_view/helpers/asset_tag_helper.rb +8 -2
  23. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +11 -12
  24. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +2 -2
  25. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +4 -3
  26. data/lib/action_view/helpers/cache_helper.rb +4 -6
  27. data/lib/action_view/helpers/controller_helper.rb +2 -0
  28. data/lib/action_view/helpers/date_helper.rb +47 -47
  29. data/lib/action_view/helpers/form_helper.rb +45 -11
  30. data/lib/action_view/helpers/form_tag_helper.rb +1 -1
  31. data/lib/action_view/helpers/number_helper.rb +3 -1
  32. data/lib/action_view/helpers/text_helper.rb +5 -6
  33. data/lib/action_view/helpers/translation_helper.rb +2 -2
  34. data/lib/action_view/helpers/url_helper.rb +11 -11
  35. data/lib/action_view/renderer/partial_renderer.rb +27 -22
  36. data/lib/sprockets/assets.rake +23 -0
  37. data/lib/sprockets/compressors.rb +21 -0
  38. data/lib/sprockets/helpers/rails_helper.rb +44 -19
  39. data/lib/sprockets/railtie.rb +29 -18
  40. metadata +29 -54
data/CHANGELOG CHANGED
@@ -1,5 +1,11 @@
1
1
  *Rails 3.1.0 (unreleased)*
2
2
 
3
+ * The submit form helper does not generate an id "object_name_id" anymore. [fbrusatti]
4
+
5
+ * Make sure respond_with with :js tries to render a template in all cases [José Valim]
6
+
7
+ * json_escape will now return a SafeBuffer string if it receives SafeBuffer string [tenderlove]
8
+
3
9
  * Make sure escape_js returns SafeBuffer string if it receives SafeBuffer string [Prem Sichanugrist]
4
10
 
5
11
  * Fix escape_js to work correctly with the new SafeBuffer restriction [Paul Gallagher]
@@ -3,7 +3,7 @@ module AbstractController
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- config_accessor :asset_host, :asset_path, :assets_dir, :javascripts_dir, :stylesheets_dir, :use_sprockets
6
+ config_accessor :asset_host, :asset_path, :assets_dir, :javascripts_dir, :stylesheets_dir
7
7
  end
8
8
  end
9
9
  end
@@ -81,11 +81,12 @@ module AbstractController
81
81
  # class EmployeeController < BankController
82
82
  # layout nil
83
83
  #
84
- # The InformationController uses "bank_standard" inherited from the BankController, the VaultController overwrites
85
- # and picks the layout dynamically, and the EmployeeController doesn't want to use a layout at all.
86
- #
87
- # The TellerController uses +teller.html.erb+, and TillController inherits that layout and
88
- # uses it as well.
84
+ # In these examples:
85
+ # * The InformationController uses the "bank_standard" layout, inherited from BankController.
86
+ # * The TellerController follows convention and uses +app/views/layouts/teller.html.erb+.
87
+ # * The TillController inherits the layout from TellerController and uses +teller.html.erb+ as well.
88
+ # * The VaultController chooses a layout dynamically by calling the <tt>access_level_layout</tt> method.
89
+ # * The EmployeeController does not use a layout at all.
89
90
  #
90
91
  # == Types of layouts
91
92
  #
@@ -138,8 +139,8 @@ module AbstractController
138
139
  #
139
140
  # end
140
141
  #
141
- # This will assign "weblog_standard" as the WeblogController's layout except for the +rss+ action, which will not wrap a layout
142
- # around the rendered view.
142
+ # This will assign "weblog_standard" as the WeblogController's layout for all actions except for the +rss+ action, which will
143
+ # be rendered directly, without wrapping a layout around the rendered view.
143
144
  #
144
145
  # Both the <tt>:only</tt> and <tt>:except</tt> condition can accept an arbitrary number of method references, so
145
146
  # #<tt>:except => [ :rss, :text_only ]</tt> is valid, as is <tt>:except => :rss</tt>.
@@ -158,7 +159,7 @@ module AbstractController
158
159
  # end
159
160
  # end
160
161
  #
161
- # This will render the help action with the "help" layout instead of the controller-wide "weblog_standard" layout.
162
+ # This will override the controller-wide "weblog_standard" layout, and will render the help action with the "help" layout instead.
162
163
  module Layouts
163
164
  extend ActiveSupport::Concern
164
165
 
@@ -166,6 +167,7 @@ module AbstractController
166
167
 
167
168
  included do
168
169
  class_attribute :_layout_conditions
170
+ remove_possible_method :_layout_conditions
169
171
  delegate :_layout_conditions, :to => :'self.class'
170
172
  self._layout_conditions = {}
171
173
  _write_layout_method
@@ -31,7 +31,7 @@ module ActionController
31
31
  # "302 Moved" HTTP response that takes the user to the index action.
32
32
  #
33
33
  # These two methods represent the two basic action archetypes used in Action Controllers. Get-and-show and do-and-redirect.
34
- # Most actions are variations of these themes.
34
+ # Most actions are variations on these themes.
35
35
  #
36
36
  # == Requests
37
37
  #
@@ -116,8 +116,8 @@ module ActionController
116
116
  #
117
117
  # Title: <%= @post.title %>
118
118
  #
119
- # You don't have to rely on the automated rendering. Especially actions that could result in the rendering of different templates will use
120
- # the manual rendering methods:
119
+ # You don't have to rely on the automated rendering. For example, actions that could result in the rendering of different templates
120
+ # will use the manual rendering methods:
121
121
  #
122
122
  # def search
123
123
  # @results = Search.find(params[:query])
@@ -132,9 +132,9 @@ module ActionController
132
132
  #
133
133
  # == Redirects
134
134
  #
135
- # Redirects are used to move from one action to another. For example, after a <tt>create</tt> action, which stores a blog entry to a database,
136
- # we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're going to reuse (and redirect to)
137
- # a <tt>show</tt> action that we'll assume has already been created. The code might look like this:
135
+ # Redirects are used to move from one action to another. For example, after a <tt>create</tt> action, which stores a blog entry to the
136
+ # database, we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're
137
+ # going to reuse (and redirect to) a <tt>show</tt> action that we'll assume has already been created. The code might look like this:
138
138
  #
139
139
  # def create
140
140
  # @entry = Entry.new(params[:entry])
@@ -146,7 +146,9 @@ module ActionController
146
146
  # end
147
147
  # end
148
148
  #
149
- # In this case, after saving our new entry to the database, the user is redirected to the <tt>show</tt> method which is then executed.
149
+ # In this case, after saving our new entry to the database, the user is redirected to the <tt>show</tt> method, which is then executed.
150
+ # Note that this is an external HTTP-level redirection which will cause the browser to make a second request (a GET to the show action),
151
+ # and not some internal re-routing which calls both "create" and then "show" within one request.
150
152
  #
151
153
  # Learn more about <tt>redirect_to</tt> and what options you have in ActionController::Redirecting.
152
154
  #
@@ -210,16 +212,16 @@ module ActionController
210
212
  # also include them at the bottom.
211
213
  AbstractController::Callbacks,
212
214
 
215
+ # Append rescue at the bottom to wrap as much as possible.
216
+ Rescue,
217
+
213
218
  # Add instrumentations hooks at the bottom, to ensure they instrument
214
219
  # all the methods properly.
215
220
  Instrumentation,
216
221
 
217
222
  # Params wrapper should come before instrumentation so they are
218
223
  # properly showed in logs
219
- ParamsWrapper,
220
-
221
- # The same with rescue, append it at the end to wrap as much as possible.
222
- Rescue
224
+ ParamsWrapper
223
225
  ]
224
226
 
225
227
  MODULES.each do |mod|
@@ -61,6 +61,7 @@ module ActionController #:nodoc:
61
61
  end
62
62
 
63
63
  def after(controller)
64
+ self.controller = controller
64
65
  callback(:after) if controller.perform_caching
65
66
  # Clean up, so that the controller can be collected after this request
66
67
  self.controller = nil
@@ -43,6 +43,7 @@ module ActionController
43
43
  #
44
44
  # The status code can either be a standard {HTTP Status code}[http://www.iana.org/assignments/http-status-codes] as an
45
45
  # integer, or a symbol representing the downcased, underscored and symbolized description.
46
+ # Note that the status code must be a 3xx HTTP code, or redirection will not occur.
46
47
  #
47
48
  # It is also possible to assign a flash message as part of the redirection. There are two special accessors for commonly used the flash names
48
49
  # +alert+ and +notice+ as well as a general purpose +flash+ bucket.
@@ -7,17 +7,16 @@ module ActionController #:nodoc:
7
7
  # Controller actions are protected from Cross-Site Request Forgery (CSRF) attacks
8
8
  # by including a token in the rendered html for your application. This token is
9
9
  # stored as a random string in the session, to which an attacker does not have
10
- # access. When a request reaches your application, \Rails then verifies the received
11
- # token with the token in the session. Only HTML and javascript requests are checked,
10
+ # access. When a request reaches your application, \Rails verifies the received
11
+ # token with the token in the session. Only HTML and JavaScript requests are checked,
12
12
  # so this will not protect your XML API (presumably you'll have a different
13
13
  # authentication scheme there anyway). Also, GET requests are not protected as these
14
14
  # should be idempotent.
15
15
  #
16
16
  # CSRF protection is turned on with the <tt>protect_from_forgery</tt> method,
17
- # which will check the token and raise an ActionController::InvalidAuthenticityToken
18
- # if it doesn't match what was expected. A call to this method is generated for new
19
- # \Rails applications by default. You can customize the error message by editing
20
- # public/422.html.
17
+ # which checks the token and resets the session if it doesn't match what was expected.
18
+ # A call to this method is generated for new \Rails applications by default.
19
+ # You can customize the error message by editing public/422.html.
21
20
  #
22
21
  # The token parameter is named <tt>authenticity_token</tt> by default. The name and
23
22
  # value of this token must be added to every layout that renders forms by including
@@ -63,7 +62,7 @@ module ActionController #:nodoc:
63
62
  #
64
63
  # Valid Options:
65
64
  #
66
- # * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified.
65
+ # * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified.
67
66
  def protect_from_forgery(options = {})
68
67
  self.request_forgery_protection_token ||= :authenticity_token
69
68
  prepend_before_filter :verify_authenticity_token, options
@@ -71,7 +70,7 @@ module ActionController #:nodoc:
71
70
  end
72
71
 
73
72
  protected
74
- # The actual before_filter that is used. Modify this to change how you handle unverified requests.
73
+ # The actual before_filter that is used. Modify this to change how you handle unverified requests.
75
74
  def verify_authenticity_token
76
75
  unless verified_request?
77
76
  logger.debug "WARNING: Can't verify CSRF token authenticity" if logger
@@ -79,11 +78,13 @@ module ActionController #:nodoc:
79
78
  end
80
79
  end
81
80
 
81
+ # This is the method that defines the application behavior when a request is found to be unverified.
82
+ # By default, \Rails resets the session when it finds an unverified request.
82
83
  def handle_unverified_request
83
84
  reset_session
84
85
  end
85
86
 
86
- # Returns true or false if a request is verified. Checks:
87
+ # Returns true or false if a request is verified. Checks:
87
88
  #
88
89
  # * is it a GET request? Gets should be safe and idempotent
89
90
  # * Does the form_authenticity_token match the given token value from the params?
@@ -162,6 +162,11 @@ module ActionController #:nodoc:
162
162
  navigation_behavior(e)
163
163
  end
164
164
 
165
+ # to_js simply tries to render a template. If no template is found, raises the error.
166
+ def to_js
167
+ default_render
168
+ end
169
+
165
170
  # All other formats follow the procedure below. First we try to render a
166
171
  # template, if the template is not available, we verify if the resource
167
172
  # responds to :to_format and display it.
@@ -24,20 +24,8 @@ module ActionController #:nodoc:
24
24
  #
25
25
  # == Examples
26
26
  #
27
- # Streaming can be added to a controller easily, all you need to do is
28
- # call +stream+ in the controller class:
29
- #
30
- # class PostsController
31
- # stream
32
- # end
33
- #
34
- # The +stream+ method accepts the same options as +before_filter+ and friends:
35
- #
36
- # class PostsController
37
- # stream :only => :index
38
- # end
39
- #
40
- # You can also selectively turn on streaming for specific actions:
27
+ # Streaming can be added to a given template easily, all you need to do is
28
+ # to pass the :stream option.
41
29
  #
42
30
  # class PostsController
43
31
  # def index
@@ -72,6 +60,9 @@ module ActionController #:nodoc:
72
60
  # render :stream => true
73
61
  # end
74
62
  #
63
+ # Notice that :stream only works with templates. Rendering :json
64
+ # or :xml with :stream won't work.
65
+ #
75
66
  # == Communication between layout and template
76
67
  #
77
68
  # When streaming, rendering happens top-down instead of inside-out.
@@ -215,6 +206,7 @@ module ActionController #:nodoc:
215
206
  # Render streaming templates. It accepts :only, :except, :if and :unless as options
216
207
  # to specify when to stream, as in ActionController filters.
217
208
  def stream(options={})
209
+ ActiveSupport::Deprecation.warn "stream class method is deprecated. Please give the :stream option to render instead"
218
210
  if defined?(Fiber)
219
211
  before_filter :_stream_filter, options
220
212
  else
@@ -4,7 +4,6 @@ require "action_dispatch/railtie"
4
4
  require "action_view/railtie"
5
5
  require "abstract_controller/railties/routes_helpers"
6
6
  require "action_controller/railties/paths"
7
- require "sprockets/railtie"
8
7
 
9
8
  module ActionController
10
9
  class Railtie < Rails::Railtie
@@ -395,7 +395,26 @@ module ActionController
395
395
  end
396
396
  alias xhr :xml_http_request
397
397
 
398
+ def paramify_values(hash_or_array_or_value)
399
+ case hash_or_array_or_value
400
+ when Hash
401
+ hash_or_array_or_value.each do |key, value|
402
+ hash_or_array_or_value[key] = paramify_values(value)
403
+ end
404
+ when Array
405
+ hash_or_array_or_value.map {|i| paramify_values(i)}
406
+ when Rack::Test::UploadedFile
407
+ hash_or_array_or_value
408
+ else
409
+ hash_or_array_or_value.to_param
410
+ end
411
+ end
412
+
398
413
  def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
414
+ # Ensure that numbers and symbols passed as params are converted to
415
+ # proper params, as is the case when engaging rack.
416
+ paramify_values(parameters)
417
+
399
418
  # Sanity check for required instance variables so we can give an
400
419
  # understandable error message.
401
420
  %w(@routes @controller @request @response).each do |iv_name|
@@ -4,7 +4,7 @@ module ActionDispatch
4
4
  attr_accessor :original_filename, :content_type, :tempfile, :headers
5
5
 
6
6
  def initialize(hash)
7
- @original_filename = hash[:filename]
7
+ @original_filename = encode_filename(hash[:filename])
8
8
  @content_type = hash[:type]
9
9
  @headers = hash[:head]
10
10
  @tempfile = hash[:tempfile]
@@ -30,6 +30,16 @@ module ActionDispatch
30
30
  def size
31
31
  @tempfile.size
32
32
  end
33
+
34
+ private
35
+ def encode_filename(filename)
36
+ # Encode the filename in the utf8 encoding, unless it is nil or we're in 1.8
37
+ if "ruby".encoding_aware? && filename
38
+ filename.force_encoding("UTF-8").encode!
39
+ else
40
+ filename
41
+ end
42
+ end
33
43
  end
34
44
 
35
45
  module Upload
@@ -128,6 +128,11 @@ module ActionDispatch
128
128
  @cookies[name.to_s]
129
129
  end
130
130
 
131
+ def key?(name)
132
+ @cookies.key?(name.to_s)
133
+ end
134
+ alias :has_key? :key?
135
+
131
136
  def update(other_hash)
132
137
  @cookies.update other_hash
133
138
  self
@@ -116,6 +116,8 @@ module ActionDispatch
116
116
  path
117
117
  elsif path.include?(":format") || path.end_with?('/')
118
118
  path
119
+ elsif @options[:format] == true
120
+ "#{path}.:format"
119
121
  else
120
122
  "#{path}(.:format)"
121
123
  end
@@ -187,13 +189,12 @@ module ActionDispatch
187
189
  end
188
190
 
189
191
  def blocks
190
- block = @scope[:blocks] || []
191
-
192
- if @options[:constraints].present? && !@options[:constraints].is_a?(Hash)
193
- block << @options[:constraints]
192
+ constraints = @options[:constraints]
193
+ if constraints.present? && !constraints.is_a?(Hash)
194
+ [constraints]
195
+ else
196
+ @scope[:blocks] || []
194
197
  end
195
-
196
- block
197
198
  end
198
199
 
199
200
  def constraints
@@ -1102,9 +1103,9 @@ module ActionDispatch
1102
1103
  #
1103
1104
  # The +comments+ resource here will have the following routes generated for it:
1104
1105
  #
1105
- # post_comments GET /sekret/posts/:post_id/comments(.:format)
1106
- # post_comments POST /sekret/posts/:post_id/comments(.:format)
1107
- # new_post_comment GET /sekret/posts/:post_id/comments/new(.:format)
1106
+ # post_comments GET /posts/:post_id/comments(.:format)
1107
+ # post_comments POST /posts/:post_id/comments(.:format)
1108
+ # new_post_comment GET /posts/:post_id/comments/new(.:format)
1108
1109
  # edit_comment GET /sekret/comments/:id/edit(.:format)
1109
1110
  # comment GET /sekret/comments/:id(.:format)
1110
1111
  # comment PUT /sekret/comments/:id(.:format)
@@ -124,14 +124,7 @@ module ActionDispatch
124
124
  args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options
125
125
  end
126
126
 
127
- if proxy
128
- proxy.send(named_route, *args)
129
- else
130
- # we need to use url_for, because polymorphic_url can be used in context of other than
131
- # current routes (e.g. engine's routes). As named routes from engine are not included
132
- # calling engine's named route directly would fail.
133
- url_for _routes.url_helpers.__send__("hash_for_#{named_route}", *args)
134
- end
127
+ (proxy || self).send(named_route, *args)
135
128
  end
136
129
 
137
130
  # Returns the path component of a URL for the given record. It uses
@@ -182,10 +175,12 @@ module ActionDispatch
182
175
 
183
176
  if record.is_a?(Symbol) || record.is_a?(String)
184
177
  route << record
185
- else
178
+ elsif record
186
179
  route << ActiveModel::Naming.route_key(record)
187
180
  route = [route.join("_").singularize] if inflection == :singular
188
181
  route << "index" if ActiveModel::Naming.uncountable?(record) && inflection == :plural
182
+ else
183
+ raise ArgumentError, "Nil location provided. Can't build URI."
189
184
  end
190
185
 
191
186
  route << routing_type(options)
@@ -83,7 +83,7 @@ module ActionDispatch
83
83
  end
84
84
 
85
85
  def escape_cookie(name, value)
86
- "#{Rack::Utils.escape(name)}=#{Rack::Utils.escape(value)}"
86
+ "#{Rack::Utils.escape(name)}=#{Rack::Utils.escape(value.to_s)}"
87
87
  end
88
88
 
89
89
  def delete_nil_values!
@@ -3,7 +3,7 @@ module ActionPack
3
3
  MAJOR = 3
4
4
  MINOR = 1
5
5
  TINY = 0
6
- PRE = "rc4"
6
+ PRE = "rc5"
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
9
9
  end
@@ -30,6 +30,7 @@ module ActionView
30
30
  extend ActiveSupport::Autoload
31
31
 
32
32
  eager_autoload do
33
+ autoload :AssetPaths
33
34
  autoload :Base
34
35
  autoload :Context
35
36
  autoload :Helpers
@@ -0,0 +1,148 @@
1
+ require 'zlib'
2
+ require 'active_support/core_ext/file'
3
+
4
+ module ActionView
5
+
6
+ class AssetPaths #:nodoc:
7
+ attr_reader :config, :controller
8
+
9
+ def initialize(config, controller = nil)
10
+ @config = config
11
+ @controller = controller
12
+ end
13
+
14
+ # Add the extension +ext+ if not present. Return full or scheme-relative URLs otherwise untouched.
15
+ # Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL
16
+ # roots. Rewrite the asset path for cache-busting asset ids. Include
17
+ # asset host, if configured, with the correct request protocol.
18
+ #
19
+ # When include_host is true and the asset host does not specify the protocol
20
+ # the protocol parameter specifies how the protocol will be added.
21
+ # When :relative (default), the protocol will be determined by the client using current protocol
22
+ # When :request, the protocol will be the request protocol
23
+ # Otherwise, the protocol is used (E.g. :http, :https, etc)
24
+ def compute_public_path(source, dir, ext = nil, include_host = true, protocol = nil)
25
+ source = source.to_s
26
+ return source if is_uri?(source)
27
+
28
+ source = rewrite_extension(source, dir, ext) if ext
29
+ source = rewrite_asset_path(source, dir)
30
+ source = rewrite_relative_url_root(source, relative_url_root) if has_request?
31
+ source = rewrite_host_and_protocol(source, protocol) if include_host
32
+ source
33
+ end
34
+
35
+ # Return the filesystem path for the source
36
+ def compute_source_path(source, dir, ext)
37
+ source = rewrite_extension(source, dir, ext) if ext
38
+ File.join(config.assets_dir, dir, source)
39
+ end
40
+
41
+ def is_uri?(path)
42
+ path =~ %r{^[-a-z]+://|^cid:|^//}
43
+ end
44
+
45
+ private
46
+
47
+ def rewrite_extension(source, dir, ext)
48
+ raise NotImplementedError
49
+ end
50
+
51
+ def rewrite_asset_path(source, path = nil)
52
+ raise NotImplementedError
53
+ end
54
+
55
+ def rewrite_relative_url_root(source, relative_url_root)
56
+ relative_url_root && !source.starts_with?("#{relative_url_root}/") ? "#{relative_url_root}#{source}" : source
57
+ end
58
+
59
+ def has_request?
60
+ controller.respond_to?(:request)
61
+ end
62
+
63
+ def rewrite_host_and_protocol(source, protocol = nil)
64
+ host = compute_asset_host(source)
65
+ if host && !is_uri?(host)
66
+ if (protocol || default_protocol) == :request && !has_request?
67
+ host = nil
68
+ else
69
+ host = "#{compute_protocol(protocol)}#{host}"
70
+ end
71
+ end
72
+ host.nil? ? source : "#{host}#{source}"
73
+ end
74
+
75
+ def compute_protocol(protocol)
76
+ protocol ||= default_protocol
77
+ case protocol
78
+ when :relative
79
+ "//"
80
+ when :request
81
+ unless @controller
82
+ invalid_asset_host!("The protocol requested was :request. Consider using :relative instead.")
83
+ end
84
+ @controller.request.protocol
85
+ else
86
+ "#{protocol}://"
87
+ end
88
+ end
89
+
90
+ def default_protocol
91
+ protocol = @config.action_controller.default_asset_host_protocol if @config.action_controller.present?
92
+ protocol ||= @config.default_asset_host_protocol
93
+ protocol || (has_request? ? :request : :relative)
94
+ end
95
+
96
+ def invalid_asset_host!(help_message)
97
+ raise ActionController::RoutingError, "This asset host cannot be computed without a request in scope. #{help_message}"
98
+ end
99
+
100
+ # Pick an asset host for this source. Returns +nil+ if no host is set,
101
+ # the host if no wildcard is set, the host interpolated with the
102
+ # numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4),
103
+ # or the value returned from invoking the proc if it's a proc or the value from
104
+ # invoking call if it's an object responding to call.
105
+ def compute_asset_host(source)
106
+ if host = asset_host_config
107
+ if host.respond_to?(:call)
108
+ args = [source]
109
+ arity = arity_of(host)
110
+ if arity > 1 && !has_request?
111
+ invalid_asset_host!("Remove the second argument to your asset_host Proc if you do not need the request.")
112
+ end
113
+ args << current_request if (arity > 1 || arity < 0) && has_request?
114
+ host.call(*args)
115
+ else
116
+ (host =~ /%d/) ? host % (Zlib.crc32(source) % 4) : host
117
+ end
118
+ end
119
+ end
120
+
121
+ def relative_url_root
122
+ config = controller.config if controller.respond_to?(:config)
123
+ config ||= config.action_controller if config.action_controller.present?
124
+ config ||= config
125
+ config.relative_url_root
126
+ end
127
+
128
+ def asset_host_config
129
+ if config.action_controller.present?
130
+ config.action_controller.asset_host
131
+ else
132
+ config.asset_host
133
+ end
134
+ end
135
+
136
+ # Returns the current request if one exists.
137
+ def current_request
138
+ controller.request if has_request?
139
+ end
140
+
141
+ # Returns the arity of a callable
142
+ def arity_of(callable)
143
+ callable.respond_to?(:arity) ? callable.arity : callable.method(:call).arity
144
+ end
145
+
146
+ end
147
+
148
+ end