actionpack 3.0.0.rc → 3.0.0.rc2

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 (57) hide show
  1. data/CHANGELOG +11 -2
  2. data/README.rdoc +3 -3
  3. data/lib/abstract_controller/base.rb +13 -1
  4. data/lib/action_controller.rb +2 -1
  5. data/lib/action_controller/base.rb +1 -1
  6. data/lib/action_controller/deprecated/base.rb +11 -1
  7. data/lib/action_controller/deprecated/url_writer.rb +14 -0
  8. data/lib/action_controller/metal/http_authentication.rb +3 -3
  9. data/lib/action_controller/metal/responder.rb +2 -8
  10. data/lib/action_controller/middleware.rb +1 -1
  11. data/lib/action_controller/test_case.rb +1 -1
  12. data/lib/action_controller/vendor/html-scanner/html/document.rb +2 -2
  13. data/lib/action_controller/vendor/html-scanner/html/node.rb +29 -29
  14. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +25 -25
  15. data/lib/action_controller/vendor/html-scanner/html/selector.rb +1 -1
  16. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +8 -8
  17. data/lib/action_dispatch.rb +1 -0
  18. data/lib/action_dispatch/http/cache.rb +2 -2
  19. data/lib/action_dispatch/http/mime_type.rb +10 -10
  20. data/lib/action_dispatch/http/parameters.rb +2 -2
  21. data/lib/action_dispatch/http/request.rb +7 -0
  22. data/lib/action_dispatch/http/url.rb +10 -0
  23. data/lib/action_dispatch/middleware/best_standards_support.rb +22 -0
  24. data/lib/action_dispatch/middleware/cookies.rb +18 -8
  25. data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -5
  26. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +1 -0
  27. data/lib/action_dispatch/middleware/show_exceptions.rb +2 -9
  28. data/lib/action_dispatch/middleware/stack.rb +1 -1
  29. data/lib/action_dispatch/railtie.rb +2 -1
  30. data/lib/action_dispatch/routing.rb +4 -4
  31. data/lib/action_dispatch/routing/deprecated_mapper.rb +1 -0
  32. data/lib/action_dispatch/routing/mapper.rb +34 -21
  33. data/lib/action_dispatch/routing/route_set.rb +8 -5
  34. data/lib/action_dispatch/routing/url_for.rb +1 -1
  35. data/lib/action_dispatch/testing/assertions/routing.rb +37 -14
  36. data/lib/action_dispatch/testing/integration.rb +1 -0
  37. data/lib/action_pack/version.rb +1 -1
  38. data/lib/action_view/base.rb +1 -0
  39. data/lib/action_view/helpers/asset_tag_helper.rb +12 -0
  40. data/lib/action_view/helpers/debug_helper.rb +1 -1
  41. data/lib/action_view/helpers/form_helper.rb +7 -6
  42. data/lib/action_view/helpers/form_options_helper.rb +21 -18
  43. data/lib/action_view/helpers/form_tag_helper.rb +9 -9
  44. data/lib/action_view/helpers/number_helper.rb +1 -1
  45. data/lib/action_view/helpers/raw_output_helper.rb +1 -1
  46. data/lib/action_view/helpers/sanitize_helper.rb +4 -2
  47. data/lib/action_view/helpers/text_helper.rb +5 -2
  48. data/lib/action_view/helpers/translation_helper.rb +9 -9
  49. data/lib/action_view/log_subscriber.rb +1 -1
  50. data/lib/action_view/render/layouts.rb +8 -4
  51. data/lib/action_view/render/partials.rb +1 -1
  52. data/lib/action_view/template/handler.rb +1 -1
  53. data/lib/action_view/template/handlers.rb +1 -1
  54. data/lib/action_view/template/resolver.rb +15 -0
  55. data/lib/action_view/test_case.rb +1 -1
  56. data/lib/action_view/testing/resolvers.rb +1 -1
  57. metadata +18 -16
@@ -182,7 +182,7 @@ module HTML
182
182
  # not another using <tt>:not</tt>. For example:
183
183
  # p:not(.post)
184
184
  # Matches all paragraphs that do not have the class <tt>.post</tt>.
185
- #
185
+ #
186
186
  # === Substitution Values
187
187
  #
188
188
  # You can use substitution with identifiers, class names and element values.
@@ -1,7 +1,7 @@
1
1
  require 'strscan'
2
2
 
3
3
  module HTML #:nodoc:
4
-
4
+
5
5
  # A simple HTML tokenizer. It simply breaks a stream of text into tokens, where each
6
6
  # token is a string. Each string represents either "text", or an HTML element.
7
7
  #
@@ -14,13 +14,13 @@ module HTML #:nodoc:
14
14
  # p token
15
15
  # end
16
16
  class Tokenizer #:nodoc:
17
-
17
+
18
18
  # The current (byte) position in the text
19
19
  attr_reader :position
20
-
20
+
21
21
  # The current line number
22
22
  attr_reader :line
23
-
23
+
24
24
  # Create a new Tokenizer for the given text.
25
25
  def initialize(text)
26
26
  text.encode! if text.encoding_aware?
@@ -42,7 +42,7 @@ module HTML #:nodoc:
42
42
  update_current_line(scan_text)
43
43
  end
44
44
  end
45
-
45
+
46
46
  private
47
47
 
48
48
  # Treat the text at the current position as a tag, and scan it. Supports
@@ -69,13 +69,13 @@ module HTML #:nodoc:
69
69
  def scan_text
70
70
  "#{@scanner.getch}#{@scanner.scan(/[^<]*/)}"
71
71
  end
72
-
72
+
73
73
  # Counts the number of newlines in the text and updates the current line
74
74
  # accordingly.
75
75
  def update_current_line(text)
76
76
  text.scan(/\r?\n/) { @current_line += 1 }
77
77
  end
78
-
78
+
79
79
  # Skips over quoted strings, so that less-than and greater-than characters
80
80
  # within the strings are ignored.
81
81
  def consume_quoted_regions
@@ -103,5 +103,5 @@ module HTML #:nodoc:
103
103
  text
104
104
  end
105
105
  end
106
-
106
+
107
107
  end
@@ -47,6 +47,7 @@ module ActionDispatch
47
47
  end
48
48
 
49
49
  autoload_under 'middleware' do
50
+ autoload :BestStandardsSupport
50
51
  autoload :Callbacks
51
52
  autoload :Cookies
52
53
  autoload :Flash
@@ -113,10 +113,10 @@ module ActionDispatch
113
113
  DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate"
114
114
 
115
115
  def set_conditional_cache_control!
116
- control = @cache_control
117
-
118
116
  return if self["Cache-Control"].present?
119
117
 
118
+ control = @cache_control
119
+
120
120
  if control.empty?
121
121
  headers["Cache-Control"] = DEFAULT_CACHE_CONTROL
122
122
  elsif @cache_control[:no_cache]
@@ -109,10 +109,10 @@ module Mime
109
109
  else
110
110
  # keep track of creation order to keep the subsequent sort stable
111
111
  list = []
112
- accept_header.split(/,/).each_with_index do |header, index|
113
- params, q = header.split(/;\s*q=/)
112
+ accept_header.split(/,/).each_with_index do |header, index|
113
+ params, q = header.split(/;\s*q=/)
114
114
  if params
115
- params.strip!
115
+ params.strip!
116
116
  list << AcceptItem.new(index, params, q) unless params.empty?
117
117
  end
118
118
  end
@@ -161,20 +161,20 @@ module Mime
161
161
  end
162
162
  end
163
163
  end
164
-
164
+
165
165
  def initialize(string, symbol = nil, synonyms = [])
166
166
  @symbol, @synonyms = symbol, synonyms
167
167
  @string = string
168
168
  end
169
-
169
+
170
170
  def to_s
171
171
  @string
172
172
  end
173
-
173
+
174
174
  def to_str
175
175
  to_s
176
176
  end
177
-
177
+
178
178
  def to_sym
179
179
  @symbol || @string.to_sym
180
180
  end
@@ -186,11 +186,11 @@ module Mime
186
186
  super
187
187
  end
188
188
  end
189
-
189
+
190
190
  def ==(mime_type)
191
191
  return false if mime_type.blank?
192
- (@synonyms + [ self ]).any? do |synonym|
193
- synonym.to_s == mime_type.to_s || synonym.to_sym == mime_type.to_sym
192
+ (@synonyms + [ self ]).any? do |synonym|
193
+ synonym.to_s == mime_type.to_s || synonym.to_sym == mime_type.to_sym
194
194
  end
195
195
  end
196
196
 
@@ -15,14 +15,14 @@ module ActionDispatch
15
15
  alias :params :parameters
16
16
 
17
17
  def path_parameters=(parameters) #:nodoc:
18
- @env.delete("action_dispatch.request.symbolized_path_parameters")
18
+ @_symbolized_path_params = nil
19
19
  @env.delete("action_dispatch.request.parameters")
20
20
  @env["action_dispatch.request.path_parameters"] = parameters
21
21
  end
22
22
 
23
23
  # The same as <tt>path_parameters</tt> with explicitly symbolized keys.
24
24
  def symbolized_path_parameters
25
- @env["action_dispatch.request.symbolized_path_parameters"] ||= path_parameters.symbolize_keys
25
+ @_symbolized_path_params ||= path_parameters.symbolize_keys
26
26
  end
27
27
 
28
28
  # Returns a hash with the \parameters used to form the \path of the request.
@@ -15,6 +15,8 @@ module ActionDispatch
15
15
  include ActionDispatch::Http::Upload
16
16
  include ActionDispatch::Http::URL
17
17
 
18
+ LOCALHOST = [/^127\.0\.0\.\d{1,3}$/, "::1", /^0:0:0:0:0:0:0:1(%.*)?$/].freeze
19
+
18
20
  %w[ AUTH_TYPE GATEWAY_INTERFACE
19
21
  PATH_TRANSLATED REMOTE_HOST
20
22
  REMOTE_IDENT REMOTE_USER REMOTE_ADDR
@@ -231,5 +233,10 @@ module ActionDispatch
231
233
  @env['X_HTTP_AUTHORIZATION'] ||
232
234
  @env['REDIRECT_X_HTTP_AUTHORIZATION']
233
235
  end
236
+
237
+ # True if the request came from localhost, 127.0.0.1.
238
+ def local?
239
+ LOCALHOST.any? { |local_ip| local_ip === remote_addr && local_ip === remote_ip }
240
+ end
234
241
  end
235
242
  end
@@ -6,6 +6,11 @@ module ActionDispatch
6
6
  protocol + host_with_port + fullpath
7
7
  end
8
8
 
9
+ # Returns 'https' if this is an SSL request and 'http' otherwise.
10
+ def scheme
11
+ ssl? ? 'https' : 'http'
12
+ end
13
+
9
14
  # Returns 'https://' if this is an SSL request and 'http://' otherwise.
10
15
  def protocol
11
16
  ssl? ? 'https://' : 'http://'
@@ -53,6 +58,11 @@ module ActionDispatch
53
58
  end
54
59
  end
55
60
 
61
+ # Returns whether this request is using the standard port
62
+ def standard_port?
63
+ port == standard_port
64
+ end
65
+
56
66
  # Returns a \port suffix like ":8080" if the \port number of this request
57
67
  # is not the default HTTP \port 80 or HTTPS \port 443.
58
68
  def port_string
@@ -0,0 +1,22 @@
1
+ module ActionDispatch
2
+ class BestStandardsSupport
3
+ def initialize(app, type = true)
4
+ @app = app
5
+
6
+ @header = case type
7
+ when true
8
+ "IE=Edge,chrome=1"
9
+ when :builtin
10
+ "IE=Edge"
11
+ when false
12
+ nil
13
+ end
14
+ end
15
+
16
+ def call(env)
17
+ status, headers, body = @app.call(env)
18
+ headers["X-UA-Compatible"] = @header
19
+ [status, headers, body]
20
+ end
21
+ end
22
+ end
@@ -69,16 +69,26 @@ module ActionDispatch
69
69
 
70
70
  class CookieJar < Hash #:nodoc:
71
71
 
72
- # This regular expression is used to split the levels of a domain
73
- # So www.example.co.uk gives:
74
- # $1 => www.
75
- # $2 => example
76
- # $3 => co.uk
77
- DOMAIN_REGEXP = /^(.*\.)*(.*)\.(...|...\...|....|..\...|..)$/
72
+ # This regular expression is used to split the levels of a domain.
73
+ # The top level domain can be any string without a period or
74
+ # **.**, ***.** style TLDs like co.uk or com.au
75
+ #
76
+ # www.example.co.uk gives:
77
+ # $1 => example
78
+ # $2 => co.uk
79
+ #
80
+ # example.com gives:
81
+ # $1 => example
82
+ # $2 => com
83
+ #
84
+ # lots.of.subdomains.example.local gives:
85
+ # $1 => example
86
+ # $2 => local
87
+ DOMAIN_REGEXP = /([^.]*)\.([^.]*|..\...|...\...)$/
78
88
 
79
89
  def self.build(request)
80
90
  secret = request.env[TOKEN_KEY]
81
- host = request.env["HTTP_HOST"]
91
+ host = request.host
82
92
 
83
93
  new(secret, host).tap do |hash|
84
94
  hash.update(request.cookies)
@@ -104,7 +114,7 @@ module ActionDispatch
104
114
 
105
115
  if options[:domain] == :all
106
116
  @host =~ DOMAIN_REGEXP
107
- options[:domain] = ".#{$2}.#{$3}"
117
+ options[:domain] = ".#{$1}.#{$2}"
108
118
  end
109
119
  end
110
120
 
@@ -191,11 +191,8 @@ module ActionDispatch
191
191
 
192
192
  def load_session(env)
193
193
  stale_session_check! do
194
- if sid = current_session_id(env)
195
- sid, session = get_session(env, sid)
196
- else
197
- sid, session = generate_sid, {}
198
- end
194
+ sid = current_session_id(env)
195
+ sid, session = get_session(env, sid)
199
196
  [sid, session]
200
197
  end
201
198
  end
@@ -25,6 +25,7 @@ module ActionDispatch
25
25
 
26
26
  private
27
27
  def get_session(env, sid)
28
+ sid ||= generate_sid
28
29
  begin
29
30
  session = @pool.get(sid) || {}
30
31
  rescue MemCache::MemCacheError, Errno::ECONNREFUSED
@@ -6,8 +6,6 @@ module ActionDispatch
6
6
  # This middleware rescues any exception returned by the application and renders
7
7
  # nice exception pages if it's being rescued locally.
8
8
  class ShowExceptions
9
- LOCALHOST = [/^127\.0\.0\.\d{1,3}$/, "::1", /^0:0:0:0:0:0:0:1(%.*)?$/].freeze
10
-
11
9
  RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates')
12
10
 
13
11
  cattr_accessor :rescue_responses
@@ -66,7 +64,7 @@ module ActionDispatch
66
64
  log_error(exception)
67
65
 
68
66
  request = Request.new(env)
69
- if @consider_all_requests_local || local_request?(request)
67
+ if @consider_all_requests_local || request.local?
70
68
  rescue_action_locally(request, exception)
71
69
  else
72
70
  rescue_action_in_public(exception)
@@ -112,11 +110,6 @@ module ActionDispatch
112
110
  end
113
111
  end
114
112
 
115
- # True if the request came from localhost, 127.0.0.1.
116
- def local_request?(request)
117
- LOCALHOST.any? { |local_ip| local_ip === request.remote_addr && local_ip === request.remote_ip }
118
- end
119
-
120
113
  def status_code(exception)
121
114
  Rack::Utils.status_code(@@rescue_responses[exception.class.name])
122
115
  end
@@ -134,7 +127,7 @@ module ActionDispatch
134
127
 
135
128
  ActiveSupport::Deprecation.silence do
136
129
  message = "\n#{exception.class} (#{exception.message}):\n"
137
- message << exception.annoted_source_code if exception.respond_to?(:annoted_source_code)
130
+ message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
138
131
  message << " " << application_trace(exception).join("\n ")
139
132
  logger.fatal("#{message}\n\n")
140
133
  end
@@ -69,7 +69,7 @@ module ActionDispatch
69
69
  end
70
70
 
71
71
  def active
72
- ActiveSupport::Deprecation.warn "All middlewares in the chain are active since the laziness " <<
72
+ ActiveSupport::Deprecation.warn "All middlewares in the chain are active since the laziness " <<
73
73
  "was removed from the middleware stack", caller
74
74
  end
75
75
 
@@ -7,10 +7,11 @@ module ActionDispatch
7
7
  config.action_dispatch.x_sendfile_header = ""
8
8
  config.action_dispatch.ip_spoofing_check = true
9
9
  config.action_dispatch.show_exceptions = true
10
+ config.action_dispatch.best_standards_support = true
10
11
 
11
12
  # Prepare dispatcher callbacks and run 'prepare' callbacks
12
13
  initializer "action_dispatch.prepare_dispatcher" do |app|
13
14
  ActionDispatch::Callbacks.to_prepare { app.routes_reloader.execute_if_updated }
14
15
  end
15
16
  end
16
- end
17
+ end
@@ -15,7 +15,7 @@ module ActionDispatch
15
15
  # match ':controller(/:action(/:id(.:format)))'
16
16
  #
17
17
  # This route states that it expects requests to consist of a
18
- # <tt>:controller</tt> followed optionally by an <tt>:action</tt> that in
18
+ # <tt>:controller</tt> followed optionally by an <tt>:action</tt> that in
19
19
  # turn is followed optionally by an <tt>:id</tt>, which in turn is followed
20
20
  # optionally by a <tt>:format</tt>
21
21
  #
@@ -134,8 +134,8 @@ module ActionDispatch
134
134
  # == HTTP Methods
135
135
  #
136
136
  # Using the <tt>:via</tt> option when specifying a route allows you to restrict it to a specific HTTP method.
137
- # Possible values are <tt>:post</tt>, <tt>:get</tt>, <tt>:put</tt>, <tt>:delete</tt> and <tt>:any</tt>.
138
- # If your route needs to respond to more than one method you can use an array, e.g. <tt>[ :get, :post ]</tt>.
137
+ # Possible values are <tt>:post</tt>, <tt>:get</tt>, <tt>:put</tt>, <tt>:delete</tt> and <tt>:any</tt>.
138
+ # If your route needs to respond to more than one method you can use an array, e.g. <tt>[ :get, :post ]</tt>.
139
139
  # The default value is <tt>:any</tt> which means that the route will respond to any of the HTTP methods.
140
140
  #
141
141
  # Examples:
@@ -144,7 +144,7 @@ module ActionDispatch
144
144
  # match 'post/:id' => "posts#create_comment', :via => :post
145
145
  #
146
146
  # Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same
147
- # URL will route to the <tt>show</tt> action.
147
+ # URL will route to the <tt>show</tt> action.
148
148
  #
149
149
  # === HTTP helper methods
150
150
  #
@@ -1,5 +1,6 @@
1
1
  require 'active_support/core_ext/object/blank'
2
2
  require 'active_support/core_ext/object/with_options'
3
+ require 'active_support/core_ext/object/try'
3
4
 
4
5
  module ActionDispatch
5
6
  module Routing
@@ -1,3 +1,4 @@
1
+ require 'erb'
1
2
  require 'active_support/core_ext/hash/except'
2
3
  require 'active_support/core_ext/object/blank'
3
4
 
@@ -25,13 +26,18 @@ module ActionDispatch
25
26
  @constraints.each { |constraint|
26
27
  if constraint.respond_to?(:matches?) && !constraint.matches?(req)
27
28
  return [ 404, {'X-Cascade' => 'pass'}, [] ]
28
- elsif constraint.respond_to?(:call) && !constraint.call(req)
29
+ elsif constraint.respond_to?(:call) && !constraint.call(*constraint_args(constraint, req))
29
30
  return [ 404, {'X-Cascade' => 'pass'}, [] ]
30
31
  end
31
32
  }
32
33
 
33
34
  @app.call(env)
34
35
  end
36
+
37
+ private
38
+ def constraint_args(constraint, request)
39
+ constraint.arity == 1 ? [request] : [request.symbolized_path_parameters, request]
40
+ end
35
41
  end
36
42
 
37
43
  class Mapping #:nodoc:
@@ -277,7 +283,6 @@ module ActionDispatch
277
283
  path = args.shift || block
278
284
  path_proc = path.is_a?(Proc) ? path : proc { |params| path % params }
279
285
  status = options[:status] || 301
280
- body = 'Moved Permanently'
281
286
 
282
287
  lambda do |env|
283
288
  req = Request.new(env)
@@ -288,13 +293,16 @@ module ActionDispatch
288
293
  uri = URI.parse(path_proc.call(*params))
289
294
  uri.scheme ||= req.scheme
290
295
  uri.host ||= req.host
291
- uri.port ||= req.port unless req.port == 80
296
+ uri.port ||= req.port unless req.standard_port?
297
+
298
+ body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
292
299
 
293
300
  headers = {
294
301
  'Location' => uri.to_s,
295
302
  'Content-Type' => 'text/html',
296
303
  'Content-Length' => body.length.to_s
297
304
  }
305
+
298
306
  [ status, headers, [body] ]
299
307
  end
300
308
  end
@@ -470,7 +478,7 @@ module ActionDispatch
470
478
 
471
479
  def initialize(entities, options = {})
472
480
  @name = entities.to_s
473
- @path = options.delete(:path) || @name
481
+ @path = (options.delete(:path) || @name).to_s
474
482
  @controller = (options.delete(:controller) || @name).to_s
475
483
  @as = options.delete(:as)
476
484
  @options = options
@@ -495,16 +503,14 @@ module ActionDispatch
495
503
  end
496
504
 
497
505
  def plural
498
- name.to_s.pluralize
506
+ @plural ||= name.to_s
499
507
  end
500
508
 
501
509
  def singular
502
- name.to_s.singularize
510
+ @singular ||= name.to_s.singularize
503
511
  end
504
512
 
505
- def member_name
506
- singular
507
- end
513
+ alias :member_name :singular
508
514
 
509
515
  # Checks for uncountable plurals, and appends "_index" if they're.
510
516
  def collection_name
@@ -515,9 +521,7 @@ module ActionDispatch
515
521
  { :controller => controller }
516
522
  end
517
523
 
518
- def collection_scope
519
- path
520
- end
524
+ alias :collection_scope :path
521
525
 
522
526
  def member_scope
523
527
  "#{path}/:id"
@@ -538,21 +542,25 @@ module ActionDispatch
538
542
 
539
543
  def initialize(entities, options)
540
544
  @name = entities.to_s
541
- @path = options.delete(:path) || @name
545
+ @path = (options.delete(:path) || @name).to_s
542
546
  @controller = (options.delete(:controller) || plural).to_s
543
547
  @as = options.delete(:as)
544
548
  @options = options
545
549
  end
546
550
 
547
- def member_name
548
- name
551
+ def plural
552
+ @plural ||= name.to_s.pluralize
549
553
  end
550
- alias :collection_name :member_name
551
554
 
552
- def member_scope
553
- path
555
+ def singular
556
+ @singular ||= name.to_s
554
557
  end
555
- alias :nested_scope :member_scope
558
+
559
+ alias :member_name :singular
560
+ alias :collection_name :singular
561
+
562
+ alias :member_scope :path
563
+ alias :nested_scope :path
556
564
  end
557
565
 
558
566
  def initialize(*args) #:nodoc:
@@ -583,10 +591,10 @@ module ActionDispatch
583
591
  end if parent_resource.actions.include?(:new)
584
592
 
585
593
  member_scope do
594
+ get :edit if parent_resource.actions.include?(:edit)
586
595
  get :show if parent_resource.actions.include?(:show)
587
596
  put :update if parent_resource.actions.include?(:update)
588
597
  delete :destroy if parent_resource.actions.include?(:destroy)
589
- get :edit if parent_resource.actions.include?(:edit)
590
598
  end
591
599
  end
592
600
 
@@ -613,10 +621,10 @@ module ActionDispatch
613
621
  end if parent_resource.actions.include?(:new)
614
622
 
615
623
  member_scope do
624
+ get :edit if parent_resource.actions.include?(:edit)
616
625
  get :show if parent_resource.actions.include?(:show)
617
626
  put :update if parent_resource.actions.include?(:update)
618
627
  delete :destroy if parent_resource.actions.include?(:destroy)
619
- get :edit if parent_resource.actions.include?(:edit)
620
628
  end
621
629
  end
622
630
 
@@ -728,6 +736,7 @@ module ActionDispatch
728
736
  end
729
737
  elsif resource_method_scope?
730
738
  path = path_for_custom_action
739
+ options[:action] ||= action
731
740
  options[:as] = name_for_action(options[:as]) if options[:as]
732
741
  args.push(options)
733
742
 
@@ -770,6 +779,10 @@ module ActionDispatch
770
779
  return true
771
780
  end
772
781
 
782
+ options.keys.each do |k|
783
+ (options[:constraints] ||= {})[k] = options.delete(k) if options[k].is_a?(Regexp)
784
+ end
785
+
773
786
  scope_options = options.slice!(*RESOURCE_OPTIONS)
774
787
  unless scope_options.empty?
775
788
  scope(scope_options) do