halorgium-actionpack 3.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +5179 -0
- data/MIT-LICENSE +21 -0
- data/README +409 -0
- data/lib/abstract_controller.rb +16 -0
- data/lib/abstract_controller/base.rb +158 -0
- data/lib/abstract_controller/callbacks.rb +113 -0
- data/lib/abstract_controller/exceptions.rb +12 -0
- data/lib/abstract_controller/helpers.rb +151 -0
- data/lib/abstract_controller/layouts.rb +250 -0
- data/lib/abstract_controller/localized_cache.rb +49 -0
- data/lib/abstract_controller/logger.rb +61 -0
- data/lib/abstract_controller/rendering_controller.rb +188 -0
- data/lib/action_controller.rb +72 -0
- data/lib/action_controller/base.rb +168 -0
- data/lib/action_controller/caching.rb +80 -0
- data/lib/action_controller/caching/actions.rb +163 -0
- data/lib/action_controller/caching/fragments.rb +116 -0
- data/lib/action_controller/caching/pages.rb +154 -0
- data/lib/action_controller/caching/sweeping.rb +97 -0
- data/lib/action_controller/deprecated.rb +4 -0
- data/lib/action_controller/deprecated/integration_test.rb +2 -0
- data/lib/action_controller/deprecated/performance_test.rb +1 -0
- data/lib/action_controller/dispatch/dispatcher.rb +57 -0
- data/lib/action_controller/metal.rb +129 -0
- data/lib/action_controller/metal/benchmarking.rb +73 -0
- data/lib/action_controller/metal/compatibility.rb +145 -0
- data/lib/action_controller/metal/conditional_get.rb +86 -0
- data/lib/action_controller/metal/configuration.rb +28 -0
- data/lib/action_controller/metal/cookies.rb +105 -0
- data/lib/action_controller/metal/exceptions.rb +55 -0
- data/lib/action_controller/metal/filter_parameter_logging.rb +77 -0
- data/lib/action_controller/metal/flash.rb +162 -0
- data/lib/action_controller/metal/head.rb +27 -0
- data/lib/action_controller/metal/helpers.rb +115 -0
- data/lib/action_controller/metal/hide_actions.rb +47 -0
- data/lib/action_controller/metal/http_authentication.rb +312 -0
- data/lib/action_controller/metal/layouts.rb +171 -0
- data/lib/action_controller/metal/mime_responds.rb +317 -0
- data/lib/action_controller/metal/rack_convenience.rb +27 -0
- data/lib/action_controller/metal/redirector.rb +22 -0
- data/lib/action_controller/metal/render_options.rb +103 -0
- data/lib/action_controller/metal/rendering_controller.rb +57 -0
- data/lib/action_controller/metal/request_forgery_protection.rb +108 -0
- data/lib/action_controller/metal/rescuable.rb +13 -0
- data/lib/action_controller/metal/responder.rb +200 -0
- data/lib/action_controller/metal/session.rb +15 -0
- data/lib/action_controller/metal/session_management.rb +45 -0
- data/lib/action_controller/metal/streaming.rb +188 -0
- data/lib/action_controller/metal/testing.rb +39 -0
- data/lib/action_controller/metal/url_for.rb +41 -0
- data/lib/action_controller/metal/verification.rb +130 -0
- data/lib/action_controller/middleware.rb +38 -0
- data/lib/action_controller/notifications.rb +10 -0
- data/lib/action_controller/polymorphic_routes.rb +183 -0
- data/lib/action_controller/record_identifier.rb +91 -0
- data/lib/action_controller/testing/process.rb +111 -0
- data/lib/action_controller/testing/test_case.rb +345 -0
- data/lib/action_controller/translation.rb +13 -0
- data/lib/action_controller/url_rewriter.rb +204 -0
- data/lib/action_controller/vendor/html-scanner.rb +16 -0
- data/lib/action_controller/vendor/html-scanner/html/document.rb +68 -0
- data/lib/action_controller/vendor/html-scanner/html/node.rb +537 -0
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +176 -0
- data/lib/action_controller/vendor/html-scanner/html/selector.rb +828 -0
- data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +105 -0
- data/lib/action_controller/vendor/html-scanner/html/version.rb +11 -0
- data/lib/action_dispatch.rb +70 -0
- data/lib/action_dispatch/http/headers.rb +33 -0
- data/lib/action_dispatch/http/mime_type.rb +231 -0
- data/lib/action_dispatch/http/mime_types.rb +23 -0
- data/lib/action_dispatch/http/request.rb +539 -0
- data/lib/action_dispatch/http/response.rb +290 -0
- data/lib/action_dispatch/http/status_codes.rb +42 -0
- data/lib/action_dispatch/http/utils.rb +20 -0
- data/lib/action_dispatch/middleware/callbacks.rb +50 -0
- data/lib/action_dispatch/middleware/params_parser.rb +79 -0
- data/lib/action_dispatch/middleware/rescue.rb +26 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +208 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +235 -0
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +47 -0
- data/lib/action_dispatch/middleware/show_exceptions.rb +143 -0
- data/lib/action_dispatch/middleware/stack.rb +116 -0
- data/lib/action_dispatch/middleware/static.rb +44 -0
- data/lib/action_dispatch/middleware/string_coercion.rb +29 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +24 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +26 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +10 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +29 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +2 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +10 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +21 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +2 -0
- data/lib/action_dispatch/routing.rb +381 -0
- data/lib/action_dispatch/routing/deprecated_mapper.rb +878 -0
- data/lib/action_dispatch/routing/mapper.rb +327 -0
- data/lib/action_dispatch/routing/route.rb +49 -0
- data/lib/action_dispatch/routing/route_set.rb +497 -0
- data/lib/action_dispatch/testing/assertions.rb +8 -0
- data/lib/action_dispatch/testing/assertions/dom.rb +35 -0
- data/lib/action_dispatch/testing/assertions/model.rb +19 -0
- data/lib/action_dispatch/testing/assertions/response.rb +145 -0
- data/lib/action_dispatch/testing/assertions/routing.rb +144 -0
- data/lib/action_dispatch/testing/assertions/selector.rb +639 -0
- data/lib/action_dispatch/testing/assertions/tag.rb +123 -0
- data/lib/action_dispatch/testing/integration.rb +504 -0
- data/lib/action_dispatch/testing/performance_test.rb +15 -0
- data/lib/action_dispatch/testing/test_request.rb +83 -0
- data/lib/action_dispatch/testing/test_response.rb +131 -0
- data/lib/action_pack.rb +24 -0
- data/lib/action_pack/version.rb +9 -0
- data/lib/action_view.rb +58 -0
- data/lib/action_view/base.rb +308 -0
- data/lib/action_view/context.rb +44 -0
- data/lib/action_view/erb/util.rb +48 -0
- data/lib/action_view/helpers.rb +62 -0
- data/lib/action_view/helpers/active_model_helper.rb +306 -0
- data/lib/action_view/helpers/ajax_helper.rb +68 -0
- data/lib/action_view/helpers/asset_tag_helper.rb +830 -0
- data/lib/action_view/helpers/atom_feed_helper.rb +198 -0
- data/lib/action_view/helpers/cache_helper.rb +39 -0
- data/lib/action_view/helpers/capture_helper.rb +168 -0
- data/lib/action_view/helpers/date_helper.rb +988 -0
- data/lib/action_view/helpers/debug_helper.rb +38 -0
- data/lib/action_view/helpers/form_helper.rb +1102 -0
- data/lib/action_view/helpers/form_options_helper.rb +600 -0
- data/lib/action_view/helpers/form_tag_helper.rb +495 -0
- data/lib/action_view/helpers/javascript_helper.rb +208 -0
- data/lib/action_view/helpers/number_helper.rb +311 -0
- data/lib/action_view/helpers/prototype_helper.rb +1309 -0
- data/lib/action_view/helpers/raw_output_helper.rb +9 -0
- data/lib/action_view/helpers/record_identification_helper.rb +20 -0
- data/lib/action_view/helpers/record_tag_helper.rb +58 -0
- data/lib/action_view/helpers/sanitize_helper.rb +259 -0
- data/lib/action_view/helpers/scriptaculous_helper.rb +226 -0
- data/lib/action_view/helpers/tag_helper.rb +151 -0
- data/lib/action_view/helpers/text_helper.rb +594 -0
- data/lib/action_view/helpers/translation_helper.rb +39 -0
- data/lib/action_view/helpers/url_helper.rb +639 -0
- data/lib/action_view/locale/en.yml +117 -0
- data/lib/action_view/paths.rb +80 -0
- data/lib/action_view/render/partials.rb +342 -0
- data/lib/action_view/render/rendering.rb +134 -0
- data/lib/action_view/safe_buffer.rb +28 -0
- data/lib/action_view/template/error.rb +101 -0
- data/lib/action_view/template/handler.rb +36 -0
- data/lib/action_view/template/handlers.rb +52 -0
- data/lib/action_view/template/handlers/builder.rb +17 -0
- data/lib/action_view/template/handlers/erb.rb +53 -0
- data/lib/action_view/template/handlers/rjs.rb +18 -0
- data/lib/action_view/template/resolver.rb +165 -0
- data/lib/action_view/template/template.rb +131 -0
- data/lib/action_view/template/text.rb +38 -0
- data/lib/action_view/test_case.rb +163 -0
- metadata +236 -0
@@ -0,0 +1,113 @@
|
|
1
|
+
module AbstractController
|
2
|
+
module Callbacks
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
# Uses ActiveSupport::Callbacks as the base functionality. For
|
6
|
+
# more details on the whole callback system, read the documentation
|
7
|
+
# for ActiveSupport::Callbacks.
|
8
|
+
include ActiveSupport::Callbacks
|
9
|
+
|
10
|
+
included do
|
11
|
+
define_callbacks :process_action, :terminator => "response_body"
|
12
|
+
end
|
13
|
+
|
14
|
+
# Override AbstractController::Base's process_action to run the
|
15
|
+
# process_action callbacks around the normal behavior.
|
16
|
+
def process_action(method_name)
|
17
|
+
run_callbacks(:process_action, method_name) do
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
# If :only or :accept are used, convert the options into the
|
24
|
+
# primitive form (:per_key) used by ActiveSupport::Callbacks.
|
25
|
+
# The basic idea is that :only => :index gets converted to
|
26
|
+
# :if => proc {|c| c.action_name == "index" }, but that the
|
27
|
+
# proc is only evaluated once per action for the lifetime of
|
28
|
+
# a Rails process.
|
29
|
+
#
|
30
|
+
# ==== Options
|
31
|
+
# :only<#to_s>:: The callback should be run only for this action
|
32
|
+
# :except<#to_s>:: The callback should be run for all actions
|
33
|
+
# except this action
|
34
|
+
def _normalize_callback_options(options)
|
35
|
+
if only = options[:only]
|
36
|
+
only = Array(only).map {|o| "action_name == '#{o}'"}.join(" || ")
|
37
|
+
options[:per_key] = {:if => only}
|
38
|
+
end
|
39
|
+
if except = options[:except]
|
40
|
+
except = Array(except).map {|e| "action_name == '#{e}'"}.join(" || ")
|
41
|
+
options[:per_key] = {:unless => except}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Skip before, after, and around filters matching any of the names
|
46
|
+
#
|
47
|
+
# ==== Parameters
|
48
|
+
# *names<Object>:: A list of valid names that could be used for
|
49
|
+
# callbacks. Note that skipping uses Ruby equality, so it's
|
50
|
+
# impossible to skip a callback defined using an anonymous proc
|
51
|
+
# using #skip_filter
|
52
|
+
def skip_filter(*names, &blk)
|
53
|
+
skip_before_filter(*names)
|
54
|
+
skip_after_filter(*names)
|
55
|
+
skip_around_filter(*names)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Take callback names and an optional callback proc, normalize them,
|
59
|
+
# then call the block with each callback. This allows us to abstract
|
60
|
+
# the normalization across several methods that use it.
|
61
|
+
#
|
62
|
+
# ==== Parameters
|
63
|
+
# callbacks<Array[*Object, Hash]>:: A list of callbacks, with an optional
|
64
|
+
# options hash as the last parameter.
|
65
|
+
# block<Proc>:: A proc that should be added to the callbacks.
|
66
|
+
#
|
67
|
+
# ==== Block Parameters
|
68
|
+
# name<Symbol>:: The callback to be added
|
69
|
+
# options<Hash>:: A list of options to be used when adding the callback
|
70
|
+
def _insert_callbacks(callbacks, block)
|
71
|
+
options = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
|
72
|
+
_normalize_callback_options(options)
|
73
|
+
callbacks.push(block) if block
|
74
|
+
callbacks.each do |callback|
|
75
|
+
yield callback, options
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# set up before_filter, prepend_before_filter, skip_before_filter, etc.
|
80
|
+
# for each of before, after, and around.
|
81
|
+
[:before, :after, :around].each do |filter|
|
82
|
+
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
83
|
+
# Append a before, after or around filter. See _insert_callbacks
|
84
|
+
# for details on the allowed parameters.
|
85
|
+
def #{filter}_filter(*names, &blk)
|
86
|
+
_insert_callbacks(names, blk) do |name, options|
|
87
|
+
set_callback(:process_action, :#{filter}, name, options)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Prepend a before, after or around filter. See _insert_callbacks
|
92
|
+
# for details on the allowed parameters.
|
93
|
+
def prepend_#{filter}_filter(*names, &blk)
|
94
|
+
_insert_callbacks(names, blk) do |name, options|
|
95
|
+
set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true))
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Skip a before, after or around filter. See _insert_callbacks
|
100
|
+
# for details on the allowed parameters.
|
101
|
+
def skip_#{filter}_filter(*names, &blk)
|
102
|
+
_insert_callbacks(names, blk) do |name, options|
|
103
|
+
skip_callback(:process_action, :#{filter}, name, options)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# *_filter is the same as append_*_filter
|
108
|
+
alias_method :append_#{filter}_filter, :#{filter}_filter
|
109
|
+
RUBY_EVAL
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module AbstractController
|
2
|
+
class Error < StandardError; end
|
3
|
+
class ActionNotFound < StandardError; end
|
4
|
+
|
5
|
+
class DoubleRenderError < Error
|
6
|
+
DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"."
|
7
|
+
|
8
|
+
def initialize(message = nil)
|
9
|
+
super(message || DEFAULT_MESSAGE)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'active_support/dependencies'
|
2
|
+
|
3
|
+
module AbstractController
|
4
|
+
module Helpers
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
include RenderingController
|
8
|
+
|
9
|
+
def self.next_serial
|
10
|
+
@helper_serial ||= 0
|
11
|
+
@helper_serial += 1
|
12
|
+
end
|
13
|
+
|
14
|
+
included do
|
15
|
+
extlib_inheritable_accessor(:_helpers) { Module.new }
|
16
|
+
extlib_inheritable_accessor(:_helper_serial) do
|
17
|
+
AbstractController::Helpers.next_serial
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module ClassMethods
|
22
|
+
# When a class is inherited, wrap its helper module in a new module.
|
23
|
+
# This ensures that the parent class's module can be changed
|
24
|
+
# independently of the child class's.
|
25
|
+
def inherited(klass)
|
26
|
+
helpers = _helpers
|
27
|
+
klass._helpers = Module.new { include helpers }
|
28
|
+
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
# Declare a controller method as a helper. For example, the following
|
33
|
+
# makes the +current_user+ controller method available to the view:
|
34
|
+
# class ApplicationController < ActionController::Base
|
35
|
+
# helper_method :current_user, :logged_in?
|
36
|
+
#
|
37
|
+
# def current_user
|
38
|
+
# @current_user ||= User.find_by_id(session[:user])
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# def logged_in?
|
42
|
+
# current_user != nil
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# In a view:
|
47
|
+
# <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%>
|
48
|
+
#
|
49
|
+
# ==== Parameters
|
50
|
+
# meths<Array[#to_s]>:: The name of a method on the controller
|
51
|
+
# to be made available on the view.
|
52
|
+
def helper_method(*meths)
|
53
|
+
meths.flatten.each do |meth|
|
54
|
+
_helpers.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
|
55
|
+
def #{meth}(*args, &blk)
|
56
|
+
controller.send(%(#{meth}), *args, &blk)
|
57
|
+
end
|
58
|
+
ruby_eval
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# The +helper+ class method can take a series of helper module names, a block, or both.
|
63
|
+
#
|
64
|
+
# ==== Parameters
|
65
|
+
# *args<Array[Module, Symbol, String, :all]>
|
66
|
+
# block<Block>:: A block defining helper methods
|
67
|
+
#
|
68
|
+
# ==== Examples
|
69
|
+
# When the argument is a module it will be included directly in the template class.
|
70
|
+
# helper FooHelper # => includes FooHelper
|
71
|
+
#
|
72
|
+
# When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file
|
73
|
+
# and include the module in the template class. The second form illustrates how to include custom helpers
|
74
|
+
# when working with namespaced controllers, or other cases where the file containing the helper definition is not
|
75
|
+
# in one of Rails' standard load paths:
|
76
|
+
# helper :foo # => requires 'foo_helper' and includes FooHelper
|
77
|
+
# helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper
|
78
|
+
#
|
79
|
+
# Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available
|
80
|
+
# to the template.
|
81
|
+
#
|
82
|
+
# # One line
|
83
|
+
# helper { def hello() "Hello, world!" end }
|
84
|
+
#
|
85
|
+
# # Multi-line
|
86
|
+
# helper do
|
87
|
+
# def foo(bar)
|
88
|
+
# "#{bar} is the very best"
|
89
|
+
# end
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of
|
93
|
+
# +symbols+, +strings+, +modules+ and blocks.
|
94
|
+
#
|
95
|
+
# helper(:three, BlindHelper) { def mice() 'mice' end }
|
96
|
+
#
|
97
|
+
def helper(*args, &block)
|
98
|
+
self._helper_serial = AbstractController::Helpers.next_serial + 1
|
99
|
+
|
100
|
+
_modules_for_helpers(args).each do |mod|
|
101
|
+
add_template_helper(mod)
|
102
|
+
end
|
103
|
+
|
104
|
+
_helpers.module_eval(&block) if block_given?
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
# Makes all the (instance) methods in the helper module available to templates
|
109
|
+
# rendered through this controller.
|
110
|
+
#
|
111
|
+
# ==== Parameters
|
112
|
+
# mod<Module>:: The module to include into the current helper module
|
113
|
+
# for the class
|
114
|
+
def add_template_helper(mod)
|
115
|
+
_helpers.module_eval { include mod }
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns a list of modules, normalized from the acceptable kinds of
|
119
|
+
# helpers with the following behavior:
|
120
|
+
#
|
121
|
+
# String or Symbol:: :FooBar or "FooBar" becomes "foo_bar_helper",
|
122
|
+
# and "foo_bar_helper.rb" is loaded using require_dependency.
|
123
|
+
#
|
124
|
+
# Module:: No further processing
|
125
|
+
#
|
126
|
+
# After loading the appropriate files, the corresponding modules
|
127
|
+
# are returned.
|
128
|
+
#
|
129
|
+
# ==== Parameters
|
130
|
+
# args<Array[String, Symbol, Module]>:: A list of helpers
|
131
|
+
#
|
132
|
+
# ==== Returns
|
133
|
+
# Array[Module]:: A normalized list of modules for the list of
|
134
|
+
# helpers provided.
|
135
|
+
def _modules_for_helpers(args)
|
136
|
+
args.flatten.map! do |arg|
|
137
|
+
case arg
|
138
|
+
when String, Symbol
|
139
|
+
file_name = "#{arg.to_s.underscore}_helper"
|
140
|
+
require_dependency(file_name, "Missing helper file helpers/%s.rb")
|
141
|
+
file_name.camelize.constantize
|
142
|
+
when Module
|
143
|
+
arg
|
144
|
+
else
|
145
|
+
raise ArgumentError, "helper must be a String, Symbol, or Module"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,250 @@
|
|
1
|
+
module AbstractController
|
2
|
+
module Layouts
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
include RenderingController
|
6
|
+
|
7
|
+
included do
|
8
|
+
extlib_inheritable_accessor(:_layout_conditions) { Hash.new }
|
9
|
+
extlib_inheritable_accessor(:_action_has_layout) { Hash.new }
|
10
|
+
_write_layout_method
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def inherited(klass)
|
15
|
+
super
|
16
|
+
klass.class_eval do
|
17
|
+
_write_layout_method
|
18
|
+
@found_layouts = {}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def clear_template_caches!
|
23
|
+
@found_layouts.clear if @found_layouts
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
def cache_layout(details)
|
28
|
+
layout = @found_layouts
|
29
|
+
key = Thread.current[:format_locale_key]
|
30
|
+
|
31
|
+
# Cache nil
|
32
|
+
if layout.key?(key)
|
33
|
+
return layout[key]
|
34
|
+
else
|
35
|
+
layout[key] = yield
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# This module is mixed in if layout conditions are provided. This means
|
40
|
+
# that if no layout conditions are used, this method is not used
|
41
|
+
module LayoutConditions
|
42
|
+
# Determines whether the current action has a layout by checking the
|
43
|
+
# action name against the :only and :except conditions set on the
|
44
|
+
# layout.
|
45
|
+
#
|
46
|
+
# ==== Returns
|
47
|
+
# Boolean:: True if the action has a layout, false otherwise.
|
48
|
+
def _action_has_layout?
|
49
|
+
conditions = _layout_conditions
|
50
|
+
|
51
|
+
if only = conditions[:only]
|
52
|
+
only.include?(action_name)
|
53
|
+
elsif except = conditions[:except]
|
54
|
+
!except.include?(action_name)
|
55
|
+
else
|
56
|
+
true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Specify the layout to use for this class.
|
62
|
+
#
|
63
|
+
# If the specified layout is a:
|
64
|
+
# String:: the String is the template name
|
65
|
+
# Symbol:: call the method specified by the symbol, which will return
|
66
|
+
# the template name
|
67
|
+
# false:: There is no layout
|
68
|
+
# true:: raise an ArgumentError
|
69
|
+
#
|
70
|
+
# ==== Parameters
|
71
|
+
# layout<String, Symbol, false)>:: The layout to use.
|
72
|
+
#
|
73
|
+
# ==== Options (conditions)
|
74
|
+
# :only<#to_s, Array[#to_s]>:: A list of actions to apply this layout to.
|
75
|
+
# :except<#to_s, Array[#to_s]>:: Apply this layout to all actions but this one
|
76
|
+
def layout(layout, conditions = {})
|
77
|
+
include LayoutConditions unless conditions.empty?
|
78
|
+
|
79
|
+
conditions.each {|k, v| conditions[k] = Array(v).map {|a| a.to_s} }
|
80
|
+
self._layout_conditions = conditions
|
81
|
+
|
82
|
+
@_layout = layout || false # Converts nil to false
|
83
|
+
_write_layout_method
|
84
|
+
end
|
85
|
+
|
86
|
+
# If no layout is supplied, look for a template named the return
|
87
|
+
# value of this method.
|
88
|
+
#
|
89
|
+
# ==== Returns
|
90
|
+
# String:: A template name
|
91
|
+
def _implied_layout_name
|
92
|
+
name && name.underscore
|
93
|
+
end
|
94
|
+
|
95
|
+
# Takes the specified layout and creates a _layout method to be called
|
96
|
+
# by _default_layout
|
97
|
+
#
|
98
|
+
# If there is no explicit layout specified:
|
99
|
+
# If a layout is found in the view paths with the controller's
|
100
|
+
# name, return that string. Otherwise, use the superclass'
|
101
|
+
# layout (which might also be implied)
|
102
|
+
def _write_layout_method
|
103
|
+
case @_layout
|
104
|
+
when String
|
105
|
+
self.class_eval %{def _layout(details) #{@_layout.inspect} end}
|
106
|
+
when Symbol
|
107
|
+
self.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
|
108
|
+
def _layout(details)
|
109
|
+
#{@_layout}.tap do |layout|
|
110
|
+
unless layout.is_a?(String) || !layout
|
111
|
+
raise ArgumentError, "Your layout method :#{@_layout} returned \#{layout}. It " \
|
112
|
+
"should have returned a String, false, or nil"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
ruby_eval
|
117
|
+
when false
|
118
|
+
self.class_eval %{def _layout(details) end}
|
119
|
+
when true
|
120
|
+
raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil"
|
121
|
+
when nil
|
122
|
+
if name
|
123
|
+
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
124
|
+
def _layout(details)
|
125
|
+
self.class.cache_layout(details) do
|
126
|
+
if template_exists?("#{_implied_layout_name}", details, :_prefix => "layouts")
|
127
|
+
"#{_implied_layout_name}"
|
128
|
+
else
|
129
|
+
super
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
RUBY
|
134
|
+
end
|
135
|
+
end
|
136
|
+
self.class_eval { private :_layout }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def render_to_body(options = {})
|
141
|
+
# In the case of a partial with a layout, handle the layout
|
142
|
+
# here, and make sure the view does not try to handle it
|
143
|
+
layout = options.delete(:layout) if options.key?(:partial)
|
144
|
+
|
145
|
+
response = super
|
146
|
+
|
147
|
+
# This is a little bit messy. We need to explicitly handle partial
|
148
|
+
# layouts here since the core lookup logic is in the view, but
|
149
|
+
# we need to determine the layout based on the controller
|
150
|
+
#
|
151
|
+
# TODO: An easier way to handle this would probably be to override
|
152
|
+
# render_template
|
153
|
+
if layout
|
154
|
+
layout = _layout_for_option(layout, options[:_template].details)
|
155
|
+
response = layout.render(view_context, options[:locals] || {}) { response }
|
156
|
+
end
|
157
|
+
|
158
|
+
response
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
|
163
|
+
# This will be overwritten by _write_layout_method
|
164
|
+
def _layout(details) end
|
165
|
+
|
166
|
+
# Determine the layout for a given name and details.
|
167
|
+
#
|
168
|
+
# ==== Parameters
|
169
|
+
# name<String>:: The name of the template
|
170
|
+
# details<Hash{Symbol => Object}>:: A list of details to restrict
|
171
|
+
# the lookup to. By default, layout lookup is limited to the
|
172
|
+
# formats specified for the current request.
|
173
|
+
def _layout_for_name(name, details)
|
174
|
+
name && _find_layout(name, details)
|
175
|
+
end
|
176
|
+
|
177
|
+
# Determine the layout for a given name and details, taking into account
|
178
|
+
# the name type.
|
179
|
+
#
|
180
|
+
# ==== Parameters
|
181
|
+
# name<String|TrueClass|FalseClass|Symbol>:: The name of the template
|
182
|
+
# details<Hash{Symbol => Object}>:: A list of details to restrict
|
183
|
+
# the lookup to. By default, layout lookup is limited to the
|
184
|
+
# formats specified for the current request.
|
185
|
+
def _layout_for_option(name, details)
|
186
|
+
case name
|
187
|
+
when String then _layout_for_name(name, details)
|
188
|
+
when true then _default_layout(details, true)
|
189
|
+
when :default then _default_layout(details, false)
|
190
|
+
when false, nil then nil
|
191
|
+
else
|
192
|
+
raise ArgumentError,
|
193
|
+
"String, true, or false, expected for `layout'; you passed #{name.inspect}"
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def _determine_template(options)
|
198
|
+
super
|
199
|
+
|
200
|
+
return unless (options.keys & [:text, :inline, :partial]).empty? || options.key?(:layout)
|
201
|
+
layout = options.key?(:layout) ? options[:layout] : :default
|
202
|
+
options[:_layout] = _layout_for_option(layout, options[:_template].details)
|
203
|
+
end
|
204
|
+
|
205
|
+
# Take in the name and details and find a Template.
|
206
|
+
#
|
207
|
+
# ==== Parameters
|
208
|
+
# name<String>:: The name of the template to retrieve
|
209
|
+
# details<Hash>:: A list of details to restrict the search by. This
|
210
|
+
# might include details like the format or locale of the template.
|
211
|
+
#
|
212
|
+
# ==== Returns
|
213
|
+
# Template:: A template object matching the name and details
|
214
|
+
def _find_layout(name, details)
|
215
|
+
# TODO: Make prefix actually part of details in ViewPath#find_by_parts
|
216
|
+
prefix = details.key?(:prefix) ? details.delete(:prefix) : "layouts"
|
217
|
+
find_template(name, details, :_prefix => prefix)
|
218
|
+
end
|
219
|
+
|
220
|
+
# Returns the default layout for this controller and a given set of details.
|
221
|
+
# Optionally raises an exception if the layout could not be found.
|
222
|
+
#
|
223
|
+
# ==== Parameters
|
224
|
+
# details<Hash>:: A list of details to restrict the search by. This
|
225
|
+
# might include details like the format or locale of the template.
|
226
|
+
# require_layout<Boolean>:: If this is true, raise an ArgumentError
|
227
|
+
# with details about the fact that the exception could not be
|
228
|
+
# found (defaults to false)
|
229
|
+
#
|
230
|
+
# ==== Returns
|
231
|
+
# Template:: The template object for the default layout (or nil)
|
232
|
+
def _default_layout(details, require_layout = false)
|
233
|
+
if require_layout && _action_has_layout? && !_layout(details)
|
234
|
+
raise ArgumentError,
|
235
|
+
"There was no default layout for #{self.class} in #{view_paths.inspect}"
|
236
|
+
end
|
237
|
+
|
238
|
+
begin
|
239
|
+
_layout_for_name(_layout(details), details) if _action_has_layout?
|
240
|
+
rescue NameError => e
|
241
|
+
raise NoMethodError,
|
242
|
+
"You specified #{@_layout.inspect} as the layout, but no such method was found"
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def _action_has_layout?
|
247
|
+
true
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|