actionpack 1.8.1 → 1.9.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.

Files changed (101) hide show
  1. data/CHANGELOG +309 -16
  2. data/README +1 -1
  3. data/lib/action_controller.rb +5 -0
  4. data/lib/action_controller/assertions.rb +57 -12
  5. data/lib/action_controller/auto_complete.rb +47 -0
  6. data/lib/action_controller/base.rb +288 -258
  7. data/lib/action_controller/benchmarking.rb +8 -3
  8. data/lib/action_controller/caching.rb +88 -42
  9. data/lib/action_controller/cgi_ext/cgi_ext.rb +1 -1
  10. data/lib/action_controller/cgi_ext/cgi_methods.rb +41 -11
  11. data/lib/action_controller/cgi_ext/multipart_progress.rb +169 -0
  12. data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +30 -12
  13. data/lib/action_controller/cgi_process.rb +39 -11
  14. data/lib/action_controller/code_generation.rb +235 -0
  15. data/lib/action_controller/cookies.rb +14 -8
  16. data/lib/action_controller/deprecated_renders_and_redirects.rb +76 -0
  17. data/lib/action_controller/filters.rb +8 -7
  18. data/lib/action_controller/helpers.rb +41 -6
  19. data/lib/action_controller/layout.rb +45 -16
  20. data/lib/action_controller/request.rb +86 -23
  21. data/lib/action_controller/rescue.rb +1 -0
  22. data/lib/action_controller/response.rb +1 -1
  23. data/lib/action_controller/routing.rb +536 -272
  24. data/lib/action_controller/scaffolding.rb +30 -25
  25. data/lib/action_controller/session/active_record_store.rb +251 -50
  26. data/lib/action_controller/streaming.rb +133 -0
  27. data/lib/action_controller/templates/rescues/_request_and_response.rhtml +0 -7
  28. data/lib/action_controller/templates/scaffolds/edit.rhtml +2 -2
  29. data/lib/action_controller/templates/scaffolds/layout.rhtml +22 -18
  30. data/lib/action_controller/templates/scaffolds/list.rhtml +3 -3
  31. data/lib/action_controller/templates/scaffolds/new.rhtml +2 -2
  32. data/lib/action_controller/templates/scaffolds/show.rhtml +1 -1
  33. data/lib/action_controller/test_process.rb +68 -47
  34. data/lib/action_controller/upload_progress.rb +421 -0
  35. data/lib/action_controller/url_rewriter.rb +8 -11
  36. data/lib/action_controller/vendor/html-scanner/html/document.rb +6 -5
  37. data/lib/action_controller/vendor/html-scanner/html/node.rb +70 -14
  38. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +17 -10
  39. data/lib/action_controller/vendor/html-scanner/html/version.rb +3 -3
  40. data/lib/action_controller/vendor/xml_simple.rb +1019 -0
  41. data/lib/action_controller/verification.rb +36 -30
  42. data/lib/action_view/base.rb +21 -14
  43. data/lib/action_view/helpers/active_record_helper.rb +15 -13
  44. data/lib/action_view/helpers/asset_tag_helper.rb +26 -9
  45. data/lib/action_view/helpers/benchmark_helper.rb +24 -0
  46. data/lib/action_view/helpers/capture_helper.rb +7 -5
  47. data/lib/action_view/helpers/date_helper.rb +63 -46
  48. data/lib/action_view/helpers/form_helper.rb +7 -1
  49. data/lib/action_view/helpers/form_options_helper.rb +19 -11
  50. data/lib/action_view/helpers/form_tag_helper.rb +5 -1
  51. data/lib/action_view/helpers/javascript_helper.rb +403 -35
  52. data/lib/action_view/helpers/javascripts/controls.js +261 -0
  53. data/lib/action_view/helpers/javascripts/dragdrop.js +476 -0
  54. data/lib/action_view/helpers/javascripts/effects.js +570 -0
  55. data/lib/action_view/helpers/javascripts/prototype.js +633 -371
  56. data/lib/action_view/helpers/number_helper.rb +11 -13
  57. data/lib/action_view/helpers/tag_helper.rb +1 -2
  58. data/lib/action_view/helpers/text_helper.rb +69 -6
  59. data/lib/action_view/helpers/upload_progress_helper.rb +433 -0
  60. data/lib/action_view/helpers/url_helper.rb +98 -3
  61. data/lib/action_view/partials.rb +14 -8
  62. data/lib/action_view/vendor/builder/xmlmarkup.rb +11 -0
  63. data/rakefile +13 -5
  64. data/test/abstract_unit.rb +1 -1
  65. data/test/controller/action_pack_assertions_test.rb +52 -9
  66. data/test/controller/active_record_assertions_test.rb +119 -120
  67. data/test/controller/active_record_store_test.rb +111 -0
  68. data/test/controller/addresses_render_test.rb +45 -0
  69. data/test/controller/caching_filestore.rb +92 -0
  70. data/test/controller/capture_test.rb +39 -0
  71. data/test/controller/cgi_test.rb +40 -3
  72. data/test/controller/helper_test.rb +65 -13
  73. data/test/controller/multipart_progress_testx.rb +365 -0
  74. data/test/controller/new_render_test.rb +263 -0
  75. data/test/controller/redirect_test.rb +64 -0
  76. data/test/controller/render_test.rb +20 -21
  77. data/test/controller/request_test.rb +83 -3
  78. data/test/controller/routing_test.rb +702 -0
  79. data/test/controller/send_file_test.rb +2 -0
  80. data/test/controller/test_test.rb +44 -8
  81. data/test/controller/upload_progress_testx.rb +89 -0
  82. data/test/controller/verification_test.rb +94 -29
  83. data/test/fixtures/addresses/list.rhtml +1 -0
  84. data/test/fixtures/test/capturing.rhtml +4 -0
  85. data/test/fixtures/test/list.rhtml +1 -1
  86. data/test/fixtures/test/update_element_with_capture.rhtml +9 -0
  87. data/test/template/active_record_helper_test.rb +30 -15
  88. data/test/template/asset_tag_helper_test.rb +12 -5
  89. data/test/template/benchmark_helper_test.rb +72 -0
  90. data/test/template/date_helper_test.rb +69 -0
  91. data/test/template/form_helper_test.rb +18 -10
  92. data/test/template/form_options_helper_test.rb +40 -5
  93. data/test/template/javascript_helper.rb +149 -2
  94. data/test/template/number_helper_test.rb +2 -0
  95. data/test/template/tag_helper_test.rb +4 -0
  96. data/test/template/text_helper_test.rb +36 -0
  97. data/test/template/upload_progress_helper_testx.rb +272 -0
  98. data/test/template/url_helper_test.rb +30 -0
  99. metadata +30 -6
  100. data/test/controller/layout_test.rb +0 -49
  101. data/test/controller/routing_tests.rb +0 -543
@@ -1,37 +1,36 @@
1
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
2
+ module Verification #:nodoc:
30
3
  def self.append_features(base) #:nodoc:
31
4
  super
32
5
  base.extend(ClassMethods)
33
6
  end
34
7
 
8
+ # This module provides a class-level method for specifying that certain
9
+ # actions are guarded against being called without certain prerequisites
10
+ # being met. This is essentially a special kind of before_filter.
11
+ #
12
+ # An action may be guarded against being invoked without certain request
13
+ # parameters being set, or without certain session values existing.
14
+ #
15
+ # When a verification is violated, values may be inserted into the flash, and
16
+ # a specified redirection is triggered.
17
+ #
18
+ # Usage:
19
+ #
20
+ # class GlobalController < ActionController::Base
21
+ # # prevent the #update_settings action from being invoked unless
22
+ # # the 'admin_privileges' request parameter exists.
23
+ # verify :params => "admin_privileges", :only => :update_post,
24
+ # :redirect_to => { :action => "settings" }
25
+ #
26
+ # # disallow a post from being updated if there was no information
27
+ # # submitted with the post, and if there is no active post in the
28
+ # # session, and if there is no "note" key in the flash.
29
+ # verify :params => "post", :session => "post", "flash" => "note",
30
+ # :only => :update_post,
31
+ # :add_flash => { "alert" => "Failed to create your message" },
32
+ # :redirect_to => :category_url
33
+ #
35
34
  module ClassMethods
36
35
  # Verify the given actions so that if certain prerequisites are not met,
37
36
  # the user is redirected to a different action. The +options+ parameter
@@ -52,6 +51,8 @@ module ActionController #:nodoc:
52
51
  # into the session's flash if the prerequisites cannot be satisfied.
53
52
  # * <tt>:redirect_to</tt>: the redirection parameters to be used when
54
53
  # redirecting if the prerequisites cannot be satisfied.
54
+ # * <tt>:render</tt>: the render parameters to be used when
55
+ # the prerequisites cannot be satisfied.
55
56
  # * <tt>:only</tt>: only apply this verification to the actions specified
56
57
  # in the associated array (may also be a single value).
57
58
  # * <tt>:except</tt>: do not apply this verification to the actions
@@ -74,10 +75,15 @@ module ActionController #:nodoc:
74
75
  prereqs_invalid ||=
75
76
  [*options[:method]].all? { |v| @request.method != v.to_sym }
76
77
  end
77
-
78
+
79
+ prereqs_invalid ||= (request.xhr? != options[:xhr]) unless options[:xhr].nil?
80
+
78
81
  if prereqs_invalid
79
82
  flash.update(options[:add_flash]) if options[:add_flash]
80
- redirect_to(options[:redirect_to])
83
+ unless performed?
84
+ render(options[:render]) if options[:render]
85
+ redirect_to(options[:redirect_to]) if options[:redirect_to]
86
+ end
81
87
  return false
82
88
  end
83
89
 
@@ -122,6 +122,8 @@ module ActionView #:nodoc:
122
122
  attr_reader :first_render
123
123
  attr_accessor :base_path, :assigns, :template_extension
124
124
  attr_accessor :controller
125
+
126
+ attr_reader :logger, :params, :response, :session, :headers, :flash
125
127
 
126
128
  # Turn on to cache the reading of templates from the file system. Doing so means that you have to restart the server
127
129
  # when changing templates, but that rendering will be faster.
@@ -142,16 +144,6 @@ module ActionView #:nodoc:
142
144
  end
143
145
  end
144
146
 
145
- def self.controller_delegate(*methods)#:nodoc:
146
- methods.flatten.each do |method|
147
- class_eval <<-end_eval
148
- def #{method}(*args, &block)
149
- controller.send(%(#{method}), *args, &block)
150
- end
151
- end_eval
152
- end
153
- end
154
-
155
147
  def self.register_template_handler(extension, klass)
156
148
  @@template_handlers[extension] = klass
157
149
  end
@@ -191,8 +183,23 @@ module ActionView #:nodoc:
191
183
 
192
184
  # Renders the template present at <tt>template_path</tt> (relative to the template_root).
193
185
  # The hash in <tt>local_assigns</tt> is made available as local variables.
194
- def render(template_path, local_assigns = {})
195
- render_file(template_path, true, local_assigns)
186
+ def render(options = {}, old_local_assigns = {})
187
+ if options.is_a?(String)
188
+ render_file(options, true, old_local_assigns)
189
+ elsif options.is_a?(Hash)
190
+ options[:locals] ||= {}
191
+ options[:use_full_path] = options[:use_full_path].nil? ? true : options[:use_full_path]
192
+
193
+ if options[:file]
194
+ render_file(options[:file], options[:use_full_path], options[:locals])
195
+ elsif options[:partial] && options[:collection]
196
+ render_partial_collection(options[:partial], options[:collection], options[:spacer_template], options[:locals])
197
+ elsif options[:partial]
198
+ render_partial(options[:partial], options[:object], options[:locals])
199
+ elsif options[:inline]
200
+ render_template(options[:type] || :rhtml, options[:inline], options[:locals] || {})
201
+ end
202
+ end
196
203
  end
197
204
 
198
205
  # Renders the +template+ which is given as a string as either rhtml or rxml depending on <tt>template_extension</tt>.
@@ -218,7 +225,7 @@ module ActionView #:nodoc:
218
225
  if @@template_handlers[template_extension]
219
226
  "delegate_render"
220
227
  else
221
- (template_extension == "rxml" ? "rxml" : "rhtml") + "_render"
228
+ (template_extension.to_s == "rxml" ? "rxml" : "rhtml") + "_render"
222
229
  end
223
230
  end
224
231
 
@@ -290,4 +297,4 @@ module ActionView #:nodoc:
290
297
  end
291
298
  end
292
299
 
293
- require 'action_view/template_error'
300
+ require 'action_view/template_error'
@@ -31,7 +31,7 @@ module ActionView
31
31
  # </p>
32
32
  # <p>
33
33
  # <label for="post_body">Body</label><br />
34
- # <textarea cols="40" id="post_body" name="post[body]" rows="20" wrap="virtual">
34
+ # <textarea cols="40" id="post_body" name="post[body]" rows="20">
35
35
  # Back to the hill and over it again!
36
36
  # </textarea>
37
37
  # </p>
@@ -56,20 +56,22 @@ module ActionView
56
56
  # form << content_tag("b", "Department")
57
57
  # form << collection_select("department", "id", @departments, "id", "name")
58
58
  # end
59
- def form(record_name, options = nil)
60
- options = (options || {}).symbolize_keys
61
- record = instance_eval("@#{record_name}")
59
+ def form(record_name, options = {})
60
+ record = instance_variable_get("@#{record_name}")
62
61
 
62
+ options = options.symbolize_keys
63
63
  options[:action] ||= record.new_record? ? "create" : "update"
64
- action = url_for(:action => options[:action])
64
+ action = url_for(:action => options[:action], :id => record)
65
65
 
66
66
  submit_value = options[:submit_value] || options[:action].gsub(/[^\w]/, '').capitalize
67
67
 
68
- id_field = record.new_record? ? "" : InstanceTag.new(record_name, "id", self).to_input_field_tag("hidden")
68
+ contents = ''
69
+ contents << hidden_field(record_name, :id) unless record.new_record?
70
+ contents << all_input_tags(record, record_name, options)
71
+ yield contents if block_given?
72
+ contents << submit_tag(submit_value)
69
73
 
70
- formtag = %(<form action="#{action}" method="post">#{id_field}) + all_input_tags(record, record_name, options)
71
- yield formtag if block_given?
72
- formtag + %(<input type="submit" value="#{submit_value}" /></form>)
74
+ content_tag('form', contents, :action => action, :method => 'post')
73
75
  end
74
76
 
75
77
  # Returns a string containing the error message attached to the +method+ on the +object+, if one exists.
@@ -83,8 +85,8 @@ module ActionView
83
85
  # <%= error_message_on "post", "title", "Title simply ", " (or it won't work)", "inputError" %> =>
84
86
  # <div class="inputError">Title simply can't be empty (or it won't work)</div>
85
87
  def error_message_on(object, method, prepend_text = "", append_text = "", css_class = "formError")
86
- if errors = instance_eval("@#{object}").errors.on(method)
87
- "<div class=\"#{css_class}\">#{prepend_text + (errors.is_a?(Array) ? errors.first : errors) + append_text}</div>"
88
+ if errors = instance_variable_get("@#{object}").errors.on(method)
89
+ content_tag("div", "#{prepend_text}#{errors.is_a?(Array) ? errors.first : errors}#{append_text}", :class => css_class)
88
90
  end
89
91
  end
90
92
 
@@ -96,7 +98,7 @@ module ActionView
96
98
  # * <tt>class</tt> - The class of the error div (default: errorExplanation)
97
99
  def error_messages_for(object_name, options = {})
98
100
  options = options.symbolize_keys
99
- object = instance_eval "@#{object_name}"
101
+ object = instance_variable_get("@#{object_name}")
100
102
  unless object.errors.empty?
101
103
  content_tag("div",
102
104
  content_tag(
@@ -117,7 +119,7 @@ module ActionView
117
119
  end
118
120
 
119
121
  def default_input_block
120
- Proc.new { |record, column| "<p><label for=\"#{record}_#{column.name}\">#{column.human_name}</label><br />#{input(record, column.name)}</p>" }
122
+ Proc.new { |record, column| %(<p><label for="#{record}_#{column.name}">#{column.human_name}</label><br />#{input(record, column.name)}</p>) }
121
123
  end
122
124
  end
123
125
 
@@ -14,11 +14,16 @@ module ActionView
14
14
  # <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.curenthost.com/controller/action" />
15
15
  # auto_discovery_link_tag(:atom) # =>
16
16
  # <link rel="alternate" type="application/atom+xml" title="ATOM" href="http://www.curenthost.com/controller/action" />
17
- # auto_discovery_link_tag(:rss, :action => "feed") # =>
18
- # <link rel="alternate" type="application/atom+xml" title="ATOM" href="http://www.curenthost.com/controller/feed" />
19
- def auto_discovery_link_tag(type = :rss, options = {})
17
+ # auto_discovery_link_tag(:rss, {:action => "feed"}) # =>
18
+ # <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.curenthost.com/controller/feed" />
19
+ # auto_discovery_link_tag(:rss, {:action => "feed"}, {:title => "My RSS"}) # =>
20
+ # <link rel="alternate" type="application/rss+xml" title="My RSS" href="http://www.curenthost.com/controller/feed" />
21
+ def auto_discovery_link_tag(type = :rss, options = {}, tag_options = {})
20
22
  tag(
21
- "link", "rel" => "alternate", "type" => "application/#{type}+xml", "title" => type.to_s.upcase,
23
+ "link",
24
+ "rel" => tag_options[:rel] || "alternate",
25
+ "type" => tag_options[:type] || "application/#{type}+xml",
26
+ "title" => tag_options[:title] || type.to_s.upcase,
22
27
  "href" => url_for(options.merge(:only_path => false))
23
28
  )
24
29
  end
@@ -38,10 +43,18 @@ module ActionView
38
43
  # javascript_include_tag "common.javascript", "/elsewhere/cools" # =>
39
44
  # <script type="text/javascript" src="/javascripts/common.javascript"></script>
40
45
  # <script type="text/javascript" src="/elsewhere/cools.js"></script>
46
+ #
47
+ # javascript_include_tag :defaults # =>
48
+ # <script type="text/javascript" src="/javascripts/prototype.js"></script>
49
+ # <script type="text/javascript" src="/javascripts/effects.js"></script>
50
+ # <script type="text/javascript" src="/javascripts/controls.js"></script>
51
+ # <script type="text/javascript" src="/javascripts/dragdrop.js"></script>
41
52
  def javascript_include_tag(*sources)
53
+ options = sources.last.is_a?(Hash) ? sources.pop.stringify_keys : { }
54
+ sources = ['prototype', 'effects', 'controls', 'dragdrop'] if sources.first == :defaults
42
55
  sources.collect { |source|
43
56
  source = javascript_path(source)
44
- content_tag("script", "", "type" => "text/javascript", "src" => source)
57
+ content_tag("script", "", { "type" => "text/javascript", "src" => source }.merge(options))
45
58
  }.join("\n")
46
59
  end
47
60
 
@@ -57,13 +70,17 @@ module ActionView
57
70
  # stylesheet_link_tag "style" # =>
58
71
  # <link href="/stylesheets/style.css" media="screen" rel="Stylesheet" type="text/css" />
59
72
  #
73
+ # stylesheet_link_tag "style", :media => "all" # =>
74
+ # <link href="/stylesheets/style.css" media="all" rel="Stylesheet" type="text/css" />
75
+ #
60
76
  # stylesheet_link_tag "random.styles", "/css/stylish" # =>
61
77
  # <link href="/stylesheets/random.styles" media="screen" rel="Stylesheet" type="text/css" />
62
78
  # <link href="/css/stylish.css" media="screen" rel="Stylesheet" type="text/css" />
63
79
  def stylesheet_link_tag(*sources)
80
+ options = sources.last.is_a?(Hash) ? sources.pop.stringify_keys : { }
64
81
  sources.collect { |source|
65
82
  source = stylesheet_path(source)
66
- tag("link", "rel" => "Stylesheet", "type" => "text/css", "media" => "screen", "href" => source)
83
+ tag("link", { "rel" => "Stylesheet", "type" => "text/css", "media" => "screen", "href" => source }.merge(options))
67
84
  }.join("\n")
68
85
  end
69
86
 
@@ -90,7 +107,7 @@ module ActionView
90
107
  options.symbolize_keys
91
108
 
92
109
  options[:src] = image_path(source)
93
- options[:alt] ||= source.split("/").last.split(".").first.capitalize
110
+ options[:alt] ||= File.basename(options[:src], '.*').split('.').first.capitalize
94
111
 
95
112
  if options[:size]
96
113
  options[:width], options[:height] = options[:size].split("x")
@@ -104,8 +121,8 @@ module ActionView
104
121
  def compute_public_path(source, dir, ext)
105
122
  source = "/#{dir}/#{source}" unless source.include?("/")
106
123
  source = "#{source}.#{ext}" unless source.include?(".")
107
- source = "#{@request.relative_url_root}#{source}"
108
- source
124
+ source = "#{@request.relative_url_root}#{source}" unless %r{^[-a-z]+://} =~ source
125
+ ActionController::Base.asset_host + source
109
126
  end
110
127
  end
111
128
  end
@@ -0,0 +1,24 @@
1
+ require 'benchmark'
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module BenchmarkHelper
6
+ # Measures the execution time of a block in a template and reports the result to the log. Example:
7
+ #
8
+ # <% benchmark "Notes section" do %>
9
+ # <%= expensive_notes_operation %>
10
+ # <% end %>
11
+ #
12
+ # Will add something like "Notes section (0.34523)" to the log.
13
+ #
14
+ # You may give an optional logger level as the second argument
15
+ # (:debug, :info, :warn, :error). The default is :info.
16
+ def benchmark(message = "Benchmarking", level = :info)
17
+ if @logger
18
+ real = Benchmark.realtime { yield }
19
+ @logger.send level, "#{message} (#{'%.5f' % real})"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -48,11 +48,11 @@ module ActionView
48
48
  # <% @greeting = capture do %>
49
49
  # Welcome To my shiny new web page!
50
50
  # <% end %>
51
- def capture(&block)
51
+ def capture(*args, &block)
52
52
  # execute the block
53
53
  buffer = eval("_erbout", block.binding)
54
54
  pos = buffer.length
55
- block.call
55
+ block.call(*args)
56
56
 
57
57
  # extract the block
58
58
  data = buffer[pos..-1]
@@ -77,7 +77,10 @@ module ActionView
77
77
  # alert('hello world')
78
78
  # <% end %>
79
79
  #
80
- # You can use @content_for_header anywhere in your templates
80
+ # You can use @content_for_header anywhere in your templates.
81
+ #
82
+ # NOTE: Beware that content_for is ignored in caches. So you shouldn't use it
83
+ # for elements that are going to be fragment cached.
81
84
  def content_for(name, &block)
82
85
  base = controller.instance_variable_get(instance_var_name(name)) || ""
83
86
  data = capture(&block)
@@ -85,8 +88,7 @@ module ActionView
85
88
  data
86
89
  end
87
90
 
88
- private
89
-
91
+ private
90
92
  def instance_var_name(name) #:nodoc#
91
93
  "@content_for_#{name}"
92
94
  end
@@ -11,31 +11,38 @@ module ActionView
11
11
  # * <tt>:discard_type</tt> - set to true if you want to discard the type part of the select name. If set to true, the select_month
12
12
  # method would use simply "date" (which can be overwritten using <tt>:prefix</tt>) instead of "date[month]".
13
13
  module DateHelper
14
- DEFAULT_PREFIX = "date" unless const_defined?("DEFAULT_PREFIX")
14
+ DEFAULT_PREFIX = 'date' unless const_defined?('DEFAULT_PREFIX')
15
15
 
16
- # Reports the approximate distance in time between to Time objects. For example, if the distance is 47 minutes, it'll return
16
+ # Reports the approximate distance in time between to Time objects or integers.
17
+ # For example, if the distance is 47 minutes, it'll return
17
18
  # "about 1 hour". See the source for the complete wording list.
18
- #Set <tt>include_seconds</tt> to true if you want more detailed approximations if distance < 1 minute
19
- def distance_of_time_in_words(from_time, to_time, include_seconds = false)
20
- distance_in_minutes = ((to_time - from_time) / 60).round.abs
21
- distance_in_seconds = ((to_time - from_time)).round.abs
19
+ #
20
+ # Integers are interpreted as seconds. So,
21
+ # <tt>distance_of_time_in_words(50)</tt> returns "less than a minute".
22
+ #
23
+ # Set <tt>include_seconds</tt> to true if you want more detailed approximations if distance < 1 minute
24
+ def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false)
25
+ from_time = from_time.to_time if from_time.respond_to?(:to_time)
26
+ to_time = to_time.to_time if to_time.respond_to?(:to_time)
27
+ distance_in_minutes = (((to_time - from_time).abs)/60).round
28
+ distance_in_seconds = ((to_time - from_time).abs).round
22
29
 
23
30
  case distance_in_minutes
24
31
  when 0..1
25
- return (distance_in_minutes==0) ? "less than a minute" : "1 minute" unless include_seconds
32
+ return (distance_in_minutes==0) ? 'less than a minute' : '1 minute' unless include_seconds
26
33
  case distance_in_seconds
27
- when 0..5 then "less than 5 seconds"
28
- when 6..10 then "less than 10 seconds"
29
- when 11..20 then "less than 20 seconds"
30
- when 21..40 then "half a minute"
31
- when 41..59 then "less than a minute"
32
- else "1 minute"
34
+ when 0..5 then 'less than 5 seconds'
35
+ when 6..10 then 'less than 10 seconds'
36
+ when 11..20 then 'less than 20 seconds'
37
+ when 21..40 then 'half a minute'
38
+ when 41..59 then 'less than a minute'
39
+ else '1 minute'
33
40
  end
34
41
 
35
42
  when 2..45 then "#{distance_in_minutes} minutes"
36
- when 46..90 then "about 1 hour"
43
+ when 46..90 then 'about 1 hour'
37
44
  when 90..1440 then "about #{(distance_in_minutes.to_f / 60.0).round} hours"
38
- when 1441..2880 then "1 day"
45
+ when 1441..2880 then '1 day'
39
46
  else "#{(distance_in_minutes / 1440).round} days"
40
47
  end
41
48
  end
@@ -100,63 +107,67 @@ module ActionView
100
107
 
101
108
  # Returns a select tag with options for each of the seconds 0 through 59 with the current second selected.
102
109
  # The <tt>second</tt> can also be substituted for a second number.
110
+ # Override the field name using the <tt>:field_name</tt> option, 'second' by default.
103
111
  def select_second(datetime, options = {})
104
112
  second_options = []
105
113
 
106
114
  0.upto(59) do |second|
107
115
  second_options << ((datetime && (datetime.kind_of?(Fixnum) ? datetime : datetime.sec) == second) ?
108
- "<option value=\"#{leading_zero_on_single_digits(second)}\" selected=\"selected\">#{leading_zero_on_single_digits(second)}</option>\n" :
109
- "<option value=\"#{leading_zero_on_single_digits(second)}\">#{leading_zero_on_single_digits(second)}</option>\n"
116
+ %(<option value="#{leading_zero_on_single_digits(second)}" selected="selected">#{leading_zero_on_single_digits(second)}</option>\n) :
117
+ %(<option value="#{leading_zero_on_single_digits(second)}">#{leading_zero_on_single_digits(second)}</option>\n)
110
118
  )
111
119
  end
112
120
 
113
- select_html("second", second_options, options[:prefix], options[:include_blank], options[:discard_type])
121
+ select_html(options[:field_name] || 'second', second_options, options[:prefix], options[:include_blank], options[:discard_type])
114
122
  end
115
123
 
116
124
  # Returns a select tag with options for each of the minutes 0 through 59 with the current minute selected.
117
125
  # Also can return a select tag with options by <tt>minute_step</tt> from 0 through 59 with the 00 minute selected
118
126
  # The <tt>minute</tt> can also be substituted for a minute number.
127
+ # Override the field name using the <tt>:field_name</tt> option, 'minute' by default.
119
128
  def select_minute(datetime, options = {})
120
129
  minute_options = []
121
130
 
122
131
  0.step(59, options[:minute_step] || 1) do |minute|
123
132
  minute_options << ((datetime && (datetime.kind_of?(Fixnum) ? datetime : datetime.min) == minute) ?
124
- "<option value=\"#{leading_zero_on_single_digits(minute)}\" selected=\"selected\">#{leading_zero_on_single_digits(minute)}</option>\n" :
125
- "<option value=\"#{leading_zero_on_single_digits(minute)}\">#{leading_zero_on_single_digits(minute)}</option>\n"
133
+ %(<option value="#{leading_zero_on_single_digits(minute)}" selected="selected">#{leading_zero_on_single_digits(minute)}</option>\n) :
134
+ %(<option value="#{leading_zero_on_single_digits(minute)}">#{leading_zero_on_single_digits(minute)}</option>\n)
126
135
  )
127
136
  end
128
137
 
129
- select_html("minute", minute_options, options[:prefix], options[:include_blank], options[:discard_type])
138
+ select_html(options[:field_name] || 'minute', minute_options, options[:prefix], options[:include_blank], options[:discard_type])
130
139
  end
131
140
 
132
141
  # Returns a select tag with options for each of the hours 0 through 23 with the current hour selected.
133
142
  # The <tt>hour</tt> can also be substituted for a hour number.
143
+ # Override the field name using the <tt>:field_name</tt> option, 'hour' by default.
134
144
  def select_hour(datetime, options = {})
135
145
  hour_options = []
136
146
 
137
147
  0.upto(23) do |hour|
138
148
  hour_options << ((datetime && (datetime.kind_of?(Fixnum) ? datetime : datetime.hour) == hour) ?
139
- "<option value=\"#{leading_zero_on_single_digits(hour)}\" selected=\"selected\">#{leading_zero_on_single_digits(hour)}</option>\n" :
140
- "<option value=\"#{leading_zero_on_single_digits(hour)}\">#{leading_zero_on_single_digits(hour)}</option>\n"
149
+ %(<option value="#{leading_zero_on_single_digits(hour)}" selected="selected">#{leading_zero_on_single_digits(hour)}</option>\n) :
150
+ %(<option value="#{leading_zero_on_single_digits(hour)}">#{leading_zero_on_single_digits(hour)}</option>\n)
141
151
  )
142
152
  end
143
153
 
144
- select_html("hour", hour_options, options[:prefix], options[:include_blank], options[:discard_type])
154
+ select_html(options[:field_name] || 'hour', hour_options, options[:prefix], options[:include_blank], options[:discard_type])
145
155
  end
146
156
 
147
157
  # Returns a select tag with options for each of the days 1 through 31 with the current day selected.
148
158
  # The <tt>date</tt> can also be substituted for a hour number.
159
+ # Override the field name using the <tt>:field_name</tt> option, 'day' by default.
149
160
  def select_day(date, options = {})
150
161
  day_options = []
151
162
 
152
163
  1.upto(31) do |day|
153
164
  day_options << ((date && (date.kind_of?(Fixnum) ? date : date.day) == day) ?
154
- "<option value=\"#{day}\" selected=\"selected\">#{day}</option>\n" :
155
- "<option value=\"#{day}\">#{day}</option>\n"
165
+ %(<option value="#{day}" selected="selected">#{day}</option>\n) :
166
+ %(<option value="#{day}">#{day}</option>\n)
156
167
  )
157
168
  end
158
169
 
159
- select_html("day", day_options, options[:prefix], options[:include_blank], options[:discard_type])
170
+ select_html(options[:field_name] || 'day', day_options, options[:prefix], options[:include_blank], options[:discard_type])
160
171
  end
161
172
 
162
173
  # Returns a select tag with options for each of the months January through December with the current month selected.
@@ -168,6 +179,8 @@ module ActionView
168
179
  # select_month(Date.today) # Will use keys like "January", "March"
169
180
  # select_month(Date.today, :use_month_numbers => true) # Will use keys like "1", "3"
170
181
  # select_month(Date.today, :add_month_numbers => true) # Will use keys like "1 - January", "3 - March"
182
+ #
183
+ # Override the field name using the <tt>:field_name</tt> option, 'month' by default.
171
184
  def select_month(date, options = {})
172
185
  month_options = []
173
186
 
@@ -175,7 +188,7 @@ module ActionView
175
188
  month_name = if options[:use_month_numbers]
176
189
  month_number
177
190
  elsif options[:add_month_numbers]
178
- month_number.to_s + " - " + Date::MONTHNAMES[month_number]
191
+ month_number.to_s + ' - ' + Date::MONTHNAMES[month_number]
179
192
  else
180
193
  Date::MONTHNAMES[month_number]
181
194
  end
@@ -186,27 +199,33 @@ module ActionView
186
199
  )
187
200
  end
188
201
 
189
- select_html("month", month_options, options[:prefix], options[:include_blank], options[:discard_type])
202
+ select_html(options[:field_name] || 'month', month_options, options[:prefix], options[:include_blank], options[:discard_type])
190
203
  end
191
204
 
192
205
  # Returns a select tag with options for each of the five years on each side of the current, which is selected. The five year radius
193
- # can be changed using the <tt>:start_year</tt> and <tt>:end_year</tt> keys in the +options+. The <tt>date</tt> can also be substituted
194
- # for a year given as a number. Example:
206
+ # can be changed using the <tt>:start_year</tt> and <tt>:end_year</tt> keys in the +options+. Both ascending and descending year
207
+ # lists are supported by making <tt>:start_year</tt> less than or greater than <tt>:end_year</tt>. The <tt>date</tt> can also be
208
+ # substituted for a year given as a number. Example:
195
209
  #
196
- # select_year(Date.today, :start_year => 1992, :end_year => 2007)
210
+ # select_year(Date.today, :start_year => 1992, :end_year => 2007) # ascending year values
211
+ # select_year(Date.today, :start_year => 2005, :end_year => 1900) # descending year values
212
+ #
213
+ # Override the field name using the <tt>:field_name</tt> option, 'year' by default.
197
214
  def select_year(date, options = {})
198
215
  year_options = []
199
216
  y = date ? (date.kind_of?(Fixnum) ? (y = (date == 0) ? Date.today.year : date) : date.year) : Date.today.year
200
- default_start_year, default_end_year = y-5, y+5
201
217
 
202
- (options[:start_year] || default_start_year).upto(options[:end_year] || default_end_year) do |year|
218
+ start_year, end_year = (options[:start_year] || y-5), (options[:end_year] || y+5)
219
+ step_val = start_year < end_year ? 1 : -1
220
+
221
+ start_year.step(end_year, step_val) do |year|
203
222
  year_options << ((date && (date.kind_of?(Fixnum) ? date : date.year) == year) ?
204
- "<option value=\"#{year}\" selected=\"selected\">#{year}</option>\n" :
205
- "<option value=\"#{year}\">#{year}</option>\n"
223
+ %(<option value="#{year}" selected="selected">#{year}</option>\n) :
224
+ %(<option value="#{year}">#{year}</option>\n)
206
225
  )
207
226
  end
208
227
 
209
- select_html("year", year_options, options[:prefix], options[:include_blank], options[:discard_type])
228
+ select_html(options[:field_name] || 'year', year_options, options[:prefix], options[:include_blank], options[:discard_type])
210
229
  end
211
230
 
212
231
  private
@@ -214,11 +233,9 @@ module ActionView
214
233
  select_html = %(<select name="#{prefix || DEFAULT_PREFIX})
215
234
  select_html << "[#{type}]" unless discard_type
216
235
  select_html << %(">\n)
217
- select_html << "<option value=\"\"></option>\n" if include_blank
236
+ select_html << %(<option value=""></option>\n) if include_blank
218
237
  select_html << options.to_s
219
238
  select_html << "</select>\n"
220
-
221
- return select_html
222
239
  end
223
240
 
224
241
  def leading_zero_on_single_digits(number)
@@ -235,7 +252,7 @@ module ActionView
235
252
  options_with_prefix = Proc.new { |position| options.merge(:prefix => "#{@object_name}[#{@method_name}(#{position}i)]") }
236
253
  date = options[:include_blank] ? (value || 0) : (value || Date.today)
237
254
 
238
- date_select = ""
255
+ date_select = ''
239
256
  options[:order] = [:month, :year, :day] if options[:month_before_year] # For backwards compatibility
240
257
  options[:order] ||= [:year, :month, :day]
241
258
 
@@ -250,7 +267,7 @@ module ActionView
250
267
  date_select << self.send("select_#{param}", date, options_with_prefix.call(position[param])) unless discard[param]
251
268
  end
252
269
 
253
- return date_select
270
+ date_select
254
271
  end
255
272
 
256
273
  def to_datetime_select_tag(options = {})
@@ -262,11 +279,11 @@ module ActionView
262
279
  datetime_select = select_year(datetime, options_with_prefix.call(1))
263
280
  datetime_select << select_month(datetime, options_with_prefix.call(2)) unless options[:discard_month]
264
281
  datetime_select << select_day(datetime, options_with_prefix.call(3)) unless options[:discard_day] || options[:discard_month]
265
- datetime_select << " &mdash; " + select_hour(datetime, options_with_prefix.call(4)) unless options[:discard_hour]
266
- datetime_select << " : " + select_minute(datetime, options_with_prefix.call(5)) unless options[:discard_minute] || options[:discard_hour]
282
+ datetime_select << ' &mdash; ' + select_hour(datetime, options_with_prefix.call(4)) unless options[:discard_hour]
283
+ datetime_select << ' : ' + select_minute(datetime, options_with_prefix.call(5)) unless options[:discard_minute] || options[:discard_hour]
267
284
 
268
- return datetime_select
285
+ datetime_select
269
286
  end
270
287
  end
271
288
  end
272
- end
289
+ end