actionpack 2.1.0 → 2.1.1
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 +17 -0
- data/Rakefile +10 -6
- data/lib/action_controller.rb +0 -0
- data/lib/action_controller/assertions/response_assertions.rb +1 -1
- data/lib/action_controller/assertions/selector_assertions.rb +26 -46
- data/lib/action_controller/base.rb +8 -4
- data/lib/action_controller/dispatcher.rb +1 -1
- data/lib/action_controller/filters.rb +194 -195
- data/lib/action_controller/polymorphic_routes.rb +25 -12
- data/lib/action_controller/record_identifier.rb +20 -13
- data/lib/action_controller/request.rb +9 -6
- data/lib/action_controller/request_profiler.rb +0 -0
- data/lib/action_controller/response.rb +0 -0
- data/lib/action_controller/routing.rb +5 -1
- data/lib/action_controller/routing/builder.rb +1 -2
- data/lib/action_controller/routing/segments.rb +1 -1
- data/lib/action_controller/templates/rescues/layout.erb +1 -1
- data/lib/action_controller/test_process.rb +4 -2
- data/lib/action_controller/vendor/html-scanner/html/document.rb +1 -1
- data/lib/action_controller/verification.rb +1 -1
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_view/base.rb +7 -3
- data/lib/action_view/helpers/asset_tag_helper.rb +14 -11
- data/lib/action_view/helpers/date_helper.rb +3 -3
- data/lib/action_view/helpers/form_helper.rb +5 -1
- data/lib/action_view/helpers/form_options_helper.rb +2 -2
- data/lib/action_view/helpers/form_tag_helper.rb +5 -3
- data/lib/action_view/helpers/javascript_helper.rb +4 -4
- data/lib/action_view/helpers/prototype_helper.rb +7 -7
- data/lib/action_view/helpers/tag_helper.rb +4 -3
- data/lib/action_view/helpers/text_helper.rb +1 -1
- data/lib/action_view/helpers/url_helper.rb +3 -5
- data/lib/action_view/partial_template.rb +1 -1
- data/test/controller/action_pack_assertions_test.rb +24 -5
- data/test/controller/assert_select_test.rb +6 -1
- data/test/controller/base_test.rb +37 -1
- data/test/controller/cgi_test.rb +0 -0
- data/test/controller/dispatcher_test.rb +2 -2
- data/test/controller/html-scanner/document_test.rb +25 -0
- data/test/controller/integration_upload_test.rb +1 -1
- data/test/controller/new_render_test.rb +22 -3
- data/test/controller/polymorphic_routes_test.rb +33 -0
- data/test/controller/redirect_test.rb +0 -0
- data/test/controller/render_test.rb +1 -1
- data/test/controller/request_test.rb +6 -0
- data/test/controller/resources_test.rb +15 -17
- data/test/controller/routing_test.rb +22 -2
- data/test/controller/session/cookie_store_test.rb +0 -0
- data/test/controller/test_test.rb +11 -2
- data/test/controller/verification_test.rb +34 -17
- data/test/fixtures/test/render_file_from_template.html.erb +1 -0
- data/test/template/date_helper_test.rb +59 -8
- data/test/template/deprecated_erb_variable_test.rb +9 -0
- data/test/template/form_options_helper_test.rb +505 -514
- data/test/template/form_tag_helper_test.rb +13 -0
- data/test/template/javascript_helper_test.rb +7 -4
- data/test/template/prototype_helper_test.rb +14 -6
- data/test/template/text_helper_test.rb +1 -0
- data/test/template/url_helper_test.rb +11 -1
- metadata +8 -4
data/CHANGELOG
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
*2.1.1 (September 4th, 2008)*
|
2
|
+
|
3
|
+
* All 2xx requests are considered successful [Josh Peek]
|
4
|
+
|
5
|
+
* Deprecate the limited follow_redirect in functional tests. If you wish to follow redirects, use integration tests. [Michael Koziarski]
|
6
|
+
|
7
|
+
* Fixed that AssetTagHelper#compute_public_path shouldn't cache the asset_host along with the source or per-request proc's won't run [DHH]
|
8
|
+
|
9
|
+
* Deprecate define_javascript_functions, javascript_include_tag and friends are much better [Michael Koziarski]
|
10
|
+
|
11
|
+
* Fix polymorphic_url with singleton resources. #461 [Tammer Saleh]
|
12
|
+
|
13
|
+
* Deprecate ActionView::Base.erb_variable. Use the concat helper method instead of appending to it directly. [Jeremy Kemper]
|
14
|
+
|
15
|
+
* Fixed Request#remote_ip to only raise hell if the HTTP_CLIENT_IP and HTTP_X_FORWARDED_FOR doesn't match (not just if they're both present) [Mark Imbriaco, Bradford Folkens]
|
16
|
+
|
17
|
+
|
1
18
|
*2.1.0 (May 31st, 2008)*
|
2
19
|
|
3
20
|
* InstanceTag#default_time_from_options overflows to DateTime [Geoff Buesing]
|
data/Rakefile
CHANGED
@@ -5,6 +5,8 @@ require 'rake/rdoctask'
|
|
5
5
|
require 'rake/packagetask'
|
6
6
|
require 'rake/gempackagetask'
|
7
7
|
require 'rake/contrib/sshpublisher'
|
8
|
+
require 'rake/contrib/rubyforgepublisher'
|
9
|
+
|
8
10
|
require File.join(File.dirname(__FILE__), 'lib', 'action_pack', 'version')
|
9
11
|
|
10
12
|
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
@@ -49,12 +51,14 @@ Rake::RDocTask.new { |rdoc|
|
|
49
51
|
rdoc.title = "Action Pack -- On rails from request to response"
|
50
52
|
rdoc.options << '--line-numbers' << '--inline-source'
|
51
53
|
rdoc.options << '--charset' << 'utf-8'
|
52
|
-
rdoc.template = "#{ENV['template']}.rb"
|
54
|
+
rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo'
|
53
55
|
if ENV['DOC_FILES']
|
54
56
|
rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/))
|
55
57
|
else
|
56
58
|
rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG')
|
57
|
-
rdoc.rdoc_files.include('lib/**/*.rb'
|
59
|
+
rdoc.rdoc_files.include(Dir['lib/**/*.rb'] -
|
60
|
+
Dir['lib/*/vendor/**/*.rb'])
|
61
|
+
rdoc.rdoc_files.exclude('lib/actionpack.rb')
|
58
62
|
end
|
59
63
|
}
|
60
64
|
|
@@ -76,7 +80,7 @@ spec = Gem::Specification.new do |s|
|
|
76
80
|
s.has_rdoc = true
|
77
81
|
s.requirements << 'none'
|
78
82
|
|
79
|
-
s.add_dependency('activesupport', '= 2.1.
|
83
|
+
s.add_dependency('activesupport', '= 2.1.1' + PKG_BUILD)
|
80
84
|
|
81
85
|
s.require_path = 'lib'
|
82
86
|
s.autorequire = 'action_controller'
|
@@ -132,13 +136,13 @@ task :update_js => [ :update_scriptaculous ]
|
|
132
136
|
|
133
137
|
desc "Publish the API documentation"
|
134
138
|
task :pgem => [:package] do
|
135
|
-
Rake::SshFilePublisher.new("
|
136
|
-
`ssh
|
139
|
+
Rake::SshFilePublisher.new("david@greed.loudthinking.com", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
|
140
|
+
`ssh david@greed.loudthinking.com '/u/sites/gems/gemupdate.sh'`
|
137
141
|
end
|
138
142
|
|
139
143
|
desc "Publish the API documentation"
|
140
144
|
task :pdoc => [:rdoc] do
|
141
|
-
Rake::SshDirPublisher.new("
|
145
|
+
Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/ap", "doc").upload
|
142
146
|
end
|
143
147
|
|
144
148
|
desc "Publish the release files to RubyForge."
|
data/lib/action_controller.rb
CHANGED
File without changes
|
@@ -97,7 +97,7 @@ module ActionController
|
|
97
97
|
value['controller'] = value['controller'].to_s
|
98
98
|
if key == :actual && value['controller'].first != '/' && !value['controller'].include?('/')
|
99
99
|
new_controller_path = ActionController::Routing.controller_relative_to(value['controller'], @controller.class.controller_path)
|
100
|
-
value['controller'] = new_controller_path if value['controller'] != new_controller_path && ActionController::Routing.possible_controllers.include?(new_controller_path)
|
100
|
+
value['controller'] = new_controller_path if value['controller'] != new_controller_path && ActionController::Routing.possible_controllers.include?(new_controller_path) && @response.redirected_to.is_a?(Hash)
|
101
101
|
end
|
102
102
|
value['controller'] = value['controller'][1..-1] if value['controller'].first == '/' # strip leading hash
|
103
103
|
end
|
@@ -398,47 +398,31 @@ module ActionController
|
|
398
398
|
# # The same, but shorter.
|
399
399
|
# assert_select "ol>li", 4
|
400
400
|
def assert_select_rjs(*args, &block)
|
401
|
-
rjs_type = nil
|
402
|
-
|
401
|
+
rjs_type = args.first.is_a?(Symbol) ? args.shift : nil
|
402
|
+
id = args.first.is_a?(String) ? args.shift : nil
|
403
403
|
|
404
404
|
# If the first argument is a symbol, it's the type of RJS statement we're looking
|
405
405
|
# for (update, replace, insertion, etc). Otherwise, we're looking for just about
|
406
406
|
# any RJS statement.
|
407
|
-
if
|
408
|
-
rjs_type = arg
|
409
|
-
|
407
|
+
if rjs_type
|
410
408
|
if rjs_type == :insert
|
411
|
-
|
412
|
-
insertion = "insert_#{
|
413
|
-
raise ArgumentError, "Unknown RJS insertion type #{
|
409
|
+
position = args.shift
|
410
|
+
insertion = "insert_#{position}".to_sym
|
411
|
+
raise ArgumentError, "Unknown RJS insertion type #{position}" unless RJS_STATEMENTS[insertion]
|
414
412
|
statement = "(#{RJS_STATEMENTS[insertion]})"
|
415
413
|
else
|
416
414
|
raise ArgumentError, "Unknown RJS statement type #{rjs_type}" unless RJS_STATEMENTS[rjs_type]
|
417
415
|
statement = "(#{RJS_STATEMENTS[rjs_type]})"
|
418
416
|
end
|
419
|
-
arg = args.shift
|
420
417
|
else
|
421
418
|
statement = "#{RJS_STATEMENTS[:any]}"
|
422
419
|
end
|
423
420
|
|
424
421
|
# Next argument we're looking for is the element identifier. If missing, we pick
|
425
|
-
# any element.
|
426
|
-
|
427
|
-
id
|
428
|
-
|
429
|
-
else
|
430
|
-
id = "[^\"]*"
|
431
|
-
end
|
432
|
-
|
433
|
-
pattern =
|
434
|
-
case rjs_type
|
435
|
-
when :chained_replace, :chained_replace_html
|
436
|
-
Regexp.new("\\$\\(\"#{id}\"\\)#{statement}\\(#{RJS_PATTERN_HTML}\\)", Regexp::MULTILINE)
|
437
|
-
when :remove, :show, :hide, :toggle
|
438
|
-
Regexp.new("#{statement}\\(\"#{id}\"\\)")
|
439
|
-
else
|
440
|
-
Regexp.new("#{statement}\\(\"#{id}\", #{RJS_PATTERN_HTML}\\)", Regexp::MULTILINE)
|
441
|
-
end
|
422
|
+
# any element, otherwise we replace it in the statement.
|
423
|
+
pattern = Regexp.new(
|
424
|
+
id ? statement.gsub(RJS_ANY_ID, "\"#{id}\"") : statement
|
425
|
+
)
|
442
426
|
|
443
427
|
# Duplicate the body since the next step involves destroying it.
|
444
428
|
matches = nil
|
@@ -447,7 +431,7 @@ module ActionController
|
|
447
431
|
matches = @response.body.match(pattern)
|
448
432
|
else
|
449
433
|
@response.body.gsub(pattern) do |match|
|
450
|
-
html = unescape_rjs(
|
434
|
+
html = unescape_rjs(match)
|
451
435
|
matches ||= []
|
452
436
|
matches.concat HTML::Document.new(html).root.children.select { |n| n.tag? }
|
453
437
|
""
|
@@ -577,27 +561,23 @@ module ActionController
|
|
577
561
|
|
578
562
|
protected
|
579
563
|
unless const_defined?(:RJS_STATEMENTS)
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
:chained_replace =>
|
584
|
-
:chained_replace_html =>
|
585
|
-
:
|
586
|
-
:
|
587
|
-
:hide => /Element\.hide/,
|
588
|
-
:toggle => /Element\.toggle/
|
564
|
+
RJS_PATTERN_HTML = "\"((\\\\\"|[^\"])*)\""
|
565
|
+
RJS_ANY_ID = "\"([^\"])*\""
|
566
|
+
RJS_STATEMENTS = {
|
567
|
+
:chained_replace => "\\$\\(#{RJS_ANY_ID}\\)\\.replace\\(#{RJS_PATTERN_HTML}\\)",
|
568
|
+
:chained_replace_html => "\\$\\(#{RJS_ANY_ID}\\)\\.update\\(#{RJS_PATTERN_HTML}\\)",
|
569
|
+
:replace_html => "Element\\.update\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)",
|
570
|
+
:replace => "Element\\.replace\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)"
|
589
571
|
}
|
590
|
-
|
572
|
+
[:remove, :show, :hide, :toggle].each do |action|
|
573
|
+
RJS_STATEMENTS[action] = "Element\\.#{action}\\(#{RJS_ANY_ID}\\)"
|
574
|
+
end
|
575
|
+
RJS_INSERTIONS = ["top", "bottom", "before", "after"]
|
591
576
|
RJS_INSERTIONS.each do |insertion|
|
592
|
-
RJS_STATEMENTS["insert_#{insertion}".to_sym] =
|
577
|
+
RJS_STATEMENTS["insert_#{insertion}".to_sym] = "Element.insert\\(#{RJS_ANY_ID}, \\{ #{insertion}: #{RJS_PATTERN_HTML} \\}\\)"
|
593
578
|
end
|
579
|
+
RJS_STATEMENTS[:insert_html] = "Element.insert\\(#{RJS_ANY_ID}, \\{ (#{RJS_INSERTIONS.join('|')}): #{RJS_PATTERN_HTML} \\}\\)"
|
594
580
|
RJS_STATEMENTS[:any] = Regexp.new("(#{RJS_STATEMENTS.values.join('|')})")
|
595
|
-
RJS_STATEMENTS[:insert_html] = Regexp.new(RJS_INSERTIONS.collect do |insertion|
|
596
|
-
Regexp.quote("new Insertion.#{insertion.to_s.camelize}")
|
597
|
-
end.join('|'))
|
598
|
-
RJS_PATTERN_HTML = /"((\\"|[^"])*)"/
|
599
|
-
RJS_PATTERN_EVERYTHING = Regexp.new("#{RJS_STATEMENTS[:any]}\\(\"([^\"]*)\", #{RJS_PATTERN_HTML}\\)",
|
600
|
-
Regexp::MULTILINE)
|
601
581
|
RJS_PATTERN_UNICODE_ESCAPED_CHAR = /\\u([0-9a-zA-Z]{4})/
|
602
582
|
end
|
603
583
|
|
@@ -611,8 +591,8 @@ module ActionController
|
|
611
591
|
root = HTML::Node.new(nil)
|
612
592
|
|
613
593
|
while true
|
614
|
-
next if body.sub!(
|
615
|
-
html = unescape_rjs(
|
594
|
+
next if body.sub!(RJS_STATEMENTS[:any]) do |match|
|
595
|
+
html = unescape_rjs(match)
|
616
596
|
matches = HTML::Document.new(html).root.children.select { |n| n.tag? }
|
617
597
|
root.children.concat matches
|
618
598
|
""
|
@@ -613,8 +613,9 @@ module ActionController #:nodoc:
|
|
613
613
|
#
|
614
614
|
# This takes the current URL as is and only exchanges the action. In contrast, <tt>url_for :action => 'print'</tt>
|
615
615
|
# would have slashed-off the path components after the changed action.
|
616
|
-
def url_for(options =
|
617
|
-
|
616
|
+
def url_for(options = {})
|
617
|
+
options ||= {}
|
618
|
+
case options
|
618
619
|
when String
|
619
620
|
options
|
620
621
|
when Hash
|
@@ -743,6 +744,9 @@ module ActionController #:nodoc:
|
|
743
744
|
# # Renders the template located in [TEMPLATE_ROOT]/weblog/show.r(html|xml) (in Rails, app/views/weblog/show.erb)
|
744
745
|
# render :template => "weblog/show"
|
745
746
|
#
|
747
|
+
# # Renders the template with a local variable
|
748
|
+
# render :template => "weblog/show", :locals => {:customer => Customer.new}
|
749
|
+
#
|
746
750
|
# === Rendering a file
|
747
751
|
#
|
748
752
|
# File rendering works just like action rendering except that it takes a filesystem path. By default, the path
|
@@ -865,7 +869,7 @@ module ActionController #:nodoc:
|
|
865
869
|
render_for_file(file, options[:status], options[:use_full_path], options[:locals] || {})
|
866
870
|
|
867
871
|
elsif template = options[:template]
|
868
|
-
render_for_file(template, options[:status], true)
|
872
|
+
render_for_file(template, options[:status], true, options[:locals] || {})
|
869
873
|
|
870
874
|
elsif inline = options[:inline]
|
871
875
|
add_variables_to_assigns
|
@@ -1147,7 +1151,7 @@ module ActionController #:nodoc:
|
|
1147
1151
|
|
1148
1152
|
def log_processing
|
1149
1153
|
if logger && logger.info?
|
1150
|
-
logger.info "\n\nProcessing #{
|
1154
|
+
logger.info "\n\nProcessing #{self.class.name}\##{action_name} (for #{request_origin}) [#{request.method.to_s.upcase}]"
|
1151
1155
|
logger.info " Session ID: #{@_session.session_id}" if @_session and @_session.respond_to?(:session_id)
|
1152
1156
|
logger.info " Parameters: #{respond_to?(:filter_parameters) ? filter_parameters(params).inspect : params.inspect}"
|
1153
1157
|
end
|
@@ -135,7 +135,7 @@ module ActionController
|
|
135
135
|
# be reloaded on the next request without restarting the server.
|
136
136
|
def cleanup_application
|
137
137
|
ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
|
138
|
-
Dependencies.clear
|
138
|
+
ActiveSupport::Dependencies.clear
|
139
139
|
ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
|
140
140
|
end
|
141
141
|
|
@@ -7,6 +7,200 @@ module ActionController #:nodoc:
|
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
+
class FilterChain < ActiveSupport::Callbacks::CallbackChain #:nodoc:
|
11
|
+
def append_filter_to_chain(filters, filter_type, &block)
|
12
|
+
pos = find_filter_append_position(filters, filter_type)
|
13
|
+
update_filter_chain(filters, filter_type, pos, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def prepend_filter_to_chain(filters, filter_type, &block)
|
17
|
+
pos = find_filter_prepend_position(filters, filter_type)
|
18
|
+
update_filter_chain(filters, filter_type, pos, &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_filters(filters, filter_type, &block)
|
22
|
+
filters, conditions = extract_options(filters, &block)
|
23
|
+
filters.map! { |filter| find_or_create_filter(filter, filter_type, conditions) }
|
24
|
+
filters
|
25
|
+
end
|
26
|
+
|
27
|
+
def skip_filter_in_chain(*filters, &test)
|
28
|
+
filters, conditions = extract_options(filters)
|
29
|
+
filters.each do |filter|
|
30
|
+
if callback = find(filter) then delete(callback) end
|
31
|
+
end if conditions.empty?
|
32
|
+
update_filter_in_chain(filters, :skip => conditions, &test)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def update_filter_chain(filters, filter_type, pos, &block)
|
37
|
+
new_filters = create_filters(filters, filter_type, &block)
|
38
|
+
insert(pos, new_filters).flatten!
|
39
|
+
end
|
40
|
+
|
41
|
+
def find_filter_append_position(filters, filter_type)
|
42
|
+
# appending an after filter puts it at the end of the call chain
|
43
|
+
# before and around filters go before the first after filter in the chain
|
44
|
+
unless filter_type == :after
|
45
|
+
each_with_index do |f,i|
|
46
|
+
return i if f.after?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
return -1
|
50
|
+
end
|
51
|
+
|
52
|
+
def find_filter_prepend_position(filters, filter_type)
|
53
|
+
# prepending a before or around filter puts it at the front of the call chain
|
54
|
+
# after filters go before the first after filter in the chain
|
55
|
+
if filter_type == :after
|
56
|
+
each_with_index do |f,i|
|
57
|
+
return i if f.after?
|
58
|
+
end
|
59
|
+
return -1
|
60
|
+
end
|
61
|
+
return 0
|
62
|
+
end
|
63
|
+
|
64
|
+
def find_or_create_filter(filter, filter_type, options = {})
|
65
|
+
update_filter_in_chain([filter], options)
|
66
|
+
|
67
|
+
if found_filter = find(filter) { |f| f.type == filter_type }
|
68
|
+
found_filter
|
69
|
+
else
|
70
|
+
filter_kind = case
|
71
|
+
when filter.respond_to?(:before) && filter_type == :before
|
72
|
+
:before
|
73
|
+
when filter.respond_to?(:after) && filter_type == :after
|
74
|
+
:after
|
75
|
+
else
|
76
|
+
:filter
|
77
|
+
end
|
78
|
+
|
79
|
+
case filter_type
|
80
|
+
when :before
|
81
|
+
BeforeFilter.new(filter_kind, filter, options)
|
82
|
+
when :after
|
83
|
+
AfterFilter.new(filter_kind, filter, options)
|
84
|
+
else
|
85
|
+
AroundFilter.new(filter_kind, filter, options)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def update_filter_in_chain(filters, options, &test)
|
91
|
+
filters.map! { |f| block_given? ? find(f, &test) : find(f) }
|
92
|
+
filters.compact!
|
93
|
+
|
94
|
+
map! do |filter|
|
95
|
+
if filters.include?(filter)
|
96
|
+
new_filter = filter.dup
|
97
|
+
new_filter.options.merge!(options)
|
98
|
+
new_filter
|
99
|
+
else
|
100
|
+
filter
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class Filter < ActiveSupport::Callbacks::Callback #:nodoc:
|
107
|
+
def before?
|
108
|
+
self.class == BeforeFilter
|
109
|
+
end
|
110
|
+
|
111
|
+
def after?
|
112
|
+
self.class == AfterFilter
|
113
|
+
end
|
114
|
+
|
115
|
+
def around?
|
116
|
+
self.class == AroundFilter
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
def should_not_skip?(controller)
|
121
|
+
if options[:skip]
|
122
|
+
!included_in_action?(controller, options[:skip])
|
123
|
+
else
|
124
|
+
true
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def included_in_action?(controller, options)
|
129
|
+
if options[:only]
|
130
|
+
Array(options[:only]).map(&:to_s).include?(controller.action_name)
|
131
|
+
elsif options[:except]
|
132
|
+
!Array(options[:except]).map(&:to_s).include?(controller.action_name)
|
133
|
+
else
|
134
|
+
true
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def should_run_callback?(controller)
|
139
|
+
should_not_skip?(controller) && included_in_action?(controller, options) && super
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
class AroundFilter < Filter #:nodoc:
|
144
|
+
def type
|
145
|
+
:around
|
146
|
+
end
|
147
|
+
|
148
|
+
def call(controller, &block)
|
149
|
+
if should_run_callback?(controller)
|
150
|
+
method = filter_responds_to_before_and_after? ? around_proc : self.method
|
151
|
+
|
152
|
+
# For around_filter do |controller, action|
|
153
|
+
if method.is_a?(Proc) && method.arity == 2
|
154
|
+
evaluate_method(method, controller, block)
|
155
|
+
else
|
156
|
+
evaluate_method(method, controller, &block)
|
157
|
+
end
|
158
|
+
else
|
159
|
+
block.call
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
def filter_responds_to_before_and_after?
|
165
|
+
method.respond_to?(:before) && method.respond_to?(:after)
|
166
|
+
end
|
167
|
+
|
168
|
+
def around_proc
|
169
|
+
Proc.new do |controller, action|
|
170
|
+
method.before(controller)
|
171
|
+
|
172
|
+
if controller.send!(:performed?)
|
173
|
+
controller.send!(:halt_filter_chain, method, :rendered_or_redirected)
|
174
|
+
else
|
175
|
+
begin
|
176
|
+
action.call
|
177
|
+
ensure
|
178
|
+
method.after(controller)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
class BeforeFilter < Filter #:nodoc:
|
186
|
+
def type
|
187
|
+
:before
|
188
|
+
end
|
189
|
+
|
190
|
+
def call(controller, &block)
|
191
|
+
super
|
192
|
+
if controller.send!(:performed?)
|
193
|
+
controller.send!(:halt_filter_chain, method, :rendered_or_redirected)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
class AfterFilter < Filter #:nodoc:
|
199
|
+
def type
|
200
|
+
:after
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
10
204
|
# Filters enable controllers to run shared pre- and post-processing code for its actions. These filters can be used to do
|
11
205
|
# authentication, caching, or auditing before the intended action is performed. Or to do localization or output
|
12
206
|
# compression after the action has been performed. Filters have access to the request, response, and all the instance
|
@@ -245,201 +439,6 @@ module ActionController #:nodoc:
|
|
245
439
|
# filter and controller action will not be run. If +before+ renders or redirects,
|
246
440
|
# the second half of +around+ and will still run but +after+ and the
|
247
441
|
# action will not. If +around+ fails to yield, +after+ will not be run.
|
248
|
-
|
249
|
-
class FilterChain < ActiveSupport::Callbacks::CallbackChain #:nodoc:
|
250
|
-
def append_filter_to_chain(filters, filter_type, &block)
|
251
|
-
pos = find_filter_append_position(filters, filter_type)
|
252
|
-
update_filter_chain(filters, filter_type, pos, &block)
|
253
|
-
end
|
254
|
-
|
255
|
-
def prepend_filter_to_chain(filters, filter_type, &block)
|
256
|
-
pos = find_filter_prepend_position(filters, filter_type)
|
257
|
-
update_filter_chain(filters, filter_type, pos, &block)
|
258
|
-
end
|
259
|
-
|
260
|
-
def create_filters(filters, filter_type, &block)
|
261
|
-
filters, conditions = extract_options(filters, &block)
|
262
|
-
filters.map! { |filter| find_or_create_filter(filter, filter_type, conditions) }
|
263
|
-
filters
|
264
|
-
end
|
265
|
-
|
266
|
-
def skip_filter_in_chain(*filters, &test)
|
267
|
-
filters, conditions = extract_options(filters)
|
268
|
-
filters.each do |filter|
|
269
|
-
if callback = find(filter) then delete(callback) end
|
270
|
-
end if conditions.empty?
|
271
|
-
update_filter_in_chain(filters, :skip => conditions, &test)
|
272
|
-
end
|
273
|
-
|
274
|
-
private
|
275
|
-
def update_filter_chain(filters, filter_type, pos, &block)
|
276
|
-
new_filters = create_filters(filters, filter_type, &block)
|
277
|
-
insert(pos, new_filters).flatten!
|
278
|
-
end
|
279
|
-
|
280
|
-
def find_filter_append_position(filters, filter_type)
|
281
|
-
# appending an after filter puts it at the end of the call chain
|
282
|
-
# before and around filters go before the first after filter in the chain
|
283
|
-
unless filter_type == :after
|
284
|
-
each_with_index do |f,i|
|
285
|
-
return i if f.after?
|
286
|
-
end
|
287
|
-
end
|
288
|
-
return -1
|
289
|
-
end
|
290
|
-
|
291
|
-
def find_filter_prepend_position(filters, filter_type)
|
292
|
-
# prepending a before or around filter puts it at the front of the call chain
|
293
|
-
# after filters go before the first after filter in the chain
|
294
|
-
if filter_type == :after
|
295
|
-
each_with_index do |f,i|
|
296
|
-
return i if f.after?
|
297
|
-
end
|
298
|
-
return -1
|
299
|
-
end
|
300
|
-
return 0
|
301
|
-
end
|
302
|
-
|
303
|
-
def find_or_create_filter(filter, filter_type, options = {})
|
304
|
-
update_filter_in_chain([filter], options)
|
305
|
-
|
306
|
-
if found_filter = find(filter) { |f| f.type == filter_type }
|
307
|
-
found_filter
|
308
|
-
else
|
309
|
-
filter_kind = case
|
310
|
-
when filter.respond_to?(:before) && filter_type == :before
|
311
|
-
:before
|
312
|
-
when filter.respond_to?(:after) && filter_type == :after
|
313
|
-
:after
|
314
|
-
else
|
315
|
-
:filter
|
316
|
-
end
|
317
|
-
|
318
|
-
case filter_type
|
319
|
-
when :before
|
320
|
-
BeforeFilter.new(filter_kind, filter, options)
|
321
|
-
when :after
|
322
|
-
AfterFilter.new(filter_kind, filter, options)
|
323
|
-
else
|
324
|
-
AroundFilter.new(filter_kind, filter, options)
|
325
|
-
end
|
326
|
-
end
|
327
|
-
end
|
328
|
-
|
329
|
-
def update_filter_in_chain(filters, options, &test)
|
330
|
-
filters.map! { |f| block_given? ? find(f, &test) : find(f) }
|
331
|
-
filters.compact!
|
332
|
-
|
333
|
-
map! do |filter|
|
334
|
-
if filters.include?(filter)
|
335
|
-
new_filter = filter.dup
|
336
|
-
new_filter.options.merge!(options)
|
337
|
-
new_filter
|
338
|
-
else
|
339
|
-
filter
|
340
|
-
end
|
341
|
-
end
|
342
|
-
end
|
343
|
-
end
|
344
|
-
|
345
|
-
class Filter < ActiveSupport::Callbacks::Callback #:nodoc:
|
346
|
-
def before?
|
347
|
-
self.class == BeforeFilter
|
348
|
-
end
|
349
|
-
|
350
|
-
def after?
|
351
|
-
self.class == AfterFilter
|
352
|
-
end
|
353
|
-
|
354
|
-
def around?
|
355
|
-
self.class == AroundFilter
|
356
|
-
end
|
357
|
-
|
358
|
-
private
|
359
|
-
def should_not_skip?(controller)
|
360
|
-
if options[:skip]
|
361
|
-
!included_in_action?(controller, options[:skip])
|
362
|
-
else
|
363
|
-
true
|
364
|
-
end
|
365
|
-
end
|
366
|
-
|
367
|
-
def included_in_action?(controller, options)
|
368
|
-
if options[:only]
|
369
|
-
Array(options[:only]).map(&:to_s).include?(controller.action_name)
|
370
|
-
elsif options[:except]
|
371
|
-
!Array(options[:except]).map(&:to_s).include?(controller.action_name)
|
372
|
-
else
|
373
|
-
true
|
374
|
-
end
|
375
|
-
end
|
376
|
-
|
377
|
-
def should_run_callback?(controller)
|
378
|
-
should_not_skip?(controller) && included_in_action?(controller, options) && super
|
379
|
-
end
|
380
|
-
end
|
381
|
-
|
382
|
-
class AroundFilter < Filter #:nodoc:
|
383
|
-
def type
|
384
|
-
:around
|
385
|
-
end
|
386
|
-
|
387
|
-
def call(controller, &block)
|
388
|
-
if should_run_callback?(controller)
|
389
|
-
method = filter_responds_to_before_and_after? ? around_proc : self.method
|
390
|
-
|
391
|
-
# For around_filter do |controller, action|
|
392
|
-
if method.is_a?(Proc) && method.arity == 2
|
393
|
-
evaluate_method(method, controller, block)
|
394
|
-
else
|
395
|
-
evaluate_method(method, controller, &block)
|
396
|
-
end
|
397
|
-
else
|
398
|
-
block.call
|
399
|
-
end
|
400
|
-
end
|
401
|
-
|
402
|
-
private
|
403
|
-
def filter_responds_to_before_and_after?
|
404
|
-
method.respond_to?(:before) && method.respond_to?(:after)
|
405
|
-
end
|
406
|
-
|
407
|
-
def around_proc
|
408
|
-
Proc.new do |controller, action|
|
409
|
-
method.before(controller)
|
410
|
-
|
411
|
-
if controller.send!(:performed?)
|
412
|
-
controller.send!(:halt_filter_chain, method, :rendered_or_redirected)
|
413
|
-
else
|
414
|
-
begin
|
415
|
-
action.call
|
416
|
-
ensure
|
417
|
-
method.after(controller)
|
418
|
-
end
|
419
|
-
end
|
420
|
-
end
|
421
|
-
end
|
422
|
-
end
|
423
|
-
|
424
|
-
class BeforeFilter < Filter #:nodoc:
|
425
|
-
def type
|
426
|
-
:before
|
427
|
-
end
|
428
|
-
|
429
|
-
def call(controller, &block)
|
430
|
-
super
|
431
|
-
if controller.send!(:performed?)
|
432
|
-
controller.send!(:halt_filter_chain, method, :rendered_or_redirected)
|
433
|
-
end
|
434
|
-
end
|
435
|
-
end
|
436
|
-
|
437
|
-
class AfterFilter < Filter #:nodoc:
|
438
|
-
def type
|
439
|
-
:after
|
440
|
-
end
|
441
|
-
end
|
442
|
-
|
443
442
|
module ClassMethods
|
444
443
|
# The passed <tt>filters</tt> will be appended to the filter_chain and
|
445
444
|
# will execute before the action on this controller is performed.
|