actionpack 3.0.0.rc2 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- data/CHANGELOG +4 -18
- data/lib/abstract_controller.rb +1 -0
- data/lib/abstract_controller/base.rb +15 -13
- data/lib/abstract_controller/callbacks.rb +22 -23
- data/lib/abstract_controller/helpers.rb +24 -7
- data/lib/abstract_controller/layouts.rb +12 -10
- data/lib/abstract_controller/view_paths.rb +7 -7
- data/lib/action_controller/base.rb +164 -1
- data/lib/action_controller/test_case.rb +1 -0
- data/lib/action_dispatch/http/mime_negotiation.rb +1 -1
- data/lib/action_dispatch/http/parameters.rb +2 -2
- data/lib/action_dispatch/http/url.rb +2 -2
- data/lib/action_dispatch/middleware/cookies.rb +11 -2
- data/lib/action_dispatch/middleware/flash.rb +7 -2
- data/lib/action_dispatch/routing/mapper.rb +87 -117
- data/lib/action_dispatch/routing/polymorphic_routes.rb +10 -10
- data/lib/action_dispatch/testing/assertions/dom.rb +1 -1
- data/lib/action_dispatch/testing/assertions/response.rb +5 -5
- data/lib/action_dispatch/testing/assertions/routing.rb +2 -2
- data/lib/action_pack/version.rb +1 -2
- data/lib/action_view/helpers/capture_helper.rb +1 -1
- data/lib/action_view/helpers/date_helper.rb +13 -17
- data/lib/action_view/helpers/form_helper.rb +12 -7
- data/lib/action_view/helpers/url_helper.rb +2 -2
- data/lib/action_view/template.rb +3 -3
- metadata +12 -17
@@ -167,6 +167,7 @@ module ActionController
|
|
167
167
|
@formats = nil
|
168
168
|
@env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
|
169
169
|
@env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
|
170
|
+
@symbolized_path_params = nil
|
170
171
|
@method = @request_method = nil
|
171
172
|
@fullpath = @ip = @remote_ip = nil
|
172
173
|
@env['action_dispatch.request.query_parameters'] = {}
|
@@ -32,7 +32,7 @@ module ActionDispatch
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
# Returns the
|
35
|
+
# Returns the MIME type for the \format used in the request.
|
36
36
|
#
|
37
37
|
# GET /posts/5.xml | request.format => Mime::XML
|
38
38
|
# GET /posts/5.xhtml | request.format => Mime::HTML
|
@@ -15,14 +15,14 @@ module ActionDispatch
|
|
15
15
|
alias :params :parameters
|
16
16
|
|
17
17
|
def path_parameters=(parameters) #:nodoc:
|
18
|
-
@
|
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
|
-
@
|
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.
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module ActionDispatch
|
2
2
|
module Http
|
3
3
|
module URL
|
4
|
-
# Returns the complete URL used for this request.
|
4
|
+
# Returns the complete \URL used for this request.
|
5
5
|
def url
|
6
6
|
protocol + host_with_port + fullpath
|
7
7
|
end
|
@@ -96,7 +96,7 @@ module ActionDispatch
|
|
96
96
|
end
|
97
97
|
|
98
98
|
# Returns the request URI, accounting for server idiosyncrasies.
|
99
|
-
# WEBrick includes the full URL. IIS leaves REQUEST_URI blank.
|
99
|
+
# WEBrick includes the full \URL. IIS leaves REQUEST_URI blank.
|
100
100
|
def request_uri
|
101
101
|
ActiveSupport::Deprecation.warn "Using #request_uri is deprecated. Use fullpath instead.", caller
|
102
102
|
fullpath
|
@@ -7,7 +7,7 @@ module ActionDispatch
|
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
-
# Cookies are read and written through ActionController#cookies.
|
10
|
+
# \Cookies are read and written through ActionController#cookies.
|
11
11
|
#
|
12
12
|
# The cookies being read are the ones received along with the request, the cookies
|
13
13
|
# being written will be sent out with the response. Reading a cookie does not get
|
@@ -21,6 +21,15 @@ module ActionDispatch
|
|
21
21
|
# # Sets a cookie that expires in 1 hour.
|
22
22
|
# cookies[:login] = { :value => "XJ-122", :expires => 1.hour.from_now }
|
23
23
|
#
|
24
|
+
# # Sets a signed cookie, which prevents a user from tampering with its value.
|
25
|
+
# # You must specify a value in ActionController::Base.cookie_verifier_secret.
|
26
|
+
# cookies.signed[:remember_me] = [current_user.id, current_user.salt]
|
27
|
+
#
|
28
|
+
# # Sets a "permanent" cookie (which expires in 20 years from now).
|
29
|
+
# cookies.permanent[:login] = "XJ-122"
|
30
|
+
# # You can also chain these methods:
|
31
|
+
# cookies.permanent.signed[:login] = "XJ-122"
|
32
|
+
#
|
24
33
|
# Examples for reading:
|
25
34
|
#
|
26
35
|
# cookies[:user_name] # => "david"
|
@@ -55,7 +64,7 @@ module ActionDispatch
|
|
55
64
|
# :domain => :all # Allow the cookie for the top most level
|
56
65
|
# domain and subdomains.
|
57
66
|
#
|
58
|
-
# * <tt>:expires</tt> - The time at which this cookie expires, as a Time object.
|
67
|
+
# * <tt>:expires</tt> - The time at which this cookie expires, as a \Time object.
|
59
68
|
# * <tt>:secure</tt> - Whether this cookie is a only transmitted to HTTPS servers.
|
60
69
|
# Default is +false+.
|
61
70
|
# * <tt>:httponly</tt> - Whether this cookie is accessible via scripting or
|
@@ -10,13 +10,13 @@ module ActionDispatch
|
|
10
10
|
|
11
11
|
# The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed
|
12
12
|
# to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
|
13
|
-
# action that sets <tt>flash[:notice] = "
|
13
|
+
# action that sets <tt>flash[:notice] = "Post successfully created"</tt> before redirecting to a display action that can
|
14
14
|
# then expose the flash to its template. Actually, that exposure is automatically done. Example:
|
15
15
|
#
|
16
16
|
# class PostsController < ActionController::Base
|
17
17
|
# def create
|
18
18
|
# # save post
|
19
|
-
# flash[:notice] = "
|
19
|
+
# flash[:notice] = "Post successfully created"
|
20
20
|
# redirect_to posts_path(@post)
|
21
21
|
# end
|
22
22
|
#
|
@@ -30,6 +30,11 @@ module ActionDispatch
|
|
30
30
|
# <div class="notice"><%= flash[:notice] %></div>
|
31
31
|
# <% end %>
|
32
32
|
#
|
33
|
+
# Since the +notice+ and +alert+ keys are a common idiom, convenience accessors are available:
|
34
|
+
#
|
35
|
+
# flash.alert = "You must be logged in"
|
36
|
+
# flash.notice = "Post successfully created"
|
37
|
+
#
|
33
38
|
# This example just places a string in the flash, but you can put any object in there. And of course, you can put as
|
34
39
|
# many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed.
|
35
40
|
#
|
@@ -43,9 +43,10 @@ module ActionDispatch
|
|
43
43
|
class Mapping #:nodoc:
|
44
44
|
IGNORE_OPTIONS = [:to, :as, :via, :on, :constraints, :defaults, :only, :except, :anchor, :shallow, :shallow_path, :shallow_prefix]
|
45
45
|
|
46
|
-
def initialize(set, scope,
|
47
|
-
@set, @scope
|
48
|
-
@
|
46
|
+
def initialize(set, scope, path, options)
|
47
|
+
@set, @scope = set, scope
|
48
|
+
@options = (@scope[:options] || {}).merge(options)
|
49
|
+
@path = normalize_path(path)
|
49
50
|
normalize_options!
|
50
51
|
end
|
51
52
|
|
@@ -54,28 +55,6 @@ module ActionDispatch
|
|
54
55
|
end
|
55
56
|
|
56
57
|
private
|
57
|
-
def extract_path_and_options(args)
|
58
|
-
options = args.extract_options!
|
59
|
-
|
60
|
-
if using_to_shorthand?(args, options)
|
61
|
-
path, to = options.find { |name, value| name.is_a?(String) }
|
62
|
-
options.merge!(:to => to).delete(path) if path
|
63
|
-
else
|
64
|
-
path = args.first
|
65
|
-
end
|
66
|
-
|
67
|
-
if path.match(':controller')
|
68
|
-
raise ArgumentError, ":controller segment is not allowed within a namespace block" if @scope[:module]
|
69
|
-
|
70
|
-
# Add a default constraint for :controller path segments that matches namespaced
|
71
|
-
# controllers with default routes like :controller/:action/:id(.:format), e.g:
|
72
|
-
# GET /admin/products/show/1
|
73
|
-
# => { :controller => 'admin/products', :action => 'show', :id => '1' }
|
74
|
-
options.reverse_merge!(:controller => /.+?/)
|
75
|
-
end
|
76
|
-
|
77
|
-
[ normalize_path(path), options ]
|
78
|
-
end
|
79
58
|
|
80
59
|
def normalize_options!
|
81
60
|
path_without_format = @path.sub(/\(\.:format\)$/, '')
|
@@ -83,25 +62,39 @@ module ActionDispatch
|
|
83
62
|
if using_match_shorthand?(path_without_format, @options)
|
84
63
|
to_shorthand = @options[:to].blank?
|
85
64
|
@options[:to] ||= path_without_format[1..-1].sub(%r{/([^/]*)$}, '#\1')
|
86
|
-
@options[:as] ||=
|
65
|
+
@options[:as] ||= Mapper.normalize_name(path_without_format)
|
87
66
|
end
|
88
67
|
|
89
68
|
@options.merge!(default_controller_and_action(to_shorthand))
|
90
69
|
end
|
91
70
|
|
92
|
-
# match "account" => "account#index"
|
93
|
-
def using_to_shorthand?(args, options)
|
94
|
-
args.empty? && options.present?
|
95
|
-
end
|
96
|
-
|
97
71
|
# match "account/overview"
|
98
72
|
def using_match_shorthand?(path, options)
|
99
73
|
path && options.except(:via, :anchor, :to, :as).empty? && path =~ %r{^/[\w\/]+$}
|
100
74
|
end
|
101
75
|
|
102
76
|
def normalize_path(path)
|
103
|
-
raise ArgumentError, "path is required" if
|
104
|
-
Mapper.normalize_path(
|
77
|
+
raise ArgumentError, "path is required" if path.blank?
|
78
|
+
path = Mapper.normalize_path(path)
|
79
|
+
|
80
|
+
if path.match(':controller')
|
81
|
+
raise ArgumentError, ":controller segment is not allowed within a namespace block" if @scope[:module]
|
82
|
+
|
83
|
+
# Add a default constraint for :controller path segments that matches namespaced
|
84
|
+
# controllers with default routes like :controller/:action/:id(.:format), e.g:
|
85
|
+
# GET /admin/products/show/1
|
86
|
+
# => { :controller => 'admin/products', :action => 'show', :id => '1' }
|
87
|
+
@options.reverse_merge!(:controller => /.+?/)
|
88
|
+
end
|
89
|
+
|
90
|
+
if @options[:format] == false
|
91
|
+
@options.delete(:format)
|
92
|
+
path
|
93
|
+
elsif path.include?(":format")
|
94
|
+
path
|
95
|
+
else
|
96
|
+
"#{path}(.:format)"
|
97
|
+
end
|
105
98
|
end
|
106
99
|
|
107
100
|
def app
|
@@ -224,6 +217,10 @@ module ActionDispatch
|
|
224
217
|
path
|
225
218
|
end
|
226
219
|
|
220
|
+
def self.normalize_name(name)
|
221
|
+
normalize_path(name)[1..-1].gsub("/", "_")
|
222
|
+
end
|
223
|
+
|
227
224
|
module Base
|
228
225
|
def initialize(set) #:nodoc:
|
229
226
|
@set = set
|
@@ -233,8 +230,8 @@ module ActionDispatch
|
|
233
230
|
match '/', options.reverse_merge(:as => :root)
|
234
231
|
end
|
235
232
|
|
236
|
-
def match(
|
237
|
-
mapping = Mapping.new(@set, @scope,
|
233
|
+
def match(path, options=nil)
|
234
|
+
mapping = Mapping.new(@set, @scope, path, options || {}).to_route
|
238
235
|
@set.add_route(*mapping)
|
239
236
|
self
|
240
237
|
end
|
@@ -250,7 +247,7 @@ module ActionDispatch
|
|
250
247
|
|
251
248
|
raise "A rack application must be specified" unless path
|
252
249
|
|
253
|
-
match(path, options.merge(:to => app, :anchor => false))
|
250
|
+
match(path, options.merge(:to => app, :anchor => false, :format => false))
|
254
251
|
self
|
255
252
|
end
|
256
253
|
|
@@ -332,13 +329,7 @@ module ActionDispatch
|
|
332
329
|
ActiveSupport::Deprecation.warn ":name_prefix was deprecated in the new router syntax. Use :as instead.", caller
|
333
330
|
end
|
334
331
|
|
335
|
-
|
336
|
-
when String
|
337
|
-
options[:path] = args.first
|
338
|
-
when Symbol
|
339
|
-
options[:controller] = args.first
|
340
|
-
end
|
341
|
-
|
332
|
+
options[:path] = args.first if args.first.is_a?(String)
|
342
333
|
recover = {}
|
343
334
|
|
344
335
|
options[:constraints] ||= {}
|
@@ -370,8 +361,9 @@ module ActionDispatch
|
|
370
361
|
@scope[:blocks] = recover[:block]
|
371
362
|
end
|
372
363
|
|
373
|
-
def controller(controller)
|
374
|
-
|
364
|
+
def controller(controller, options={})
|
365
|
+
options[:controller] = controller
|
366
|
+
scope(options) { yield }
|
375
367
|
end
|
376
368
|
|
377
369
|
def namespace(path, options = {})
|
@@ -389,21 +381,6 @@ module ActionDispatch
|
|
389
381
|
scope(:defaults => defaults) { yield }
|
390
382
|
end
|
391
383
|
|
392
|
-
def match(*args)
|
393
|
-
options = args.extract_options!
|
394
|
-
|
395
|
-
options = (@scope[:options] || {}).merge(options)
|
396
|
-
|
397
|
-
if @scope[:as] && !options[:as].blank?
|
398
|
-
options[:as] = "#{@scope[:as]}_#{options[:as]}"
|
399
|
-
elsif @scope[:as] && options[:as] == ""
|
400
|
-
options[:as] = @scope[:as].to_s
|
401
|
-
end
|
402
|
-
|
403
|
-
args.push(options)
|
404
|
-
super(*args)
|
405
|
-
end
|
406
|
-
|
407
384
|
private
|
408
385
|
def scope_options
|
409
386
|
@scope_options ||= private_methods.grep(/^merge_(.+)_scope$/) { $1.to_sym }
|
@@ -467,9 +444,9 @@ module ActionDispatch
|
|
467
444
|
module Resources
|
468
445
|
# CANONICAL_ACTIONS holds all actions that does not need a prefix or
|
469
446
|
# a path appended since they fit properly in their scope level.
|
470
|
-
VALID_ON_OPTIONS
|
471
|
-
|
472
|
-
|
447
|
+
VALID_ON_OPTIONS = [:new, :collection, :member]
|
448
|
+
RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except]
|
449
|
+
CANONICAL_ACTIONS = %w(index create new show update destroy)
|
473
450
|
|
474
451
|
class Resource #:nodoc:
|
475
452
|
DEFAULT_ACTIONS = [:index, :create, :new, :show, :update, :destroy, :edit]
|
@@ -718,47 +695,31 @@ module ActionDispatch
|
|
718
695
|
raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
|
719
696
|
end
|
720
697
|
|
721
|
-
if @scope[:scope_level] == :
|
698
|
+
if @scope[:scope_level] == :resources
|
699
|
+
args.push(options)
|
700
|
+
return nested { match(*args) }
|
701
|
+
elsif @scope[:scope_level] == :resource
|
722
702
|
args.push(options)
|
723
703
|
return member { match(*args) }
|
724
704
|
end
|
725
705
|
|
726
|
-
path = options.delete(:path)
|
727
706
|
action = args.first
|
707
|
+
path = path_for_action(action, options.delete(:path))
|
728
708
|
|
729
|
-
if action.
|
730
|
-
|
731
|
-
options[:
|
732
|
-
|
733
|
-
|
734
|
-
with_exclusive_scope do
|
735
|
-
return super(path, options)
|
736
|
-
end
|
737
|
-
elsif resource_method_scope?
|
738
|
-
path = path_for_custom_action
|
739
|
-
options[:action] ||= action
|
740
|
-
options[:as] = name_for_action(options[:as]) if options[:as]
|
741
|
-
args.push(options)
|
742
|
-
|
743
|
-
with_exclusive_scope do
|
744
|
-
scope(path) do
|
745
|
-
return super
|
746
|
-
end
|
747
|
-
end
|
748
|
-
end
|
749
|
-
|
750
|
-
if resource_scope?
|
751
|
-
raise ArgumentError, "can't define route directly in resource(s) scope"
|
709
|
+
if action.to_s =~ /^[\w\/]+$/
|
710
|
+
options[:action] ||= action unless action.to_s.include?("/")
|
711
|
+
options[:as] = name_for_action(action, options[:as])
|
712
|
+
else
|
713
|
+
options[:as] = name_for_action(options[:as])
|
752
714
|
end
|
753
715
|
|
754
|
-
|
755
|
-
super
|
716
|
+
super(path, options)
|
756
717
|
end
|
757
718
|
|
758
719
|
def root(options={})
|
759
720
|
if @scope[:scope_level] == :resources
|
760
|
-
with_scope_level(:
|
761
|
-
scope(parent_resource.path
|
721
|
+
with_scope_level(:root) do
|
722
|
+
scope(parent_resource.path) do
|
762
723
|
super(options)
|
763
724
|
end
|
764
725
|
end
|
@@ -895,7 +856,7 @@ module ActionDispatch
|
|
895
856
|
end
|
896
857
|
|
897
858
|
def canonical_action?(action, flag)
|
898
|
-
flag && CANONICAL_ACTIONS.include?(action)
|
859
|
+
flag && resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s)
|
899
860
|
end
|
900
861
|
|
901
862
|
def shallow_scoping?
|
@@ -906,18 +867,10 @@ module ActionDispatch
|
|
906
867
|
prefix = shallow_scoping? ?
|
907
868
|
"#{@scope[:shallow_path]}/#{parent_resource.path}/:id" : @scope[:path]
|
908
869
|
|
909
|
-
if canonical_action?(action, path.blank?)
|
910
|
-
|
870
|
+
path = if canonical_action?(action, path.blank?)
|
871
|
+
prefix.to_s
|
911
872
|
else
|
912
|
-
"#{prefix}/#{action_path(action, path)}
|
913
|
-
end
|
914
|
-
end
|
915
|
-
|
916
|
-
def path_for_custom_action
|
917
|
-
if shallow_scoping?
|
918
|
-
"#{@scope[:shallow_path]}/#{parent_resource.path}/:id"
|
919
|
-
else
|
920
|
-
@scope[:path]
|
873
|
+
"#{prefix}/#{action_path(action, path)}"
|
921
874
|
end
|
922
875
|
end
|
923
876
|
|
@@ -927,44 +880,61 @@ module ActionDispatch
|
|
927
880
|
|
928
881
|
def prefix_name_for_action(action, as)
|
929
882
|
if as.present?
|
930
|
-
|
883
|
+
as.to_s
|
931
884
|
elsif as
|
932
|
-
|
885
|
+
nil
|
933
886
|
elsif !canonical_action?(action, @scope[:scope_level])
|
934
|
-
|
887
|
+
action.to_s
|
935
888
|
end
|
936
889
|
end
|
937
890
|
|
938
891
|
def name_for_action(action, as=nil)
|
939
892
|
prefix = prefix_name_for_action(action, as)
|
893
|
+
prefix = Mapper.normalize_name(prefix) if prefix
|
940
894
|
name_prefix = @scope[:as]
|
941
895
|
|
942
896
|
if parent_resource
|
943
897
|
collection_name = parent_resource.collection_name
|
944
898
|
member_name = parent_resource.member_name
|
945
|
-
name_prefix = "#{name_prefix}_" if name_prefix.present?
|
946
899
|
end
|
947
900
|
|
948
|
-
case @scope[:scope_level]
|
901
|
+
name = case @scope[:scope_level]
|
902
|
+
when :nested
|
903
|
+
[member_name, prefix]
|
949
904
|
when :collection
|
950
|
-
|
905
|
+
[prefix, name_prefix, collection_name]
|
951
906
|
when :new
|
952
|
-
|
907
|
+
[prefix, :new, name_prefix, member_name]
|
908
|
+
when :member
|
909
|
+
[prefix, shallow_scoping? ? @scope[:shallow_prefix] : name_prefix, member_name]
|
910
|
+
when :root
|
911
|
+
[name_prefix, collection_name, prefix]
|
953
912
|
else
|
954
|
-
|
955
|
-
shallow_prefix = "#{@scope[:shallow_prefix]}_" if @scope[:shallow_prefix].present?
|
956
|
-
"#{prefix}#{shallow_prefix}#{member_name}"
|
957
|
-
else
|
958
|
-
"#{prefix}#{name_prefix}#{member_name}"
|
959
|
-
end
|
913
|
+
[name_prefix, member_name, prefix]
|
960
914
|
end
|
915
|
+
|
916
|
+
name.select(&:present?).join("_").presence
|
917
|
+
end
|
918
|
+
end
|
919
|
+
|
920
|
+
module Shorthand
|
921
|
+
def match(*args)
|
922
|
+
if args.size == 1 && args.last.is_a?(Hash)
|
923
|
+
options = args.pop
|
924
|
+
path, to = options.find { |name, value| name.is_a?(String) }
|
925
|
+
options.merge!(:to => to).delete(path)
|
926
|
+
super(path, options)
|
927
|
+
else
|
928
|
+
super
|
961
929
|
end
|
930
|
+
end
|
962
931
|
end
|
963
932
|
|
964
933
|
include Base
|
965
934
|
include HttpHelpers
|
966
935
|
include Scoping
|
967
936
|
include Resources
|
937
|
+
include Shorthand
|
968
938
|
end
|
969
939
|
end
|
970
940
|
end
|