actionpack 3.0.0.beta2 → 3.0.0.beta3

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 (47) hide show
  1. data/CHANGELOG +37 -0
  2. data/lib/abstract_controller/rendering.rb +9 -11
  3. data/lib/action_controller.rb +0 -1
  4. data/lib/action_controller/base.rb +45 -44
  5. data/lib/action_controller/caching/pages.rb +2 -2
  6. data/lib/action_controller/deprecated/base.rb +21 -5
  7. data/lib/action_controller/metal/compatibility.rb +0 -2
  8. data/lib/action_controller/metal/cookies.rb +1 -1
  9. data/lib/action_controller/metal/helpers.rb +1 -1
  10. data/lib/action_controller/metal/http_authentication.rb +12 -5
  11. data/lib/action_controller/metal/responder.rb +3 -2
  12. data/lib/action_controller/polymorphic_routes.rb +1 -1
  13. data/lib/action_controller/railtie.rb +6 -1
  14. data/lib/action_controller/record_identifier.rb +2 -2
  15. data/lib/action_controller/test_case.rb +2 -0
  16. data/lib/action_dispatch/http/mime_negotiation.rb +2 -2
  17. data/lib/action_dispatch/http/request.rb +39 -21
  18. data/lib/action_dispatch/middleware/cookies.rb +18 -22
  19. data/lib/action_dispatch/middleware/params_parser.rb +8 -9
  20. data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -1
  21. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +1 -1
  22. data/lib/action_dispatch/railtie.rb +1 -0
  23. data/lib/action_dispatch/routing.rb +24 -12
  24. data/lib/action_dispatch/routing/deprecated_mapper.rb +1 -1
  25. data/lib/action_dispatch/routing/mapper.rb +77 -51
  26. data/lib/action_dispatch/routing/route_set.rb +9 -5
  27. data/lib/action_dispatch/routing/url_for.rb +2 -3
  28. data/lib/action_dispatch/testing/integration.rb +24 -11
  29. data/lib/action_dispatch/testing/test_request.rb +2 -0
  30. data/lib/action_pack/version.rb +1 -1
  31. data/lib/action_view/base.rb +8 -2
  32. data/lib/action_view/helpers.rb +3 -6
  33. data/lib/action_view/helpers/active_model_helper.rb +20 -265
  34. data/lib/action_view/helpers/asset_tag_helper.rb +46 -9
  35. data/lib/action_view/helpers/atom_feed_helper.rb +2 -2
  36. data/lib/action_view/helpers/form_helper.rb +161 -120
  37. data/lib/action_view/helpers/form_options_helper.rb +0 -2
  38. data/lib/action_view/helpers/form_tag_helper.rb +76 -13
  39. data/lib/action_view/helpers/url_helper.rb +48 -21
  40. data/lib/action_view/locale/en.yml +0 -8
  41. data/lib/action_view/lookup_context.rb +11 -4
  42. data/lib/action_view/render/layouts.rb +32 -18
  43. data/lib/action_view/render/partials.rb +2 -2
  44. data/lib/action_view/template.rb +10 -1
  45. data/lib/action_view/test_case.rb +6 -1
  46. metadata +7 -8
  47. data/lib/action_controller/metal/verification.rb +0 -130
@@ -1,3 +1,5 @@
1
+ require "active_support/core_ext/object/blank"
2
+
1
3
  module ActionDispatch
2
4
  class Request
3
5
  def cookie_jar
@@ -52,16 +54,17 @@ module ActionDispatch
52
54
  class Cookies
53
55
  class CookieJar < Hash #:nodoc:
54
56
  def self.build(request)
55
- new.tap do |hash|
57
+ secret = request.env["action_dispatch.secret_token"]
58
+ new(secret).tap do |hash|
56
59
  hash.update(request.cookies)
57
60
  end
58
61
  end
59
62
 
60
- def initialize
63
+ def initialize(secret=nil)
64
+ @secret = secret
61
65
  @set_cookies = {}
62
66
  @delete_cookies = {}
63
-
64
- super
67
+ super()
65
68
  end
66
69
 
67
70
  # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists.
@@ -111,7 +114,7 @@ module ActionDispatch
111
114
  # cookies.permanent.signed[:remember_me] = current_user.id
112
115
  # # => Set-Cookie: discount=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
113
116
  def permanent
114
- @permanent ||= PermanentCookieJar.new(self)
117
+ @permanent ||= PermanentCookieJar.new(self, @secret)
115
118
  end
116
119
 
117
120
  # Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from
@@ -119,7 +122,7 @@ module ActionDispatch
119
122
  # cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception will
120
123
  # be raised.
121
124
  #
122
- # This jar requires that you set a suitable secret for the verification on ActionController::Base.cookie_verifier_secret.
125
+ # This jar requires that you set a suitable secret for the verification on your app's config.secret_token.
123
126
  #
124
127
  # Example:
125
128
  #
@@ -128,7 +131,7 @@ module ActionDispatch
128
131
  #
129
132
  # cookies.signed[:discount] # => 45
130
133
  def signed
131
- @signed ||= SignedCookieJar.new(self)
134
+ @signed ||= SignedCookieJar.new(self, @secret)
132
135
  end
133
136
 
134
137
  def write(response)
@@ -138,8 +141,8 @@ module ActionDispatch
138
141
  end
139
142
 
140
143
  class PermanentCookieJar < CookieJar #:nodoc:
141
- def initialize(parent_jar)
142
- @parent_jar = parent_jar
144
+ def initialize(parent_jar, secret)
145
+ @parent_jar, @secret = parent_jar, secret
143
146
  end
144
147
 
145
148
  def []=(key, options)
@@ -154,11 +157,7 @@ module ActionDispatch
154
157
  end
155
158
 
156
159
  def signed
157
- @signed ||= SignedCookieJar.new(self)
158
- end
159
-
160
- def controller
161
- @parent_jar.controller
160
+ @signed ||= SignedCookieJar.new(self, @secret)
162
161
  end
163
162
 
164
163
  def method_missing(method, *arguments, &block)
@@ -167,18 +166,15 @@ module ActionDispatch
167
166
  end
168
167
 
169
168
  class SignedCookieJar < CookieJar #:nodoc:
170
- def initialize(parent_jar)
171
- unless ActionController::Base.config.secret
172
- raise "You must set ActionController::Base.config.secret"
173
- end
174
-
169
+ def initialize(parent_jar, secret)
170
+ raise "You must set config.secret_token in your app's config" if secret.blank?
175
171
  @parent_jar = parent_jar
176
- @verifier = ActiveSupport::MessageVerifier.new(ActionController::Base.config.secret)
172
+ @verifier = ActiveSupport::MessageVerifier.new(secret)
177
173
  end
178
174
 
179
175
  def [](name)
180
- if value = @parent_jar[name]
181
- @verifier.verify(value)
176
+ if signed_message = @parent_jar[name]
177
+ @verifier.verify(signed_message)
182
178
  end
183
179
  end
184
180
 
@@ -36,17 +36,16 @@ module ActionDispatch
36
36
  when Proc
37
37
  strategy.call(request.raw_post)
38
38
  when :xml_simple, :xml_node
39
- request.body.size == 0 ? {} : Hash.from_xml(request.raw_post).with_indifferent_access
39
+ data = Hash.from_xml(request.body) || {}
40
+ request.body.rewind if request.body.respond_to?(:rewind)
41
+ data.with_indifferent_access
40
42
  when :yaml
41
43
  YAML.load(request.raw_post)
42
44
  when :json
43
- if request.body.size == 0
44
- {}
45
- else
46
- data = ActiveSupport::JSON.decode(request.raw_post)
47
- data = {:_json => data} unless data.is_a?(Hash)
48
- data.with_indifferent_access
49
- end
45
+ data = ActiveSupport::JSON.decode(request.body)
46
+ request.body.rewind if request.body.respond_to?(:rewind)
47
+ data = {:_json => data} unless data.is_a?(Hash)
48
+ data.with_indifferent_access
50
49
  else
51
50
  false
52
51
  end
@@ -76,4 +75,4 @@ module ActionDispatch
76
75
  defined?(Rails.logger) ? Rails.logger : Logger.new($stderr)
77
76
  end
78
77
  end
79
- end
78
+ end
@@ -192,7 +192,7 @@ module ActionDispatch
192
192
  if secret.blank?
193
193
  raise ArgumentError, "A secret is required to generate an " +
194
194
  "integrity hash for cookie session data. Use " +
195
- "config.cookie_secret = \"some secret phrase of at " +
195
+ "config.secret_token = \"some secret phrase of at " +
196
196
  "least #{SECRET_MIN_LENGTH} characters\"" +
197
197
  "in config/application.rb"
198
198
  end
@@ -6,7 +6,7 @@
6
6
  <% end %>
7
7
 
8
8
  <%
9
- clean_params = @request.parameters.clone
9
+ clean_params = @request.filtered_parameters.clone
10
10
  clean_params.delete("action")
11
11
  clean_params.delete("controller")
12
12
 
@@ -6,6 +6,7 @@ module ActionDispatch
6
6
  config.action_dispatch = ActiveSupport::OrderedOptions.new
7
7
  config.action_dispatch.x_sendfile_header = ""
8
8
  config.action_dispatch.ip_spoofing_check = true
9
+ config.action_dispatch.show_exceptions = true
9
10
 
10
11
  # Prepare dispatcher callbacks and run 'prepare' callbacks
11
12
  initializer "action_dispatch.prepare_dispatcher" do |app|
@@ -136,20 +136,32 @@ module ActionDispatch
136
136
  #
137
137
  # == HTTP Methods
138
138
  #
139
- # With conditions you can define restrictions on routes. Currently the only valid condition is <tt>:method</tt>.
139
+ # Using the <tt>:via</tt> option when specifying a route allows you to restrict it to a specific HTTP method.
140
+ # Possible values are <tt>:post</tt>, <tt>:get</tt>, <tt>:put</tt>, <tt>:delete</tt> and <tt>:any</tt>.
141
+ # If your route needs to respond to more than one method you can use an array, e.g. <tt>[ :get, :post ]</tt>.
142
+ # The default value is <tt>:any</tt> which means that the route will respond to any of the HTTP methods.
140
143
  #
141
- # * <tt>:method</tt> - Allows you to specify which HTTP method(s) can access the route. Possible values are
142
- # <tt>:post</tt>, <tt>:get</tt>, <tt>:put</tt>, <tt>:delete</tt> and <tt>:any</tt>. Use an array to specify more
143
- # than one method, e.g. <tt>[ :get, :post ]</tt>. The default value is <tt>:any</tt>, <tt>:any</tt> means that any
144
- # method can access the route.
144
+ # Examples:
145
145
  #
146
- # Example:
146
+ # match 'post/:id' => 'posts#show', :via => :get
147
+ # match 'post/:id' => "posts#create_comment', :via => :post
148
+ #
149
+ # Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same
150
+ # URL will route to the <tt>show</tt> action.
151
+ #
152
+ # === HTTP helper methods
147
153
  #
148
- # get 'post/:id' => 'posts#show'
154
+ # An alternative method of specifying which HTTP method a route should respond to is to use the helper
155
+ # methods <tt>get</tt>, <tt>post</tt>, <tt>put</tt> and <tt>delete</tt>.
156
+ #
157
+ # Examples:
158
+ #
159
+ # get 'post/:id' => 'posts#show'
149
160
  # post 'post/:id' => "posts#create_comment'
150
161
  #
151
- # Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same
152
- # URL will route to the <tt>show</tt> action.
162
+ # This syntax is less verbose and the intention is more apparent to someone else reading your code,
163
+ # however if your route needs to respond to more than one HTTP method (or all methods) then using the
164
+ # <tt>:via</tt> option on <tt>match</tt> is preferable.
153
165
  #
154
166
  # == Reloading routes
155
167
  #
@@ -208,11 +220,11 @@ module ActionDispatch
208
220
  autoload :RouteSet, 'action_dispatch/routing/route_set'
209
221
  autoload :UrlFor, 'action_dispatch/routing/url_for'
210
222
 
211
- SEPARATORS = %w( / . ? )
212
- HTTP_METHODS = [:get, :head, :post, :put, :delete, :options]
223
+ SEPARATORS = %w( / . ? ) #:nodoc:
224
+ HTTP_METHODS = [:get, :head, :post, :put, :delete, :options] #:nodoc:
213
225
 
214
226
  # A helper module to hold URL related helpers.
215
- module Helpers
227
+ module Helpers #:nodoc:
216
228
  include ActionController::PolymorphicRoutes
217
229
  end
218
230
  end
@@ -75,7 +75,7 @@ module ActionDispatch
75
75
  # supplying you with methods to create them in your routes.rb file.
76
76
  #
77
77
  # Read more about REST at http://en.wikipedia.org/wiki/Representational_State_Transfer
78
- class DeprecatedMapper #:doc:
78
+ class DeprecatedMapper #:nodoc:
79
79
  def initialize(set) #:nodoc:
80
80
  @set = set
81
81
  end
@@ -4,21 +4,21 @@ require 'active_support/core_ext/object/blank'
4
4
  module ActionDispatch
5
5
  module Routing
6
6
  class Mapper
7
- class Constraints
8
- def self.new(app, constraints = [])
7
+ class Constraints #:nodoc:
8
+ def self.new(app, constraints, request = Rack::Request)
9
9
  if constraints.any?
10
- super(app, constraints)
10
+ super(app, constraints, request)
11
11
  else
12
12
  app
13
13
  end
14
14
  end
15
15
 
16
- def initialize(app, constraints = [])
17
- @app, @constraints = app, constraints
16
+ def initialize(app, constraints, request)
17
+ @app, @constraints, @request = app, constraints, request
18
18
  end
19
19
 
20
20
  def call(env)
21
- req = Rack::Request.new(env)
21
+ req = @request.new(env)
22
22
 
23
23
  @constraints.each { |constraint|
24
24
  if constraint.respond_to?(:matches?) && !constraint.matches?(req)
@@ -32,7 +32,7 @@ module ActionDispatch
32
32
  end
33
33
  end
34
34
 
35
- class Mapping
35
+ class Mapping #:nodoc:
36
36
  IGNORE_OPTIONS = [:to, :as, :controller, :action, :via, :on, :constraints, :defaults, :only, :except, :anchor]
37
37
 
38
38
  def initialize(set, scope, args)
@@ -55,6 +55,14 @@ module ActionDispatch
55
55
  path = args.first
56
56
  end
57
57
 
58
+ if @scope[:module] && options[:to]
59
+ if options[:to].to_s.include?("#")
60
+ options[:to] = "#{@scope[:module]}/#{options[:to]}"
61
+ elsif @scope[:controller].nil?
62
+ options[:to] = "#{@scope[:module]}##{options[:to]}"
63
+ end
64
+ end
65
+
58
66
  path = normalize_path(path)
59
67
 
60
68
  if using_match_shorthand?(path, options)
@@ -83,7 +91,8 @@ module ActionDispatch
83
91
  def app
84
92
  Constraints.new(
85
93
  to.respond_to?(:call) ? to : Routing::RouteSet::Dispatcher.new(:defaults => defaults),
86
- blocks
94
+ blocks,
95
+ @set.request_class
87
96
  )
88
97
  end
89
98
 
@@ -115,11 +124,16 @@ module ActionDispatch
115
124
  controller, action = to.split('#')
116
125
  { :controller => controller, :action => action }
117
126
  when Symbol
118
- { :action => to.to_s }.merge(default_controller ? { :controller => default_controller } : {})
127
+ { :action => to.to_s }
119
128
  else
120
- default_controller ? { :controller => default_controller } : {}
129
+ {}
121
130
  end
122
131
 
132
+ defaults[:controller] ||= default_controller
133
+
134
+ defaults.delete(:controller) if defaults[:controller].blank?
135
+ defaults.delete(:action) if defaults[:action].blank?
136
+
123
137
  if defaults[:controller].blank? && segment_keys.exclude?("controller")
124
138
  raise ArgumentError, "missing :controller"
125
139
  end
@@ -166,7 +180,11 @@ module ActionDispatch
166
180
  end
167
181
 
168
182
  def default_controller
169
- @scope[:controller].to_s if @scope[:controller]
183
+ if @options[:controller]
184
+ @options[:controller].to_s
185
+ elsif @scope[:controller]
186
+ @scope[:controller].to_s
187
+ end
170
188
  end
171
189
  end
172
190
 
@@ -180,7 +198,7 @@ module ActionDispatch
180
198
  end
181
199
 
182
200
  module Base
183
- def initialize(set)
201
+ def initialize(set) #:nodoc:
184
202
  @set = set
185
203
  end
186
204
 
@@ -194,6 +212,21 @@ module ActionDispatch
194
212
  self
195
213
  end
196
214
 
215
+ def mount(app, options = nil)
216
+ if options
217
+ path = options.delete(:at)
218
+ else
219
+ options = app
220
+ app, path = options.find { |k, v| k.respond_to?(:call) }
221
+ options.delete(app) if app
222
+ end
223
+
224
+ raise "A rack application must be specified" unless path
225
+
226
+ match(path, options.merge(:to => app, :anchor => false))
227
+ self
228
+ end
229
+
197
230
  def default_url_options=(options)
198
231
  @set.default_url_options = options
199
232
  end
@@ -227,7 +260,11 @@ module ActionDispatch
227
260
 
228
261
  lambda do |env|
229
262
  req = Request.new(env)
230
- uri = URI.parse(path_proc.call(req.symbolized_path_parameters))
263
+
264
+ params = [req.symbolized_path_parameters]
265
+ params << req if path_proc.arity > 1
266
+
267
+ uri = URI.parse(path_proc.call(*params))
231
268
  uri.scheme ||= req.scheme
232
269
  uri.host ||= req.host
233
270
  uri.port ||= req.port unless req.port == 80
@@ -252,7 +289,7 @@ module ActionDispatch
252
289
  end
253
290
 
254
291
  module Scoping
255
- def initialize(*args)
292
+ def initialize(*args) #:nodoc:
256
293
  @scope = {}
257
294
  super
258
295
  end
@@ -304,7 +341,8 @@ module ActionDispatch
304
341
  end
305
342
 
306
343
  def namespace(path)
307
- scope(path.to_s, :name_prefix => path.to_s, :controller_namespace => path.to_s) { yield }
344
+ path = path.to_s
345
+ scope(:path => path, :name_prefix => path, :module => path) { yield }
308
346
  end
309
347
 
310
348
  def constraints(constraints = {})
@@ -343,15 +381,15 @@ module ActionDispatch
343
381
  parent ? "#{parent}_#{child}" : child
344
382
  end
345
383
 
346
- def merge_controller_namespace_scope(parent, child)
384
+ def merge_module_scope(parent, child)
347
385
  parent ? "#{parent}/#{child}" : child
348
386
  end
349
387
 
350
388
  def merge_controller_scope(parent, child)
351
- @scope[:controller_namespace] ? "#{@scope[:controller_namespace]}/#{child}" : child
389
+ @scope[:module] ? "#{@scope[:module]}/#{child}" : child
352
390
  end
353
391
 
354
- def merge_resources_path_names_scope(parent, child)
392
+ def merge_path_names_scope(parent, child)
355
393
  merge_options_scope(parent, child)
356
394
  end
357
395
 
@@ -373,21 +411,20 @@ module ActionDispatch
373
411
  end
374
412
 
375
413
  module Resources
376
- CRUD_ACTIONS = [:index, :show, :create, :update, :destroy]
414
+ CRUD_ACTIONS = [:index, :show, :create, :update, :destroy] #:nodoc:
377
415
 
378
416
  class Resource #:nodoc:
379
417
  def self.default_actions
380
418
  [:index, :create, :new, :show, :update, :destroy, :edit]
381
419
  end
382
420
 
383
- attr_reader :plural, :singular, :options
421
+ attr_reader :controller, :path, :options
384
422
 
385
423
  def initialize(entities, options = {})
386
- @name = entities.to_s
387
- @options = options
388
-
389
- @plural = @name.pluralize
390
- @singular = @name.singularize
424
+ @name = entities.to_s
425
+ @path = options.delete(:path) || @name
426
+ @controller = options.delete(:controller) || @name.to_s.pluralize
427
+ @options = options
391
428
  end
392
429
 
393
430
  def default_actions
@@ -417,8 +454,12 @@ module ActionDispatch
417
454
  options[:as] || @name
418
455
  end
419
456
 
420
- def controller
421
- options[:controller] || plural
457
+ def plural
458
+ name.to_s.pluralize
459
+ end
460
+
461
+ def singular
462
+ name.to_s.singularize
422
463
  end
423
464
 
424
465
  def member_name
@@ -495,9 +536,9 @@ module ActionDispatch
495
536
  end
496
537
  end
497
538
 
498
- def initialize(*args)
539
+ def initialize(*args) #:nodoc:
499
540
  super
500
- @scope[:resources_path_names] = @set.resources_path_names
541
+ @scope[:path_names] = @set.resources_path_names
501
542
  end
502
543
 
503
544
  def resource(*resources, &block)
@@ -509,7 +550,7 @@ module ActionDispatch
509
550
 
510
551
  resource = SingletonResource.new(resources.pop, options)
511
552
 
512
- scope(:path => resource.name.to_s, :controller => resource.controller) do
553
+ scope(:path => resource.path, :controller => resource.controller) do
513
554
  with_scope_level(:resource, resource) do
514
555
 
515
556
  scope(:name_prefix => resource.name.to_s, :as => "") do
@@ -539,7 +580,7 @@ module ActionDispatch
539
580
 
540
581
  resource = Resource.new(resources.pop, options)
541
582
 
542
- scope(:path => resource.name.to_s, :controller => resource.controller) do
583
+ scope(:path => resource.path, :controller => resource.controller) do
543
584
  with_scope_level(:resources, resource) do
544
585
  yield if block_given?
545
586
 
@@ -603,21 +644,6 @@ module ActionDispatch
603
644
  end
604
645
  end
605
646
 
606
- def mount(app, options = nil)
607
- if options
608
- path = options.delete(:at)
609
- else
610
- options = app
611
- app, path = options.find { |k, v| k.respond_to?(:call) }
612
- options.delete(app) if app
613
- end
614
-
615
- raise "A rack application must be specified" unless path
616
-
617
- match(path, options.merge(:to => app, :anchor => false))
618
- self
619
- end
620
-
621
647
  def match(*args)
622
648
  options = args.extract_options!
623
649
 
@@ -628,7 +654,7 @@ module ActionDispatch
628
654
  return self
629
655
  end
630
656
 
631
- resources_path_names = options.delete(:path_names)
657
+ path_names = options.delete(:path_names)
632
658
 
633
659
  if args.first.is_a?(Symbol)
634
660
  action = args.first
@@ -645,7 +671,7 @@ module ActionDispatch
645
671
  end
646
672
  else
647
673
  with_exclusive_name_prefix(action) do
648
- return match("#{action_path(action, resources_path_names)}(.:format)", options.reverse_merge(:to => action))
674
+ return match("#{action_path(action, path_names)}(.:format)", options.reverse_merge(:to => action))
649
675
  end
650
676
  end
651
677
  end
@@ -667,13 +693,13 @@ module ActionDispatch
667
693
  end
668
694
 
669
695
  protected
670
- def parent_resource
696
+ def parent_resource #:nodoc:
671
697
  @scope[:scope_level_resource]
672
698
  end
673
699
 
674
700
  private
675
701
  def action_path(name, path_names = nil)
676
- path_names ||= @scope[:resources_path_names]
702
+ path_names ||= @scope[:path_names]
677
703
  path_names[name.to_sym] || name.to_s
678
704
  end
679
705
 
@@ -684,7 +710,7 @@ module ActionDispatch
684
710
  end
685
711
 
686
712
  if path_names = options.delete(:path_names)
687
- scope(:resources_path_names => path_names) do
713
+ scope(:path_names => path_names) do
688
714
  send(method, resources.pop, options, &block)
689
715
  end
690
716
  return true