roda 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|