actionpack 2.3.8 → 2.3.9.pre

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 (35) hide show
  1. data/CHANGELOG +5 -0
  2. data/Rakefile +1 -1
  3. data/lib/action_controller/caching/fragments.rb +1 -1
  4. data/lib/action_controller/integration.rb +16 -5
  5. data/lib/action_controller/request.rb +2 -2
  6. data/lib/action_controller/rescue.rb +2 -2
  7. data/lib/action_controller/resources.rb +1 -1
  8. data/lib/action_controller/routing.rb +1 -1
  9. data/lib/action_controller/routing/route.rb +3 -3
  10. data/lib/action_controller/session/abstract_store.rb +152 -56
  11. data/lib/action_controller/session/cookie_store.rb +51 -16
  12. data/lib/action_controller/session/mem_cache_store.rb +9 -0
  13. data/lib/action_controller/test_process.rb +1 -1
  14. data/lib/action_controller/url_rewriter.rb +9 -1
  15. data/lib/action_pack/version.rb +1 -1
  16. data/lib/action_view/helpers/form_helper.rb +3 -3
  17. data/lib/action_view/helpers/form_options_helper.rb +1 -1
  18. data/lib/action_view/helpers/form_tag_helper.rb +1 -1
  19. data/lib/action_view/helpers/prototype_helper.rb +3 -3
  20. data/lib/action_view/helpers/text_helper.rb +27 -17
  21. data/lib/action_view/locale/en.yml +14 -14
  22. data/lib/action_view/template.rb +2 -2
  23. data/test/abstract_unit.rb +17 -0
  24. data/test/activerecord/active_record_store_test.rb +65 -18
  25. data/test/controller/integration_test.rb +26 -0
  26. data/test/controller/request/multipart_params_parsing_test.rb +15 -0
  27. data/test/controller/rescue_test.rb +5 -6
  28. data/test/controller/session/cookie_store_test.rb +80 -2
  29. data/test/controller/session/mem_cache_store_test.rb +65 -2
  30. data/test/controller/url_rewriter_test.rb +9 -3
  31. data/test/fixtures/session_autoload_test/session_autoload_test/foo.rb +10 -0
  32. data/test/template/form_helper_test.rb +17 -0
  33. data/test/template/form_options_helper_test.rb +4 -0
  34. data/test/template/text_helper_test.rb +47 -18
  35. metadata +14 -9
data/CHANGELOG CHANGED
@@ -1,3 +1,8 @@
1
+ *2.3.9 (August 29, 2010)*
2
+
3
+ * Version bump.
4
+
5
+
1
6
  *2.3.8 (May 24, 2010)*
2
7
 
3
8
  * HTML safety: fix compatibility *without* the optional rails_xss plugin.
data/Rakefile CHANGED
@@ -79,7 +79,7 @@ spec = Gem::Specification.new do |s|
79
79
  s.has_rdoc = true
80
80
  s.requirements << 'none'
81
81
 
82
- s.add_dependency('activesupport', '= 2.3.8' + PKG_BUILD)
82
+ s.add_dependency('activesupport', '= 2.3.9' + PKG_BUILD)
83
83
  s.add_dependency('rack', '~> 1.1.0')
84
84
 
85
85
  s.require_path = 'lib'
@@ -65,8 +65,8 @@ module ActionController #:nodoc:
65
65
  def read_fragment(key, options = nil)
66
66
  return unless cache_configured?
67
67
 
68
+ key = fragment_cache_key(key)
68
69
  self.class.benchmark "Cached fragment hit: #{key}" do
69
- key = fragment_cache_key(key)
70
70
  result = cache_store.read(key, options)
71
71
  result.respond_to?(:html_safe) ? result.html_safe : result
72
72
  end
@@ -414,15 +414,25 @@ module ActionController
414
414
  end
415
415
 
416
416
  def multipart_requestify(params, first=true)
417
- returning Hash.new do |p|
417
+ Array.new.tap do |p|
418
418
  params.each do |key, value|
419
419
  k = first ? key.to_s : "[#{key.to_s}]"
420
420
  if Hash === value
421
421
  multipart_requestify(value, false).each do |subkey, subvalue|
422
- p[k + subkey] = subvalue
422
+ p << [k + subkey, subvalue]
423
+ end
424
+ elsif Array === value
425
+ value.each do |element|
426
+ if Hash === element || Array === element
427
+ multipart_requestify(element, false).each do |subkey, subvalue|
428
+ p << ["#{k}[]#{subkey}", subvalue]
429
+ end
430
+ else
431
+ p << ["#{k}[]", element]
432
+ end
423
433
  end
424
434
  else
425
- p[k] = value
435
+ p << [k, value]
426
436
  end
427
437
  end
428
438
  end
@@ -453,6 +463,7 @@ EOF
453
463
  end
454
464
  end.join("")+"--#{boundary}--\r"
455
465
  end
466
+
456
467
  end
457
468
 
458
469
  # A module used to extend ActionController::Base, so that integration tests
@@ -500,7 +511,7 @@ EOF
500
511
  reset! unless @integration_session
501
512
  # reset the html_document variable, but only for new get/post calls
502
513
  @html_document = nil unless %w(cookies assigns).include?(method)
503
- returning @integration_session.__send__(method, *args) do
514
+ @integration_session.__send__(method, *args).tap do
504
515
  copy_session_variables!
505
516
  end
506
517
  end
@@ -556,7 +567,7 @@ EOF
556
567
  def method_missing(sym, *args, &block)
557
568
  reset! unless @integration_session
558
569
  if @integration_session.respond_to?(sym)
559
- returning @integration_session.__send__(sym, *args, &block) do
570
+ @integration_session.__send__(sym, *args, &block).tap do
560
571
  copy_session_variables!
561
572
  end
562
573
  else
@@ -446,8 +446,8 @@ EOM
446
446
  end
447
447
 
448
448
  def reset_session
449
- @env['rack.session.options'].delete(:id)
450
- @env['rack.session'] = {}
449
+ session.destroy if session
450
+ self.session = {}
451
451
  end
452
452
 
453
453
  def session_options
@@ -15,7 +15,7 @@ module ActionController #:nodoc:
15
15
  # behavior is achieved by overriding the <tt>rescue_action_in_public</tt>
16
16
  # and <tt>rescue_action_locally</tt> methods.
17
17
  module Rescue
18
- LOCALHOST = ['127.0.0.1', '::1'].freeze
18
+ LOCALHOST = [/^127\.0\.0\.\d{1,3}$/, /^::1$/, /^0:0:0:0:0:0:0:1(%.*)?$/].freeze
19
19
 
20
20
  DEFAULT_RESCUE_RESPONSE = :internal_server_error
21
21
  DEFAULT_RESCUE_RESPONSES = {
@@ -122,7 +122,7 @@ module ActionController #:nodoc:
122
122
  # method if you wish to redefine the meaning of a local request to
123
123
  # include remote IP addresses or other criteria.
124
124
  def local_request? #:doc:
125
- LOCALHOST.any?{ |local_ip| request.remote_addr == local_ip && request.remote_ip == local_ip }
125
+ LOCALHOST.any?{ |local_ip| request.remote_addr =~ local_ip && request.remote_ip =~ local_ip }
126
126
  end
127
127
 
128
128
  # Render detailed diagnostics for unhandled exceptions rescued from
@@ -659,7 +659,7 @@ module ActionController
659
659
  end
660
660
 
661
661
  def add_conditions_for(conditions, method)
662
- returning({:conditions => conditions.dup}) do |options|
662
+ ({:conditions => conditions.dup}).tap do |options|
663
663
  options[:conditions][:method] = method unless method == :any
664
664
  end
665
665
  end
@@ -377,7 +377,7 @@ module ActionController
377
377
  ActiveSupport::Inflector.module_eval do
378
378
  # Ensures that routes are reloaded when Rails inflections are updated.
379
379
  def inflections_with_route_reloading(&block)
380
- returning(inflections_without_route_reloading(&block)) {
380
+ (inflections_without_route_reloading(&block)).tap {
381
381
  ActionController::Routing::Routes.reload! if block_given?
382
382
  }
383
383
  end
@@ -65,7 +65,7 @@ module ActionController
65
65
  # map.connect '/page/:id', :controller => 'pages', :action => 'show', :id => /\d+/
66
66
  #
67
67
  def parameter_shell
68
- @parameter_shell ||= returning({}) do |shell|
68
+ @parameter_shell ||= {}.tap do |shell|
69
69
  requirements.each do |key, requirement|
70
70
  shell[key] = requirement unless requirement.is_a? Regexp
71
71
  end
@@ -76,7 +76,7 @@ module ActionController
76
76
  # includes keys that appear inside the path, and keys that have requirements
77
77
  # placed upon them.
78
78
  def significant_keys
79
- @significant_keys ||= returning([]) do |sk|
79
+ @significant_keys ||= [].tap do |sk|
80
80
  segments.each { |segment| sk << segment.key if segment.respond_to? :key }
81
81
  sk.concat requirements.keys
82
82
  sk.uniq!
@@ -86,7 +86,7 @@ module ActionController
86
86
  # Return a hash of key/value pairs representing the keys in the route that
87
87
  # have defaults, or which are specified by non-regexp requirements.
88
88
  def defaults
89
- @defaults ||= returning({}) do |hash|
89
+ @defaults ||= {}.tap do |hash|
90
90
  segments.each do |segment|
91
91
  next unless segment.respond_to? :default
92
92
  hash[segment.key] = segment.default unless segment.default.nil?
@@ -2,13 +2,42 @@ require 'rack/utils'
2
2
 
3
3
  module ActionController
4
4
  module Session
5
- class AbstractStore
5
+ class AbstractStore
6
6
  ENV_SESSION_KEY = 'rack.session'.freeze
7
7
  ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze
8
8
 
9
9
  HTTP_COOKIE = 'HTTP_COOKIE'.freeze
10
10
  SET_COOKIE = 'Set-Cookie'.freeze
11
11
 
12
+ # thin wrapper around Hash that allows us to lazily
13
+ # load session id into session_options
14
+ class OptionsHash < Hash
15
+ def initialize(by, env, default_options)
16
+ @by = by
17
+ @env = env
18
+ @session_id_loaded = false
19
+ merge!(default_options)
20
+ end
21
+
22
+ def [](key)
23
+ if key == :id
24
+ load_session_id! unless super(:id) || has_session_id?
25
+ end
26
+ super(key)
27
+ end
28
+
29
+ private
30
+
31
+ def has_session_id?
32
+ @session_id_loaded
33
+ end
34
+
35
+ def load_session_id!
36
+ self[:id] = @by.send(:extract_session_id, @env)
37
+ @session_id_loaded = true
38
+ end
39
+ end
40
+
12
41
  class SessionHash < Hash
13
42
  def initialize(by, env)
14
43
  super()
@@ -25,21 +54,42 @@ module ActionController
25
54
  end
26
55
 
27
56
  def [](key)
28
- load! unless @loaded
57
+ load_for_read!
58
+ super
59
+ end
60
+
61
+ def has_key?(key)
62
+ load_for_read!
29
63
  super
30
64
  end
31
65
 
32
66
  def []=(key, value)
33
- load! unless @loaded
67
+ load_for_write!
68
+ super
69
+ end
70
+
71
+ def clear
72
+ load_for_write!
34
73
  super
35
74
  end
36
75
 
37
76
  def to_hash
77
+ load_for_read!
38
78
  h = {}.replace(self)
39
79
  h.delete_if { |k,v| v.nil? }
40
80
  h
41
81
  end
42
82
 
83
+ def update(hash)
84
+ load_for_write!
85
+ super
86
+ end
87
+
88
+ def delete(key)
89
+ load_for_write!
90
+ super
91
+ end
92
+
43
93
  def data
44
94
  ActiveSupport::Deprecation.warn(
45
95
  "ActionController::Session::AbstractStore::SessionHash#data " +
@@ -48,40 +98,43 @@ module ActionController
48
98
  end
49
99
 
50
100
  def inspect
51
- load! unless @loaded
101
+ load_for_read!
52
102
  super
53
103
  end
54
104
 
105
+ def exists?
106
+ return @exists if instance_variable_defined?(:@exists)
107
+ @exists = @by.send(:exists?, @env)
108
+ end
109
+
110
+ def loaded?
111
+ @loaded
112
+ end
113
+
114
+ def destroy
115
+ clear
116
+ @by.send(:destroy, @env) if @by
117
+ @env[ENV_SESSION_OPTIONS_KEY][:id] = nil if @env && @env[ENV_SESSION_OPTIONS_KEY]
118
+ @loaded = false
119
+ end
120
+
55
121
  private
56
- def loaded?
57
- @loaded
122
+
123
+ def load_for_read!
124
+ load! if !loaded? && exists?
58
125
  end
59
126
 
60
- def load!
61
- stale_session_check! do
62
- id, session = @by.send(:load_session, @env)
63
- (@env[ENV_SESSION_OPTIONS_KEY] ||= {})[:id] = id
64
- replace(session)
65
- @loaded = true
66
- end
127
+ def load_for_write!
128
+ load! unless loaded?
67
129
  end
68
130
 
69
- def stale_session_check!
70
- yield
71
- rescue ArgumentError => argument_error
72
- if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
73
- begin
74
- # Note that the regexp does not allow $1 to end with a ':'
75
- $1.constantize
76
- rescue LoadError, NameError => const_error
77
- raise ActionController::SessionRestoreError, "Session contains objects whose class definition isn\\'t available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: \#{const_error.message} [\#{const_error.class}])\n"
78
- end
79
-
80
- retry
81
- else
82
- raise
83
- end
131
+ def load!
132
+ id, session = @by.send(:load_session, @env)
133
+ @env[ENV_SESSION_OPTIONS_KEY][:id] = id
134
+ replace(session)
135
+ @loaded = true
84
136
  end
137
+
85
138
  end
86
139
 
87
140
  DEFAULT_OPTIONS = {
@@ -120,18 +173,14 @@ module ActionController
120
173
  end
121
174
 
122
175
  def call(env)
123
- session = SessionHash.new(self, env)
124
-
125
- env[ENV_SESSION_KEY] = session
126
- env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup
127
-
176
+ prepare!(env)
128
177
  response = @app.call(env)
129
178
 
130
179
  session_data = env[ENV_SESSION_KEY]
131
180
  options = env[ENV_SESSION_OPTIONS_KEY]
132
181
 
133
- if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) || options[:expire_after]
134
- session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.send(:loaded?)
182
+ if !session_data.is_a?(AbstractStore::SessionHash) || session_data.loaded? || options[:expire_after]
183
+ session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.loaded?
135
184
 
136
185
  sid = options[:id] || generate_sid
137
186
 
@@ -139,21 +188,23 @@ module ActionController
139
188
  return response
140
189
  end
141
190
 
142
- cookie = Rack::Utils.escape(@key) + '=' + Rack::Utils.escape(sid)
143
- cookie << "; domain=#{options[:domain]}" if options[:domain]
144
- cookie << "; path=#{options[:path]}" if options[:path]
145
- if options[:expire_after]
146
- expiry = Time.now + options[:expire_after]
147
- cookie << "; expires=#{expiry.httpdate}"
148
- end
149
- cookie << "; Secure" if options[:secure]
150
- cookie << "; HttpOnly" if options[:httponly]
151
-
152
- headers = response[1]
153
- unless headers[SET_COOKIE].blank?
154
- headers[SET_COOKIE] << "\n#{cookie}"
155
- else
156
- headers[SET_COOKIE] = cookie
191
+ if (env["rack.request.cookie_hash"] && env["rack.request.cookie_hash"][@key] != sid) || options[:expire_after]
192
+ cookie = Rack::Utils.escape(@key) + '=' + Rack::Utils.escape(sid)
193
+ cookie << "; domain=#{options[:domain]}" if options[:domain]
194
+ cookie << "; path=#{options[:path]}" if options[:path]
195
+ if options[:expire_after]
196
+ expiry = Time.now + options[:expire_after]
197
+ cookie << "; expires=#{expiry.httpdate}"
198
+ end
199
+ cookie << "; Secure" if options[:secure]
200
+ cookie << "; HttpOnly" if options[:httponly]
201
+
202
+ headers = response[1]
203
+ unless headers[SET_COOKIE].blank?
204
+ headers[SET_COOKIE] << "\n#{cookie}"
205
+ else
206
+ headers[SET_COOKIE] = cookie
207
+ end
157
208
  end
158
209
  end
159
210
 
@@ -161,18 +212,39 @@ module ActionController
161
212
  end
162
213
 
163
214
  private
215
+
216
+ def prepare!(env)
217
+ env[ENV_SESSION_KEY] = SessionHash.new(self, env)
218
+ env[ENV_SESSION_OPTIONS_KEY] = OptionsHash.new(self, env, @default_options)
219
+ end
220
+
164
221
  def generate_sid
165
222
  ActiveSupport::SecureRandom.hex(16)
166
223
  end
167
224
 
168
225
  def load_session(env)
169
- request = Rack::Request.new(env)
170
- sid = request.cookies[@key]
171
- unless @cookie_only
172
- sid ||= request.params[@key]
226
+ stale_session_check! do
227
+ sid = current_session_id(env)
228
+ sid, session = get_session(env, sid)
229
+ [sid, session]
230
+ end
231
+ end
232
+
233
+ def extract_session_id(env)
234
+ stale_session_check! do
235
+ request = Rack::Request.new(env)
236
+ sid = request.cookies[@key]
237
+ sid ||= request.params[@key] unless @cookie_only
238
+ sid
173
239
  end
174
- sid, session = get_session(env, sid)
175
- [sid, session]
240
+ end
241
+
242
+ def current_session_id(env)
243
+ env[ENV_SESSION_OPTIONS_KEY][:id]
244
+ end
245
+
246
+ def exists?(env)
247
+ current_session_id(env).present?
176
248
  end
177
249
 
178
250
  def get_session(env, sid)
@@ -182,6 +254,30 @@ module ActionController
182
254
  def set_session(env, sid, session_data)
183
255
  raise '#set_session needs to be implemented.'
184
256
  end
257
+
258
+ def destroy(env)
259
+ raise '#destroy needs to be implemented.'
260
+ end
261
+
262
+ module SessionUtils
263
+ private
264
+ def stale_session_check!
265
+ yield
266
+ rescue ArgumentError => argument_error
267
+ if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
268
+ begin
269
+ # Note that the regexp does not allow $1 to end with a ':'
270
+ $1.constantize
271
+ rescue LoadError, NameError => const_error
272
+ raise ActionController::SessionRestoreError, "Session contains objects whose class definition isn\\'t available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: \#{const_error.message} [\#{const_error.class}])\n"
273
+ end
274
+ retry
275
+ else
276
+ raise
277
+ end
278
+ end
279
+ end
280
+ include SessionUtils
185
281
  end
186
282
  end
187
283
  end