actionpack 1.6.0 → 1.7.0

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.

data/CHANGELOG CHANGED
@@ -1,3 +1,42 @@
1
+ *1.7.0* (27th March, 2005)
2
+
3
+ * Added ActionController::Base.page_cache_extension for setting the page cache file extension (the default is .html) #903 [Andreas]
4
+
5
+ * Fixed "bad environment variable value" exception caused by Safari, Apache, and Ajax calls #918
6
+
7
+ * Fixed that pagination_helper would ignore :params #947 [Sebastian Kanthak]
8
+
9
+ * Added :owerwrite_params back to url_for and friends -- it was AWL since the introduction of Routes #921 [raphinou]
10
+
11
+ * Added :position option to link_to_remote/form_remote_tag that can be either :before, :top, :bottom, or :after and specifies where the return from the method should be inserted #952 [Matthew McCray/Sam Stephenson]
12
+
13
+ * Added Effect.Highlight to prototype.js to do Yellow Fade Technique (of 37signals' fame) on any container #952 [Sam Stephenson/courtenay]
14
+
15
+ * Added include_seconds option as the third parameter to distance_of_time_in_words which will render "less than a minute" in higher resolution ("less than 10 seconds" etc) #944 [thomas@fesch.at]
16
+
17
+ * Added fourth option to process in test cases to specify the content of the flash #949 [Jamis Buck]
18
+
19
+ * Added Verifications that allows you to specify preconditions to actions in form of statements like <tt>verify :only => :update_post, :params => "admin_privileges", :redirect_to => { :action => "settings" }</tt>, which ensure that the update_post action is only called if admin_privileges is available as a parameter -- otherwise the user is redirected to settings. #897 [Jamis Buck]
20
+
21
+ * Fixed Form.Serialize for the JavascriptHelper to also seriliaze password fields #934 [dweitzman@gmail.com]
22
+
23
+ * Added JavascriptHelper#escape_javascript as a public method (was private) and made it escape both single and double quotes and new lines #940 [mortonda@dgrmm.net]
24
+
25
+ * Added trailing_slash option to url_for, so you can generate urls ending in a slash. Note that is currently not recommended unless you need it for special reasons since it breaks caching #937 [stian@grytoyr.net]
26
+
27
+ * Added expire_matched_fragments(regular_expression) to clear out a lot of fragment caches at once #927 [technoweenie@gmail.com]
28
+
29
+ * Fixed the problems with : and ? in file names for fragment caches on Windows #927 [technoweenie@gmail.com]
30
+
31
+ * Added TextHelper#human_size for formatting file sizes, like human_size(1234567) => 1.2 MB #943 [thomas@fesch.at]
32
+
33
+ * Fixed link_to :confirm #936 [Nicholas Seckar]
34
+
35
+ * Improved error reporting especially around never shallowing exceptions. Debugging helpers should be much easier now #980 [Nicholas Seckar]
36
+
37
+ * Fixed Toggle.display in prototype.js #902 [Lucas Carlson]
38
+
39
+
1
40
  *1.6.0* (22th March, 2005)
2
41
 
3
42
  * Added a JavascriptHelper and accompanying prototype.js library that opens the world of Ajax to Action Pack with a large array of options for dynamically interacting with an application without reloading the page #884 [Sam Stephenson/David]
@@ -46,6 +46,7 @@ require 'action_controller/cookies'
46
46
  require 'action_controller/cgi_process'
47
47
  require 'action_controller/caching'
48
48
  require 'action_controller/components'
49
+ require 'action_controller/verification'
49
50
 
50
51
  require 'action_view'
51
52
  ActionController::Base.template_class = ActionView::Base
@@ -64,4 +65,5 @@ ActionController::Base.class_eval do
64
65
  include ActionController::Session
65
66
  include ActionController::Caching
66
67
  include ActionController::Components
68
+ include ActionController::Verification
67
69
  end
@@ -105,6 +105,13 @@ module ActionController #:nodoc:
105
105
  # Any object can be placed in the session (as long as it can be Marshalled). But remember that 1000 active sessions each storing a
106
106
  # 50kb object could lead to a 50MB memory overhead. In other words, think carefully about size and caching before resorting to the use
107
107
  # of the session.
108
+ #
109
+ # If you store a model in the session, you must also include a line like:
110
+ #
111
+ # model :person
112
+ #
113
+ # For that particular controller. In Rails, you can also just add it in your app/controller/application.rb file (so the model is available
114
+ # for all controllers). This lets Action Pack know to have the model definition loaded before retrieving the object from the session.
108
115
  #
109
116
  # For removing objects from the session, you can either assign a single key to nil, like <tt>@session[:person] = nil</tt>, or you can
110
117
  # remove the entire session with reset_session.
@@ -336,6 +343,8 @@ module ActionController #:nodoc:
336
343
  # <tt>url_for :controller => 'posts', :action => 'show', :id => 10, :anchor => 'comments'</tt>
337
344
  # will produce "/posts/show/10#comments".
338
345
  # * <tt>:only_path</tt> -- if true, returns the absolute URL (omitting the protocol, host name, and port)
346
+ # * <tt>:trailing_slash</tt> -- if true, adds a trailing slash, as in "/archive/2005/". Note that this
347
+ # is currently not recommended since it breaks caching.
339
348
  # * <tt>:host</tt> -- overrides the default (current) host if provided
340
349
  # * <tt>:protocol</tt> -- overrides the default (current) protocol if provided
341
350
  #
@@ -51,6 +51,11 @@ module ActionController #:nodoc:
51
51
  #
52
52
  # The cache directory should be the document root for the web server and is set using Base.page_cache_directory = "/document/root".
53
53
  # For Rails, this directory has already been set to RAILS_ROOT + "/public".
54
+ #
55
+ # == Setting the cache extension
56
+ #
57
+ # By default, the cache extension is .html, which makes it easy for the cached files to be picked up by the web server. If you want
58
+ # something else, like .php or .shtml, just set Base.page_cache_extension.
54
59
  module Pages
55
60
  def self.append_features(base) #:nodoc:
56
61
  super
@@ -58,6 +63,9 @@ module ActionController #:nodoc:
58
63
  base.class_eval do
59
64
  @@page_cache_directory = defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/public" : ""
60
65
  cattr_accessor :page_cache_directory
66
+
67
+ @@page_cache_extension = '.html'
68
+ cattr_accessor :page_cache_extension
61
69
  end
62
70
  end
63
71
 
@@ -91,7 +99,7 @@ module ActionController #:nodoc:
91
99
  private
92
100
  def page_cache_file(path)
93
101
  name = ((path.empty? || path == "/") ? "/index" : path)
94
- name << '.html' unless (name.split('/').last || name).include? '.'
102
+ name << @@page_cache_extension unless (name.split('/').last || name).include? '.'
95
103
  return name
96
104
  end
97
105
 
@@ -285,7 +293,12 @@ module ActionController #:nodoc:
285
293
  fragment_cache_store.delete(name, options)
286
294
  logger.info "Expired fragment: #{name}" unless logger.nil?
287
295
  end
288
-
296
+
297
+ def expire_matched_fragments(re=Regexp.new('/*/'), options = {})
298
+ fragment_cache_store.delete_matched(re, { :root_path => url_for.split('://').last.split('/').first })
299
+ logger.info "Expired all fragments matching: #{re} " unless logger.nil?
300
+ end
301
+
289
302
  class MemoryStore #:nodoc:
290
303
  def initialize
291
304
  @data, @mutex = { }, Mutex.new
@@ -302,6 +315,10 @@ module ActionController #:nodoc:
302
315
  def delete(name, options = {}) #:nodoc:
303
316
  @mutex.synchronize { @data.delete(name) }
304
317
  end
318
+
319
+ def delete_matched(re, options) #:nodoc:
320
+ @mutex.synchronize { @data.delete_if {|k,v| k.index(options[:root_path]) == 0 and k =~ re} }
321
+ end
305
322
  end
306
323
 
307
324
  class DRbStore < MemoryStore #:nodoc:
@@ -335,15 +352,37 @@ module ActionController #:nodoc:
335
352
  def delete(name, options) #:nodoc:
336
353
  File.delete(real_file_path(name)) if File.exist?(real_file_path(name))
337
354
  end
355
+
356
+ def delete_matched(re, options) #:nodoc:
357
+ rootPath = real_file_path(options[:root_path])
358
+ search_dir(@cache_path).each do |f|
359
+ File.delete(f) if f.index(rootPath) == 0 and f =~ re and File.exist?(f)
360
+ end
361
+ end
338
362
 
339
363
  private
340
364
  def real_file_path(name)
341
- "#{@cache_path}/#{name}"
365
+ '%s/%s' % [@cache_path, name.gsub('?', '.').gsub(':', '.')]
342
366
  end
343
367
 
344
368
  def ensure_cache_path(path)
345
369
  FileUtils.makedirs(path) unless File.exists?(path)
346
370
  end
371
+
372
+ def search_dir(dir)
373
+ require 'pathname'
374
+ files = []
375
+ dir = Dir.new(dir)
376
+ dir.each do |d|
377
+ unless d == '.' or d == '..'
378
+ d = File.join(dir.path, d)
379
+ p = Pathname.new(d)
380
+ files << p.to_s if p.file?
381
+ files += search_dir(d) if p.directory?
382
+ end
383
+ end
384
+ files
385
+ end
347
386
  end
348
387
  end
349
388
 
@@ -37,7 +37,7 @@ class CGI #:nodoc:
37
37
  when 'POST'
38
38
  stdinput.binmode if stdinput.respond_to?(:binmode)
39
39
  content = stdinput.read(Integer(env_table['CONTENT_LENGTH'])) || ''
40
- env_table['RAW_POST_DATA'] = content.freeze
40
+ env_table['RAW_POST_DATA'] = content.split("&_").first.freeze # &_ is a fix for Safari Ajax postings that always append \000
41
41
  else
42
42
  read_from_cmdline
43
43
  end
@@ -71,8 +71,8 @@ module ActionController #:nodoc:
71
71
  dependencies.flatten.each do |dependency|
72
72
  begin
73
73
  require_dependency(dependency.to_s)
74
- rescue LoadError
75
- raise LoadError, "Missing #{layer} #{dependency}.rb"
74
+ rescue LoadError => e
75
+ raise LoadError.new("Missing #{layer} #{dependency}.rb").copy_blame!(e)
76
76
  rescue Object => exception
77
77
  exception.blame_file! "=> #{layer} #{dependency}.rb"
78
78
  raise
@@ -82,13 +82,7 @@ module ActionController #:nodoc:
82
82
 
83
83
  def inherited(child)
84
84
  inherited_without_model(child)
85
- return if child.controller_name == "application" # otherwise the ApplicationController in Rails will include itself
86
- begin
87
- child.model(child.controller_name.singularize)
88
- rescue NameError, LoadError
89
- # No neither singular or plural model available for this controller
90
- end
91
85
  end
92
86
  end
93
87
  end
94
- end
88
+ end
@@ -52,12 +52,13 @@ module ActionController #:nodoc:
52
52
  when String, Symbol
53
53
  file_name = arg.to_s.underscore + '_helper'
54
54
  class_name = file_name.camelize
55
-
55
+
56
56
  begin
57
57
  require_dependency(file_name)
58
58
  rescue LoadError => load_error
59
59
  requiree = / -- (.*?)(\.rb)?$/.match(load_error).to_a[1]
60
- raise LoadError, requiree == file_name ? "Missing helper file helpers/#{file_name}.rb" : "Can't load file: #{requiree}"
60
+ msg = (requiree == file_name) ? "Missing helper file helpers/#{file_name}.rb" : "Can't load file: #{requiree}"
61
+ raise LoadError.new(msg).copy_blame!(load_error)
61
62
  end
62
63
 
63
64
  add_template_helper(class_name.constantize)
@@ -90,10 +91,9 @@ module ActionController #:nodoc:
90
91
  private
91
92
  def inherited(child)
92
93
  inherited_without_helper(child)
93
- begin
94
- child.helper(child.controller_path)
95
- rescue ArgumentError, LoadError
96
- # No default helper available for this controller
94
+ begin child.helper(child.controller_path)
95
+ rescue MissingSourceFile => e
96
+ raise unless e.is_missing?("helpers/#{child.controller_path}_helper")
97
97
  end
98
98
  end
99
99
  end
@@ -77,8 +77,8 @@ module ActionController #:nodoc:
77
77
  def perform_action_with_rescue #:nodoc:
78
78
  begin
79
79
  perform_action_without_rescue
80
- rescue => exception
81
- if defined?(Breakpoint) and @params["BP-RETRY"] then
80
+ rescue Object => exception
81
+ if defined?(Breakpoint) && @params["BP-RETRY"]
82
82
  msg = exception.backtrace.first
83
83
  if md = /^(.+?):(\d+)(?::in `(.+)')?$/.match(msg) then
84
84
  origin_file, origin_line = md[1], md[2].to_i
@@ -312,7 +312,7 @@ module ActionController
312
312
  route_file = defined?(RAILS_ROOT) ? File.join(RAILS_ROOT, 'config', 'routes') : nil
313
313
  require_dependency(route_file) if route_file
314
314
  rescue LoadError, ScriptError => e
315
- raise RoutingError, "Cannot load config/routes.rb:\n #{e.message}"
315
+ raise RoutingError.new("Cannot load config/routes.rb:\n #{e.message}").copy_blame!(e)
316
316
  ensure # Ensure that there is at least one route:
317
317
  connect(':controller/:action/:id', :action => 'index', :id => nil) if @routes.empty?
318
318
  end
@@ -1,22 +1,25 @@
1
- <% if @exception.blamed_files && !@exception.blamed_files.empty? %>
2
- <a href="#" onclick="document.getElementById('blame_trace').style.display='block'; return false;">Show blamed files</a>
3
- <pre id="blame_trace" style="display:none"><code><%=h @exception.describe_blame %></code></pre>
1
+ <% unless @exception.blamed_files.blank? %>
2
+ <% if (hide = @exception.blamed_files.length > 8) %>
3
+ <a href="#" onclick="document.getElementById('blame_trace').style.display='block'; return false;">Show blamed files</a>
4
+ <% end %>
5
+ <pre id="blame_trace" <%='style="display:none"' if hide %>><code><%=h @exception.describe_blame %></code></pre>
4
6
  <% end %>
5
7
 
6
8
  <% if defined?(Breakpoint) %>
7
9
  <br /><br />
8
- <% begin %><%= form_tag({:params => {}, :only_path => true}, "method" => @request.method) %>
9
- <input type="hidden" name="BP-RETRY" value="1" />
10
+ <% begin %>
11
+ <%= form_tag(@request.request_uri, "method" => @request.method) %>
12
+ <input type="hidden" name="BP-RETRY" value="1" />
10
13
 
11
- <% for key, values in @params %>
12
- <% next if key == "BP-RETRY" %>
13
- <% for value in Array(values) %>
14
- <input type="hidden" name="<%= key %>" value="<%= value %>" />
14
+ <% for key, values in @params %>
15
+ <% next if key == "BP-RETRY" %>
16
+ <% for value in Array(values) %>
17
+ <input type="hidden" name="<%= key %>" value="<%= value %>" />
18
+ <% end %>
15
19
  <% end %>
16
- <% end %>
17
20
 
18
- <input type="submit" value="Retry with Breakpoint" />
19
- </form>
21
+ <input type="submit" value="Retry with Breakpoint" />
22
+ </form>
20
23
  <% rescue Exception => e %>
21
24
  <%=h "Couldn't render breakpoint link due to #{e.class} #{e.message}" %>
22
25
  <% end %>
@@ -251,12 +251,13 @@ module Test
251
251
  class TestCase #:nodoc:
252
252
  private
253
253
  # execute the request and set/volley the response
254
- def process(action, parameters = nil, session = nil)
254
+ def process(action, parameters = nil, session = nil, flash = nil)
255
255
  @request.env['REQUEST_METHOD'] ||= "GET"
256
256
  @request.action = action.to_s
257
257
  @request.path_parameters = { :controller => @controller.class.controller_path }
258
258
  @request.parameters.update(parameters) unless parameters.nil?
259
259
  @request.session = ActionController::TestSession.new(session) unless session.nil?
260
+ @request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash
260
261
  @controller.process(@request, @response)
261
262
  end
262
263
 
@@ -2,10 +2,9 @@ module ActionController
2
2
  # Rewrites URLs for Base.redirect_to and Base.url_for in the controller.
3
3
 
4
4
  class UrlRewriter #:nodoc:
5
- RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :application_prefix]
5
+ RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :trailing_slash]
6
6
  def initialize(request, parameters)
7
7
  @request, @parameters = request, parameters
8
- @rewritten_path = @request.path ? @request.path.dup : ""
9
8
  end
10
9
 
11
10
  def rewrite(options = {})
@@ -19,14 +18,14 @@ module ActionController
19
18
  alias_method :to_s, :to_str
20
19
 
21
20
  private
22
- def rewrite_url(path, options)
23
-
21
+ def rewrite_url(path, options)
24
22
  rewritten_url = ""
25
23
  rewritten_url << (options[:protocol] || @request.protocol) unless options[:only_path]
26
24
  rewritten_url << (options[:host] || @request.host_with_port) unless options[:only_path]
27
25
 
28
- rewritten_url << (options[:application_prefix] || @request.relative_url_root)
26
+ rewritten_url << @request.relative_url_root.to_s
29
27
  rewritten_url << path
28
+ rewritten_url << '/' if options[:trailing_slash]
30
29
  rewritten_url << "##{options[:anchor]}" if options[:anchor]
31
30
 
32
31
  return rewritten_url
@@ -34,10 +33,17 @@ module ActionController
34
33
 
35
34
  def rewrite_path(options)
36
35
  options = options.symbolize_keys
37
- options.update((options[:params]).symbolize_keys) if options[:params]
36
+ options.update((options[:params] || {}).symbolize_keys)
38
37
  RESERVED_OPTIONS.each {|k| options.delete k}
39
-
40
38
  path, extras = Routing::Routes.generate(options, @request)
39
+
40
+ if extras[:overwrite_params]
41
+ params_copy = @request.parameters.delete_if { |k,v| ["controller","action"].include? k }
42
+ params_copy.update extras[:overwrite_params]
43
+ extras.delete(:overwrite_params)
44
+ extras.update(params_copy)
45
+ end
46
+
41
47
  path = "/#{path.join('/')}".chomp '/'
42
48
  path = '/' if path.empty?
43
49
  path += build_query_string(extras)
@@ -45,46 +51,6 @@ module ActionController
45
51
  return path
46
52
  end
47
53
 
48
- def action_name(options, action_prefix = nil, action_suffix = nil)
49
- ensure_slash_suffix(options, :action_prefix)
50
- ensure_slash_prefix(options, :action_suffix)
51
-
52
- prefix = options[:action_prefix] || action_prefix || ""
53
- suffix = options[:action] == "index" ? "" : (options[:action_suffix] || action_suffix || "")
54
- name = (options[:action] == "index" ? "" : options[:action]) || ""
55
-
56
- return prefix + name + suffix
57
- end
58
-
59
- def controller_name(options, controller_prefix)
60
- ensure_slash_suffix(options, :controller_prefix)
61
-
62
- controller_name = case options[:controller_prefix]
63
- when String: options[:controller_prefix]
64
- when false : ""
65
- when nil : controller_prefix || ""
66
- end
67
-
68
- controller_name << (options[:controller] + "/") if options[:controller]
69
- return controller_name
70
- end
71
-
72
- def path_params_in_list(options)
73
- options[:path_params].inject("") { |path, pair| path += "/#{pair.last}" }
74
- end
75
-
76
- def ensure_slash_suffix(options, key)
77
- options[key] = options[key] + "/" if options[key] && !options[key].empty? && options[key][-1..-1] != "/"
78
- end
79
-
80
- def ensure_slash_prefix(options, key)
81
- options[key] = "/" + options[key] if options[key] && !options[key].empty? && options[key][0..1] != "/"
82
- end
83
-
84
- def include_id_in_path_params(options)
85
- options[:path_params] = (options[:path_params] || {}).merge({"id" => options[:id]}) if options[:id]
86
- end
87
-
88
54
  # Returns a query string with escaped keys and values from the passed hash. If the passed hash contains an "id" it'll
89
55
  # be added as a path element instead of a regular parameter pair.
90
56
  def build_query_string(hash)
@@ -0,0 +1,79 @@
1
+ module ActionController #:nodoc:
2
+
3
+ # This module provides a class-level method for specifying that certain
4
+ # actions are guarded against being called without certain prerequisites
5
+ # being met. This is essentially a special kind of before_filter.
6
+ #
7
+ # An action may be guarded against being invoked without certain request
8
+ # parameters being set, or without certain session values existing.
9
+ #
10
+ # When a verification is violated, values may be inserted into the flash, and
11
+ # a specified redirection is triggered.
12
+ #
13
+ # Usage:
14
+ #
15
+ # class GlobalController < ActionController::Base
16
+ # # prevent the #update_settings action from being invoked unless
17
+ # # the 'admin_privileges' request parameter exists.
18
+ # verify :params => "admin_privileges", :only => :update_post,
19
+ # :redirect_to => { :action => "settings" }
20
+ #
21
+ # # disallow a post from being updated if there was no information
22
+ # # submitted with the post, and if there is no active post in the
23
+ # # session, and if there is no "note" key in the flash.
24
+ # verify :params => "post", :session => "post", "flash" => "note",
25
+ # :only => :update_post,
26
+ # :add_flash => { "alert" => "Failed to create your message" },
27
+ # :redirect_to => :category_url
28
+ #
29
+ module Verification
30
+ def self.append_features(base) #:nodoc:
31
+ super
32
+ base.extend(ClassMethods)
33
+ end
34
+
35
+ module ClassMethods
36
+ # Verify the given actions so that if certain prerequisites are not met,
37
+ # the user is redirected to a different action. The +options+ parameter
38
+ # is a hash consisting of the following key/value pairs:
39
+ #
40
+ # * <tt>:params</tt>: a single key or an array of keys that must
41
+ # be in the @params hash in order for the action(s) to be safely
42
+ # called.
43
+ # * <tt>:session</tt>: a single key or an array of keys that must
44
+ # be in the @session in order for the action(s) to be safely called.
45
+ # * <tt>:flash</tt>: a single key or an array of keys that must
46
+ # be in the flash in order for the action(s) to be safely called.
47
+ # * <tt>:add_flash</tt>: a hash of name/value pairs that should be merged
48
+ # into the session's flash if the prerequisites cannot be satisfied.
49
+ # * <tt>:redirect_to</tt>: the redirection parameters to be used when
50
+ # redirecting if the prerequisites cannot be satisfied.
51
+ # * <tt>:only</tt>: only apply this verification to the actions specified in
52
+ # the associated array (may also be a single value).
53
+ # * <tt>:except</tt>: do not apply this verification to the actions specified in
54
+ # the associated array (may also be a single value).
55
+ def verify(options={})
56
+ filter_opts = { :only => options[:only], :except => options[:except] }
57
+ before_filter(filter_opts) do |c|
58
+ c.send :verify_action, options
59
+ end
60
+ end
61
+ end
62
+
63
+ def verify_action(options) #:nodoc:
64
+ prereqs_invalid =
65
+ [*options[:params] ].find { |v| @params[v].nil? } ||
66
+ [*options[:session]].find { |v| @session[v].nil? } ||
67
+ [*options[:flash] ].find { |v| flash[v].nil? }
68
+
69
+ if prereqs_invalid
70
+ flash.update(options[:add_flash]) if options[:add_flash]
71
+ redirect_to(options[:redirect_to])
72
+ return false
73
+ end
74
+
75
+ true
76
+ end
77
+ private :verify_action
78
+ end
79
+ end