roda 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +26 -0
- data/README.rdoc +83 -22
- data/Rakefile +1 -1
- data/doc/release_notes/2.1.0.txt +124 -0
- data/lib/roda/plugins/assets.rb +17 -9
- data/lib/roda/plugins/class_level_routing.rb +5 -2
- data/lib/roda/plugins/delegate.rb +6 -3
- data/lib/roda/plugins/indifferent_params.rb +7 -0
- data/lib/roda/plugins/mailer.rb +18 -1
- data/lib/roda/plugins/multi_route.rb +2 -1
- data/lib/roda/plugins/path.rb +75 -6
- data/lib/roda/plugins/render.rb +33 -14
- data/lib/roda/plugins/static.rb +35 -0
- data/lib/roda/plugins/view_options.rb +161 -0
- data/lib/roda/plugins/view_subdirs.rb +6 -63
- data/lib/roda/version.rb +1 -1
- data/spec/composition_spec.rb +12 -0
- data/spec/matchers_spec.rb +34 -0
- data/spec/plugin/assets_spec.rb +112 -17
- data/spec/plugin/delete_empty_headers_spec.rb +12 -0
- data/spec/plugin/mailer_spec.rb +46 -3
- data/spec/plugin/module_include_spec.rb +17 -0
- data/spec/plugin/multi_route_spec.rb +10 -0
- data/spec/plugin/named_templates_spec.rb +6 -0
- data/spec/plugin/not_found_spec.rb +1 -1
- data/spec/plugin/path_spec.rb +76 -0
- data/spec/plugin/render_each_spec.rb +6 -0
- data/spec/plugin/render_spec.rb +40 -1
- data/spec/plugin/sinatra_helpers_spec.rb +5 -0
- data/spec/plugin/static_spec.rb +30 -0
- data/spec/plugin/view_options_spec.rb +117 -0
- data/spec/spec_helper.rb +5 -1
- data/spec/views/multiple-layout.erb +1 -0
- data/spec/views/multiple.erb +1 -0
- metadata +10 -4
- data/spec/plugin/static_path_info_spec.rb +0 -56
- data/spec/plugin/view_subdirs_spec.rb +0 -44
@@ -21,21 +21,24 @@ class Roda
|
|
21
21
|
#
|
22
22
|
# # /hello branch
|
23
23
|
# on "hello" do
|
24
|
+
# # Set variable for all routes in /hello branch
|
25
|
+
# @greeting = 'Hello'
|
26
|
+
#
|
24
27
|
# # GET /hello/world request
|
25
28
|
# get "world" do
|
26
|
-
# "
|
29
|
+
# "#{@greeting} world!"
|
27
30
|
# end
|
28
31
|
#
|
29
32
|
# # /hello request
|
30
33
|
# is do
|
31
34
|
# # GET /hello request
|
32
35
|
# get do
|
33
|
-
# "
|
36
|
+
# "#{@greeting}!"
|
34
37
|
# end
|
35
38
|
#
|
36
39
|
# # POST /hello request
|
37
40
|
# post do
|
38
|
-
# puts "Someone said
|
41
|
+
# puts "Someone said #{@greeting}!"
|
39
42
|
# redirect
|
40
43
|
# end
|
41
44
|
# end
|
@@ -14,6 +14,13 @@ class Roda
|
|
14
14
|
# The params hash is initialized lazily, so you only pay
|
15
15
|
# the penalty of copying the request params if you call
|
16
16
|
# the +params+ method.
|
17
|
+
#
|
18
|
+
# Note that there is a rack-indifferent gem that
|
19
|
+
# automatically makes rack use indifferent params. Using
|
20
|
+
# rack-indifferent is faster and has some other minor
|
21
|
+
# advantages over the indifferent_params plugin, though
|
22
|
+
# it affects rack itself instead of just the Roda app that
|
23
|
+
# you load the plugin into.
|
17
24
|
module IndifferentParams
|
18
25
|
module InstanceMethods
|
19
26
|
# A copy of the request params that will automatically
|
data/lib/roda/plugins/mailer.rb
CHANGED
@@ -168,6 +168,10 @@ class Roda
|
|
168
168
|
header_content_type = @headers.delete(CONTENT_TYPE)
|
169
169
|
m.headers(@headers)
|
170
170
|
m.body(@body.join) unless @body.empty?
|
171
|
+
mail_attachments.each do |a, block|
|
172
|
+
m.add_file(*a)
|
173
|
+
block.call if block
|
174
|
+
end
|
171
175
|
|
172
176
|
if content_type = header_content_type || roda_class.opts[:mailer][:content_type]
|
173
177
|
if mail.multipart?
|
@@ -188,11 +192,16 @@ class Roda
|
|
188
192
|
super
|
189
193
|
end
|
190
194
|
end
|
195
|
+
|
196
|
+
# The attachments related to the current mail.
|
197
|
+
def mail_attachments
|
198
|
+
@mail_attachments ||= []
|
199
|
+
end
|
191
200
|
end
|
192
201
|
|
193
202
|
module InstanceMethods
|
194
203
|
# Add delegates for common email methods.
|
195
|
-
[:from, :to, :cc, :bcc, :subject
|
204
|
+
[:from, :to, :cc, :bcc, :subject].each do |meth|
|
196
205
|
define_method(meth) do |*args|
|
197
206
|
env[RODA_MAIL].send(meth, *args)
|
198
207
|
nil
|
@@ -215,6 +224,14 @@ class Roda
|
|
215
224
|
end
|
216
225
|
end
|
217
226
|
|
227
|
+
# Delay adding a file to the message until after the message body has been set.
|
228
|
+
# If a block is given, the block is called after the file has been added, and you
|
229
|
+
# can access the attachment via <tt>response.mail.attachments.last</tt>.
|
230
|
+
def add_file(*a, &block)
|
231
|
+
response.mail_attachments << [a, block]
|
232
|
+
nil
|
233
|
+
end
|
234
|
+
|
218
235
|
private
|
219
236
|
|
220
237
|
# Set the text_part or html_part (depending on the method) in the related email,
|
@@ -147,7 +147,8 @@ class Roda
|
|
147
147
|
|
148
148
|
# The names for the currently stored named routes
|
149
149
|
def named_routes(namespace=nil)
|
150
|
-
opts[:namespaced_routes][namespace]
|
150
|
+
routes = opts[:namespaced_routes][namespace]
|
151
|
+
routes ? routes.keys : []
|
151
152
|
end
|
152
153
|
|
153
154
|
# Return the named route with the given name.
|
data/lib/roda/plugins/path.rb
CHANGED
@@ -3,40 +3,93 @@ class Roda
|
|
3
3
|
# The path plugin adds support for named paths. Using the +path+ class method, you can
|
4
4
|
# easily create <tt>*_path</tt> instance methods for each named path. Those instance
|
5
5
|
# methods can then be called if you need to get the path for a form action, link,
|
6
|
-
# redirect, or anything else.
|
6
|
+
# redirect, or anything else.
|
7
|
+
#
|
8
|
+
# Additionally, you can call the +path+ class method with a class and a block, and it will register
|
9
|
+
# the class. You can then call the +path+ instance method with an instance of that class, and it will
|
10
|
+
# instance_exec the block with the arguments provided to path.
|
11
|
+
#
|
12
|
+
# Example:
|
7
13
|
#
|
8
14
|
# plugin :path
|
9
15
|
# path :foo, '/foo'
|
10
16
|
# path :bar do |bar|
|
11
17
|
# "/bar/#{bar.id}"
|
12
18
|
# end
|
19
|
+
# path Baz do |baz, *paths|
|
20
|
+
# "/baz/#{baz.id}/#{paths.join('/')}"
|
21
|
+
# end
|
22
|
+
# path Quux |quux, path|
|
23
|
+
# "/quux/#{quux.id}/#{path}"
|
24
|
+
# end
|
13
25
|
#
|
14
26
|
# route do |r|
|
27
|
+
# r.post 'foo' do
|
28
|
+
# r.redirect foo_path # /foo
|
29
|
+
# end
|
30
|
+
#
|
15
31
|
# r.post 'bar' do
|
16
32
|
# bar = Bar.create(r.params['bar'])
|
17
|
-
# r.redirect bar_path(bar)
|
33
|
+
# r.redirect bar_path(bar) # /bar/1
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# r.post 'baz' do
|
37
|
+
# bar = Baz[1]
|
38
|
+
# r.redirect path(baz, 'c', 'd') # /baz/1/c/d
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# r.post 'quux' do
|
42
|
+
# bar = Quux[1]
|
43
|
+
# r.redirect path(quux, '/bar') # /quux/1/bar
|
18
44
|
# end
|
19
45
|
# end
|
20
46
|
#
|
21
|
-
# The path method accepts the following options:
|
47
|
+
# The path method accepts the following options when not called with a class:
|
22
48
|
#
|
23
|
-
# :add_script_name :: Prefix the path generated with SCRIPT_NAME.
|
49
|
+
# :add_script_name :: Prefix the path generated with SCRIPT_NAME. This defaults to the app's
|
50
|
+
# :add_script_name option.
|
24
51
|
# :name :: Provide a different name for the method, instead of using <tt>*_path</tt>.
|
25
52
|
# :url :: Create a url method in addition to the path method, which will prefix the string generated
|
26
53
|
# with the appropriate scheme, host, and port. If true, creates a <tt>*_url</tt>
|
27
54
|
# method. If a Symbol or String, uses the value as the url method name.
|
28
55
|
# :url_only :: Do not create a path method, just a url method.
|
29
56
|
#
|
30
|
-
# Note that if :add_script_name, :url, or :url_only is used,
|
57
|
+
# Note that if :add_script_name, :url, or :url_only is used, will also create a <tt>_*_path</tt>
|
31
58
|
# method. This is necessary in order to support path methods that accept blocks, as you can't pass
|
32
59
|
# a block to a block that is instance_execed.
|
33
60
|
module Path
|
34
61
|
DEFAULT_PORTS = {'http' => 80, 'https' => 443}.freeze
|
35
62
|
OPTS = {}.freeze
|
36
63
|
|
64
|
+
# Initialize the path classes when loading the plugin
|
65
|
+
def self.configure(app)
|
66
|
+
app.instance_eval do
|
67
|
+
@path_classes ||= {}
|
68
|
+
unless @path_classes[String]
|
69
|
+
path(String){|str| str}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
37
74
|
module ClassMethods
|
75
|
+
# Hash of recognizes classes for path instance method. Keys are classes, values are procs.
|
76
|
+
attr_reader :path_classes
|
77
|
+
|
78
|
+
# Freeze the path classes when freezing the app.
|
79
|
+
def freeze
|
80
|
+
@path_classes.freeze
|
81
|
+
super
|
82
|
+
end
|
83
|
+
|
38
84
|
# Create a new instance method for the named path. See plugin module documentation for options.
|
39
85
|
def path(name, path=nil, opts=OPTS, &block)
|
86
|
+
if name.is_a?(Class)
|
87
|
+
raise RodaError, "can't provide path or options when calling path with a class" unless path.nil? && opts.empty?
|
88
|
+
raise RodaError, "must provide a block when calling path with a class" unless block
|
89
|
+
@path_classes[name] = block
|
90
|
+
return
|
91
|
+
end
|
92
|
+
|
40
93
|
if path.is_a?(Hash)
|
41
94
|
raise RodaError, "cannot provide two option hashses to Roda.path" unless opts.empty?
|
42
95
|
opts = path
|
@@ -53,7 +106,7 @@ class Roda
|
|
53
106
|
|
54
107
|
meth = opts[:name] || "#{name}_path"
|
55
108
|
url = opts[:url]
|
56
|
-
add_script_name = opts[:add_script_name]
|
109
|
+
add_script_name = opts.fetch(:add_script_name, self.opts[:add_script_name])
|
57
110
|
|
58
111
|
if add_script_name || url || opts[:url_only]
|
59
112
|
_meth = "_#{meth}"
|
@@ -93,6 +146,22 @@ class Roda
|
|
93
146
|
nil
|
94
147
|
end
|
95
148
|
end
|
149
|
+
|
150
|
+
module InstanceMethods
|
151
|
+
# Return a path based on the class of the object. The object passed must have
|
152
|
+
# had its class previously registered with the application. If the app's
|
153
|
+
# :add_script_name option is true, this prepends the SCRIPT_NAME to the path.
|
154
|
+
def path(obj, *args)
|
155
|
+
app = self.class
|
156
|
+
if blk = app.path_classes[obj.class]
|
157
|
+
path = instance_exec(obj, *args, &blk)
|
158
|
+
path = request.script_name.to_s + path if app.opts[:add_script_name]
|
159
|
+
path
|
160
|
+
else
|
161
|
+
raise RodaError, "unrecognized object given to Roda#path: #{obj.inspect}"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
96
165
|
end
|
97
166
|
|
98
167
|
register_plugin(:path, Path)
|
data/lib/roda/plugins/render.rb
CHANGED
@@ -38,8 +38,8 @@ class Roda
|
|
38
38
|
# from the default options.
|
39
39
|
# :template_opts :: The tilt options used when rendering templates, defaults to
|
40
40
|
# <tt>{:outvar=>'@_out_buf', :default_encoding=>Encoding.default_external}</tt>.
|
41
|
-
# :views :: The directory holding the view files, defaults to 'views'
|
42
|
-
#
|
41
|
+
# :views :: The directory holding the view files, defaults to the 'views' subdirectory of the
|
42
|
+
# application's :root option (the process's working directory by default).
|
43
43
|
#
|
44
44
|
# Most of these options can be overridden at runtime by passing options
|
45
45
|
# to the +view+ or +render+ methods:
|
@@ -86,20 +86,20 @@ class Roda
|
|
86
86
|
|
87
87
|
# Setup default rendering options. See Render for details.
|
88
88
|
def self.configure(app, opts=OPTS)
|
89
|
-
orig_opts = opts
|
90
89
|
if app.opts[:render]
|
91
|
-
|
92
|
-
else
|
93
|
-
app.opts[:render] = opts.dup
|
90
|
+
opts = app.opts[:render][:orig_opts].merge(opts)
|
94
91
|
end
|
92
|
+
app.opts[:render] = opts.dup
|
93
|
+
app.opts[:render][:orig_opts] = opts
|
95
94
|
|
96
95
|
opts = app.opts[:render]
|
97
96
|
opts[:engine] ||= "erb"
|
98
97
|
opts[:ext] = nil unless opts.has_key?(:ext)
|
99
|
-
opts[:views]
|
98
|
+
opts[:views] = File.expand_path(opts[:views]||"views", app.opts[:root])
|
100
99
|
opts[:layout_opts] = (opts[:layout_opts] || {}).dup
|
100
|
+
opts[:layout_opts][:_is_layout] = true
|
101
101
|
|
102
|
-
if layout =
|
102
|
+
if layout = opts.fetch(:layout, true)
|
103
103
|
opts[:layout] = true unless opts.has_key?(:layout)
|
104
104
|
|
105
105
|
case layout
|
@@ -196,8 +196,8 @@ class Roda
|
|
196
196
|
end
|
197
197
|
end
|
198
198
|
|
199
|
-
# Given the template name and options,
|
200
|
-
#
|
199
|
+
# Given the template name and options, set the template class, template path/content,
|
200
|
+
# template block, and locals to use for the render in the passed options.
|
201
201
|
def find_template(opts)
|
202
202
|
if content = opts[:inline]
|
203
203
|
path = opts[:path] = content
|
@@ -221,6 +221,14 @@ class Roda
|
|
221
221
|
opts[:key] = key
|
222
222
|
end
|
223
223
|
|
224
|
+
if !opts[:_is_layout] && (r_locals = render_opts[:locals])
|
225
|
+
opts[:locals] = if locals = opts[:locals]
|
226
|
+
r_locals.merge(locals)
|
227
|
+
else
|
228
|
+
r_locals
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
224
232
|
opts
|
225
233
|
end
|
226
234
|
|
@@ -230,6 +238,12 @@ class Roda
|
|
230
238
|
opts.merge(template)
|
231
239
|
end
|
232
240
|
|
241
|
+
# The default render options to use. These set defaults that can be overridden by
|
242
|
+
# providing a :layout_opts option to the view/render method.
|
243
|
+
def render_layout_opts
|
244
|
+
render_opts[:layout_opts].dup
|
245
|
+
end
|
246
|
+
|
233
247
|
# The name to use for the template. By default, just converts the :template option to a string.
|
234
248
|
def template_name(opts)
|
235
249
|
opts[:template].to_s
|
@@ -246,10 +260,15 @@ class Roda
|
|
246
260
|
# used, return nil.
|
247
261
|
def view_layout_opts(opts)
|
248
262
|
if layout = opts.fetch(:layout, render_opts[:layout])
|
249
|
-
layout_opts =
|
250
|
-
|
251
|
-
|
252
|
-
|
263
|
+
layout_opts = render_layout_opts
|
264
|
+
if l_opts = opts[:layout_opts]
|
265
|
+
if (l_locals = l_opts[:locals]) && (layout_locals = layout_opts[:locals])
|
266
|
+
set_locals = layout_locals.merge(l_locals)
|
267
|
+
end
|
268
|
+
layout_opts.merge!(l_opts)
|
269
|
+
if set_locals
|
270
|
+
layout_opts[:locals] = set_locals
|
271
|
+
end
|
253
272
|
end
|
254
273
|
|
255
274
|
case layout
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Roda
|
2
|
+
module RodaPlugins
|
3
|
+
# The static plugin loads the Rack::Static middleware into the application.
|
4
|
+
# It mainly exists to make serving static files simpler, by supplying
|
5
|
+
# defaults to Rack::Static that are appropriate for Roda.
|
6
|
+
#
|
7
|
+
# The static plugin recognizes the application's :root option, and by default
|
8
|
+
# sets the Rack::Static +:root+ option to the +public+ subfolder of the application's
|
9
|
+
# +:root+ option. Additionally, if a relative path is provided as the :root
|
10
|
+
# option to the plugin, it will be considered relative to the application's
|
11
|
+
# +:root+ option.
|
12
|
+
#
|
13
|
+
# Since the :urls option for Rack::Static is always required, the static plugin
|
14
|
+
# uses a separate option for it.
|
15
|
+
#
|
16
|
+
# Examples:
|
17
|
+
#
|
18
|
+
# opts[:root] = '/path/to/app'
|
19
|
+
# plugin :static, ['/js', '/css'] # path: /path/to/app/public
|
20
|
+
# plugin :static, ['/images'], :root=>'pub' # path: /path/to/app/pub
|
21
|
+
# plugin :static, ['/media'], :root=>'/path/to/public' # path: /path/to/public
|
22
|
+
module Static
|
23
|
+
# Load the Rack::Static middleware. Use the paths given as the :urls option,
|
24
|
+
# and set the :root option to be relative to the application's :root option.
|
25
|
+
def self.configure(app, paths, opts={})
|
26
|
+
opts = opts.dup
|
27
|
+
opts[:urls] = paths
|
28
|
+
opts[:root] = File.expand_path(opts[:root]||"public", app.opts[:root])
|
29
|
+
app.use ::Rack::Static, opts
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
register_plugin(:static, Static)
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
class Roda
|
2
|
+
module RodaPlugins
|
3
|
+
# The view_options plugin allows you to override view and layout
|
4
|
+
# options and locals for specific branches and routes.
|
5
|
+
#
|
6
|
+
# plugin :render
|
7
|
+
# plugin :view_options
|
8
|
+
#
|
9
|
+
# route do |r|
|
10
|
+
# r.on "users" do
|
11
|
+
# set_layout_options :template=>'users_layout'
|
12
|
+
# set_layout_locals :title=>'Users'
|
13
|
+
# set_view_options :engine=>'haml'
|
14
|
+
# set_view_locals :footer=>'(c) Roda'
|
15
|
+
#
|
16
|
+
# # ...
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# The options and locals you specify have higher precedence than
|
21
|
+
# the render plugin options, but lower precedence than options
|
22
|
+
# you directly pass to the view/render methods.
|
23
|
+
#
|
24
|
+
# The view_options plugin also has special support for sites
|
25
|
+
# that have outgrown a flat view directory and use subdirectories
|
26
|
+
# for views. It allows you to set the view directory to
|
27
|
+
# use, and template names that do not contain a slash will
|
28
|
+
# automatically use that view subdirectory. Example:
|
29
|
+
#
|
30
|
+
# plugin :render, :layout=>'./layout'
|
31
|
+
# plugin :view_options
|
32
|
+
#
|
33
|
+
# route do |r|
|
34
|
+
# r.on "users" do
|
35
|
+
# set_view_subdir 'users'
|
36
|
+
#
|
37
|
+
# r.get :id do
|
38
|
+
# append_view_subdir 'profile'
|
39
|
+
# view 'index' # uses ./views/users/profile/index.erb
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# r.get 'list' do
|
43
|
+
# view 'lists/users' # uses ./views/lists/users.erb
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# Note that when a view subdirectory is set, the layout will
|
49
|
+
# also be looked up in the subdirectory unless it contains
|
50
|
+
# a slash. So if you want to use a view subdirectory for
|
51
|
+
# templates but have a shared layout, you should make sure your
|
52
|
+
# layout contains a slash, similar to the example above.
|
53
|
+
module ViewOptions
|
54
|
+
# Load the render plugin before this plugin, since this plugin
|
55
|
+
# works by overriding methods in the render plugin.
|
56
|
+
def self.load_dependencies(app)
|
57
|
+
app.plugin :render
|
58
|
+
end
|
59
|
+
|
60
|
+
# The following methods are created via metaprogramming:
|
61
|
+
# set_layout_locals :: Set locals to use in the layout
|
62
|
+
# set_layout_options :: Set options to use when rendering the layout
|
63
|
+
# set_view_locals :: Set locals to use in the view
|
64
|
+
# set_view_options :: Set options to use when rendering the view
|
65
|
+
module InstanceMethods
|
66
|
+
%w'layout view'.each do |type|
|
67
|
+
%w'locals options'.each do |var|
|
68
|
+
v = "_#{type}_#{var}"
|
69
|
+
module_eval(<<-END, __FILE__, __LINE__+1)
|
70
|
+
def set#{v}(opts)
|
71
|
+
if @#{v}
|
72
|
+
@#{v} = @#{v}.merge(opts)
|
73
|
+
else
|
74
|
+
@#{v} = opts
|
75
|
+
end
|
76
|
+
end
|
77
|
+
END
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Append a view subdirectory to use. If there hasn't already
|
82
|
+
# been a view subdirectory set, this just sets it to the argument.
|
83
|
+
# If there has already been a view subdirectory set, this sets
|
84
|
+
# the view subdirectory to a subdirectory of the existing
|
85
|
+
# view subdirectory.
|
86
|
+
def append_view_subdir(v)
|
87
|
+
if subdir = @_view_subdir
|
88
|
+
set_view_subdir("#{subdir}/#{v}")
|
89
|
+
else
|
90
|
+
set_view_subdir(v)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Set the view subdirectory to use. This can be set to nil
|
95
|
+
# to not use a view subdirectory.
|
96
|
+
def set_view_subdir(v)
|
97
|
+
@_view_subdir = v
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
# If view options or locals have been set and this
|
103
|
+
# template isn't a layout template, merge the options
|
104
|
+
# and locals into the returned hash.
|
105
|
+
def parse_template_opts(template, opts)
|
106
|
+
t_opts = super
|
107
|
+
|
108
|
+
unless t_opts[:_is_layout]
|
109
|
+
if v_opts = @_view_options
|
110
|
+
t_opts.merge!(v_opts)
|
111
|
+
end
|
112
|
+
|
113
|
+
if v_locals = @_view_locals
|
114
|
+
t_opts[:locals] = if t_locals = t_opts[:locals]
|
115
|
+
v_locals.merge(t_locals)
|
116
|
+
else
|
117
|
+
v_locals
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
t_opts
|
123
|
+
end
|
124
|
+
|
125
|
+
# If layout options or locals have been set,
|
126
|
+
# merge the options and locals into the returned hash.
|
127
|
+
def render_layout_opts
|
128
|
+
opts = super
|
129
|
+
|
130
|
+
if l_opts = @_layout_options
|
131
|
+
opts.merge!(l_opts)
|
132
|
+
end
|
133
|
+
|
134
|
+
if l_locals = @_layout_locals
|
135
|
+
opts[:locals] = if o_locals = opts[:locals]
|
136
|
+
o_locals.merge(l_locals)
|
137
|
+
else
|
138
|
+
l_locals
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
opts
|
143
|
+
end
|
144
|
+
|
145
|
+
# Override the template name to use the view subdirectory if the
|
146
|
+
# there is a view subdirectory and the template name does not
|
147
|
+
# contain a slash.
|
148
|
+
def template_name(opts)
|
149
|
+
name = super
|
150
|
+
if (v = @_view_subdir) && name !~ /\//
|
151
|
+
"#{v}/#{name}"
|
152
|
+
else
|
153
|
+
name
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
register_plugin(:view_options, ViewOptions)
|
160
|
+
end
|
161
|
+
end
|