roda 2.2.0 → 2.3.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 +34 -0
- data/Rakefile +22 -46
- data/doc/release_notes/2.3.0.txt +109 -0
- data/lib/roda/plugins/assets.rb +2 -1
- data/lib/roda/plugins/caching.rb +1 -1
- data/lib/roda/plugins/chunked.rb +1 -1
- data/lib/roda/plugins/error_email.rb +1 -1
- data/lib/roda/plugins/head.rb +6 -0
- data/lib/roda/plugins/heartbeat.rb +40 -0
- data/lib/roda/plugins/json.rb +23 -3
- data/lib/roda/plugins/json_parser.rb +72 -0
- data/lib/roda/plugins/mailer.rb +22 -5
- data/lib/roda/plugins/named_templates.rb +2 -2
- data/lib/roda/plugins/path_rewriter.rb +82 -0
- data/lib/roda/plugins/precompile_templates.rb +87 -0
- data/lib/roda/plugins/render.rb +111 -43
- data/lib/roda/plugins/render_each.rb +1 -1
- data/lib/roda/plugins/shared_vars.rb +1 -1
- data/lib/roda/plugins/view_options.rb +28 -3
- data/lib/roda/version.rb +1 -1
- data/spec/composition_spec.rb +3 -3
- data/spec/env_spec.rb +1 -1
- data/spec/freeze_spec.rb +6 -6
- data/spec/integration_spec.rb +16 -15
- data/spec/matchers_spec.rb +110 -110
- data/spec/opts_spec.rb +8 -8
- data/spec/plugin/_erubis_escaping_spec.rb +34 -3
- data/spec/plugin/all_verbs_spec.rb +8 -8
- data/spec/plugin/assets_spec.rb +164 -150
- data/spec/plugin/backtracking_array_spec.rb +18 -18
- data/spec/plugin/caching_spec.rb +70 -70
- data/spec/plugin/chunked_spec.rb +38 -38
- data/spec/plugin/class_level_routing_spec.rb +78 -78
- data/spec/plugin/content_for_spec.rb +2 -2
- data/spec/plugin/cookies_spec.rb +4 -4
- data/spec/plugin/csrf_spec.rb +8 -8
- data/spec/plugin/default_headers_spec.rb +6 -6
- data/spec/plugin/delay_build_spec.rb +7 -6
- data/spec/plugin/delegate_spec.rb +2 -2
- data/spec/plugin/delete_empty_headers_spec.rb +2 -2
- data/spec/plugin/drop_body_spec.rb +6 -6
- data/spec/plugin/empty_root_spec.rb +3 -3
- data/spec/plugin/environments_spec.rb +7 -7
- data/spec/plugin/error_email_spec.rb +23 -23
- data/spec/plugin/error_handler_spec.rb +14 -14
- data/spec/plugin/flash_spec.rb +30 -29
- data/spec/plugin/h_spec.rb +1 -1
- data/spec/plugin/halt_spec.rb +16 -16
- data/spec/plugin/hash_matcher_spec.rb +5 -5
- data/spec/plugin/head_spec.rb +10 -10
- data/spec/plugin/header_matchers_spec.rb +13 -13
- data/spec/plugin/heartbeat_spec.rb +74 -0
- data/spec/plugin/hooks_spec.rb +20 -20
- data/spec/plugin/indifferent_params_spec.rb +1 -1
- data/spec/plugin/json_parser_spec.rb +72 -0
- data/spec/plugin/json_spec.rb +22 -9
- data/spec/plugin/mailer_spec.rb +72 -58
- data/spec/plugin/match_affix_spec.rb +2 -2
- data/spec/plugin/middleware_spec.rb +7 -7
- data/spec/plugin/module_include_spec.rb +4 -4
- data/spec/plugin/multi_route_spec.rb +66 -66
- data/spec/plugin/multi_run_spec.rb +21 -21
- data/spec/plugin/named_templates_spec.rb +6 -6
- data/spec/plugin/not_allowed_spec.rb +17 -17
- data/spec/plugin/not_found_spec.rb +14 -14
- data/spec/plugin/padrino_render_spec.rb +2 -2
- data/spec/plugin/param_matchers_spec.rb +6 -6
- data/spec/plugin/partials_spec.rb +3 -3
- data/spec/plugin/pass_spec.rb +7 -7
- data/spec/plugin/path_matchers_spec.rb +6 -6
- data/spec/plugin/path_rewriter_spec.rb +37 -0
- data/spec/plugin/path_spec.rb +41 -40
- data/spec/plugin/per_thread_caching_spec.rb +6 -6
- data/spec/plugin/precompile_templates_spec.rb +74 -0
- data/spec/plugin/render_each_spec.rb +4 -4
- data/spec/plugin/render_spec.rb +179 -76
- data/spec/plugin/shared_vars_spec.rb +4 -4
- data/spec/plugin/sinatra_helpers_spec.rb +121 -121
- data/spec/plugin/slash_path_empty_spec.rb +10 -10
- data/spec/plugin/static_spec.rb +4 -4
- data/spec/plugin/streaming_spec.rb +11 -11
- data/spec/plugin/symbol_matchers_spec.rb +24 -24
- data/spec/plugin/symbol_views_spec.rb +3 -3
- data/spec/plugin/view_options_spec.rb +10 -10
- data/spec/plugin_spec.rb +2 -2
- data/spec/redirect_spec.rb +10 -10
- data/spec/request_spec.rb +8 -8
- data/spec/response_spec.rb +23 -23
- data/spec/session_spec.rb +4 -4
- data/spec/spec_helper.rb +5 -19
- data/spec/version_spec.rb +4 -4
- data/spec/views/iv.erb +1 -0
- metadata +16 -5
data/lib/roda/plugins/mailer.rb
CHANGED
@@ -87,6 +87,14 @@ class Roda
|
|
87
87
|
# ""
|
88
88
|
# end
|
89
89
|
#
|
90
|
+
# If while preparing the email you figure out you don't want to send an
|
91
|
+
# email, call +no_mail!+:
|
92
|
+
#
|
93
|
+
# r.mail '/welcome/:d' do |id|
|
94
|
+
# no_mail! unless user = User[id]
|
95
|
+
# # ...
|
96
|
+
# end
|
97
|
+
#
|
90
98
|
# By default, the mailer uses text/plain as the Content-Type for emails.
|
91
99
|
# You can override the default by specifying a :content_type option when
|
92
100
|
# loading the plugin:
|
@@ -128,15 +136,19 @@ class Roda
|
|
128
136
|
# calling +deliver+ to send the mail.
|
129
137
|
def mail(path, *args)
|
130
138
|
mail = ::Mail.new
|
131
|
-
|
132
|
-
|
139
|
+
catch(:no_mail) do
|
140
|
+
unless mail.equal?(new(PATH_INFO=>path, SCRIPT_NAME=>EMPTY_STRING, REQUEST_METHOD=>MAIL, RACK_INPUT=>StringIO.new, RODA_MAIL=>mail, RODA_MAIL_ARGS=>args).call(&route_block))
|
141
|
+
raise Error, "route did not return mail instance for #{path.inspect}, #{args.inspect}"
|
142
|
+
end
|
143
|
+
mail
|
133
144
|
end
|
134
|
-
mail
|
135
145
|
end
|
136
146
|
|
137
147
|
# Calls +mail+ and immediately sends the resulting mail.
|
138
148
|
def sendmail(*args)
|
139
|
-
mail(*args)
|
149
|
+
if m = mail(*args)
|
150
|
+
m.deliver
|
151
|
+
end
|
140
152
|
end
|
141
153
|
end
|
142
154
|
|
@@ -149,7 +161,7 @@ class Roda
|
|
149
161
|
def mail(*args)
|
150
162
|
if @env[REQUEST_METHOD] == MAIL
|
151
163
|
if_match(args) do |*vs|
|
152
|
-
yield
|
164
|
+
yield(*(vs + @env[RODA_MAIL_ARGS]))
|
153
165
|
end
|
154
166
|
end
|
155
167
|
end
|
@@ -232,6 +244,11 @@ class Roda
|
|
232
244
|
nil
|
233
245
|
end
|
234
246
|
|
247
|
+
# Signal that no mail should be sent for this request.
|
248
|
+
def no_mail!
|
249
|
+
throw :no_mail
|
250
|
+
end
|
251
|
+
|
235
252
|
private
|
236
253
|
|
237
254
|
# Set the text_part or html_part (depending on the method) in the related email,
|
@@ -79,9 +79,9 @@ class Roda
|
|
79
79
|
def find_template(options)
|
80
80
|
if options[:template] && (template_opts, block = opts[:named_templates][template_name(options)]; block)
|
81
81
|
if template_opts
|
82
|
-
options = template_opts.merge(options)
|
82
|
+
options = Hash[template_opts].merge!(options)
|
83
83
|
else
|
84
|
-
options = options
|
84
|
+
options = Hash[options]
|
85
85
|
end
|
86
86
|
|
87
87
|
options[:inline] = instance_exec(&block)
|
@@ -0,0 +1,82 @@
|
|
1
|
+
class Roda
|
2
|
+
module RodaPlugins
|
3
|
+
# The path_rewriter plugin allows you to rewrite the remaining path
|
4
|
+
# or the path info for requests. This is useful if you want to
|
5
|
+
# transparently treat some paths the same as other paths.
|
6
|
+
#
|
7
|
+
# By default, +rewrite_path+ will rewrite just the remaining path. So
|
8
|
+
# only routing in the current Roda app will be affected. This is useful
|
9
|
+
# if you have other code in your app that uses PATH_INFO and needs to
|
10
|
+
# see the original PATH_INFO (for example, when using relative links).
|
11
|
+
#
|
12
|
+
# rewrite_path '/a', '/b'
|
13
|
+
# # PATH_INFO '/a' => remaining_path '/b'
|
14
|
+
# # PATH_INFO '/a/c' => remaining_path '/b/c'
|
15
|
+
#
|
16
|
+
# In some cases, you may want to override PATH_INFO for the rewritten
|
17
|
+
# paths, such as when you are passing the request to another Rack app.
|
18
|
+
# For those cases, you can use the <tt>:path_info => true</tt> option to
|
19
|
+
# +rewrite_path+.
|
20
|
+
#
|
21
|
+
# rewrite_path '/a', '/b', :path_info => true
|
22
|
+
# # PATH_INFO '/a' => PATH_INFO '/b'
|
23
|
+
# # PATH_INFO '/a/c' => PATH_INFO '/b/c'
|
24
|
+
#
|
25
|
+
# If you pass a string to +rewrite_path+, it will rewrite all paths starting
|
26
|
+
# with that string. You can provide a regexp if you want more complete control,
|
27
|
+
# such as only matching exact paths.
|
28
|
+
#
|
29
|
+
# rewrite_path /\A\/a\z/, '/b'
|
30
|
+
# # PATH_INFO '/a' => remaining_path '/b'
|
31
|
+
# # PATH_INFO '/a/c' => remaining_path '/a/c', no change
|
32
|
+
#
|
33
|
+
# All path rewrites are applied in order, so if a path is rewritten by one rewrite,
|
34
|
+
# it can be rewritten again by a later rewrite. Note that PATH_INFO rewrites are
|
35
|
+
# processed before remaining_path rewrites.
|
36
|
+
module PathRewriter
|
37
|
+
PATH_INFO = 'PATH_INFO'.freeze
|
38
|
+
OPTS={}.freeze
|
39
|
+
|
40
|
+
def self.configure(app)
|
41
|
+
app.instance_exec do
|
42
|
+
app.opts[:remaining_path_rewrites] ||= []
|
43
|
+
app.opts[:path_info_rewrites] ||= []
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module ClassMethods
|
48
|
+
# Freeze the path rewrite metadata.
|
49
|
+
def freeze
|
50
|
+
opts[:remaining_path_rewrites].freeze
|
51
|
+
opts[:path_info_rewrites].freeze
|
52
|
+
super
|
53
|
+
end
|
54
|
+
|
55
|
+
# Record a path rewrite from path +was+ to path +is+. Options:
|
56
|
+
# :path_info :: Modify PATH_INFO, not just remaining path.
|
57
|
+
def rewrite_path(was, is, opts=OPTS)
|
58
|
+
was = /\A#{Regexp.escape(was)}/ unless was.is_a?(Regexp)
|
59
|
+
array = @opts[opts[:path_info] ? :path_info_rewrites : :remaining_path_rewrites]
|
60
|
+
array << [was, is.dup.freeze].freeze
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
module RequestMethods
|
65
|
+
# Rewrite remaining_path and/or PATH_INFO based on the path rewrites.
|
66
|
+
def initialize(scope, env)
|
67
|
+
path_info = env[PATH_INFO]
|
68
|
+
scope.class.opts[:path_info_rewrites].each do |was, is|
|
69
|
+
path_info.sub!(was, is)
|
70
|
+
end
|
71
|
+
super
|
72
|
+
remaining_path = @remaining_path = @remaining_path.dup
|
73
|
+
scope.class.opts[:remaining_path_rewrites].each do |was, is|
|
74
|
+
remaining_path.sub!(was, is)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
register_plugin(:path_rewriter, PathRewriter)
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
class Roda
|
2
|
+
module RodaPlugins
|
3
|
+
# The precompile_templates plugin adds support for precompiling template code.
|
4
|
+
# This can result in a large memory savings for applications that have large
|
5
|
+
# templates or a large number of small templates if the application uses a
|
6
|
+
# forking webserver. By default, template compilation is lazy, so all the
|
7
|
+
# child processes in a forking webserver will have their own copy of the
|
8
|
+
# compiled template. By using the precompile_templates plugin, you can
|
9
|
+
# precompile the templates in the parent process before forking, and then
|
10
|
+
# all of the child processes can use the same precompiled templates, which
|
11
|
+
# saves memory.
|
12
|
+
#
|
13
|
+
# After loading the plugin, you can call +precompile_templates+ with
|
14
|
+
# the pattern of templates you would like to precompile:
|
15
|
+
#
|
16
|
+
# plugin :precompile_templates
|
17
|
+
# precompile_templates "views/\*\*/*.erb"
|
18
|
+
#
|
19
|
+
# That will precompile all erb template files in the views directory or
|
20
|
+
# any subdirectory.
|
21
|
+
#
|
22
|
+
# If the templates use local variables, you need to specify which local
|
23
|
+
# variables to precompile, which should be an array of symbols:
|
24
|
+
#
|
25
|
+
# precompile_templates 'views/users/_*.erb', :locals=>[:user]
|
26
|
+
#
|
27
|
+
# Note that if you have multiple local variables and are not using a Tilt
|
28
|
+
# version greater than 2.0.1, you should specify the :locals option in the
|
29
|
+
# same order as the keys in the :locals hash you pass to render/view. Since
|
30
|
+
# hashes are not ordered in ruby 1.8, you should not attempt to precompile
|
31
|
+
# templates that use :locals on ruby 1.8 unless you are using a Tilt version
|
32
|
+
# greater than 2.0.1. If you are running the Tilt master branch, you can
|
33
|
+
# force sorting of locals using the +:sort_locals+ option when loading the
|
34
|
+
# plugin.
|
35
|
+
#
|
36
|
+
# You can specify other render options when calling +precompile_templates+,
|
37
|
+
# including +:cache_key+, +:template_class+, and +:template_opts+. If you
|
38
|
+
# are passing any of those options to render/view for the template, you
|
39
|
+
# should pass the same options when precompiling the template.
|
40
|
+
#
|
41
|
+
# To compile inline templates, just pass a single hash containing an :inline
|
42
|
+
# to +precompile_templates+:
|
43
|
+
#
|
44
|
+
# precompile_templates :inline=>some_template_string
|
45
|
+
module PrecompileTemplates
|
46
|
+
OPTS = {}.freeze
|
47
|
+
|
48
|
+
# Load the render plugin as precompile_templates depends on it.
|
49
|
+
# Default to sorting the locals if the Tilt version is greater than 2.0.1.
|
50
|
+
def self.load_dependencies(app, opts=OPTS)
|
51
|
+
app.plugin :render
|
52
|
+
app.opts[:precompile_templates_sort] = opts.fetch(:sort_locals, Tilt::VERSION > '2.0.1')
|
53
|
+
end
|
54
|
+
|
55
|
+
module ClassMethods
|
56
|
+
# Precompile the templates using the given options. See PrecompileTemplates
|
57
|
+
# for details.
|
58
|
+
def precompile_templates(pattern, opts=OPTS)
|
59
|
+
if pattern.is_a?(Hash)
|
60
|
+
opts = pattern.merge(opts)
|
61
|
+
end
|
62
|
+
|
63
|
+
locals = opts[:locals] || []
|
64
|
+
if locals && self.opts[:precompile_templates_sort]
|
65
|
+
locals = locals.sort{|x,y| x.to_s <=> y.to_s}
|
66
|
+
end
|
67
|
+
|
68
|
+
compile_opts = if pattern.is_a?(Hash)
|
69
|
+
[opts]
|
70
|
+
else
|
71
|
+
Dir[pattern].map{|file| opts.merge(:path=>File.expand_path(file))}
|
72
|
+
end
|
73
|
+
|
74
|
+
instance = allocate
|
75
|
+
compile_opts.each do |compile_opt|
|
76
|
+
template = instance.send(:retrieve_template, compile_opt)
|
77
|
+
template.send(:compiled_method, locals)
|
78
|
+
end
|
79
|
+
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
register_plugin(:precompile_templates, PrecompileTemplates)
|
86
|
+
end
|
87
|
+
end
|
data/lib/roda/plugins/render.rb
CHANGED
@@ -22,12 +22,15 @@ class Roda
|
|
22
22
|
#
|
23
23
|
# plugin :render, :engine=>'haml', :views=>'admin_views'
|
24
24
|
#
|
25
|
-
#
|
25
|
+
# = Plugin Options
|
26
|
+
#
|
27
|
+
# The following plugin options are supported:
|
26
28
|
#
|
27
29
|
# :cache :: nil/false to not cache templates (useful for development), defaults
|
28
30
|
# to true unless RACK_ENV is development to automatically use the
|
29
31
|
# default template cache.
|
30
|
-
# :engine :: The tilt engine to use for rendering,
|
32
|
+
# :engine :: The tilt engine to use for rendering, also the default file extension for
|
33
|
+
# templates, defaults to 'erb'.
|
31
34
|
# :escape :: Use Roda's Erubis escaping support, which makes <tt><%= %></tt> escape output,
|
32
35
|
# <tt><%== %></tt> not escape output, and handles postfix conditions inside
|
33
36
|
# <tt><%= %></tt> tags.
|
@@ -36,25 +39,32 @@ class Roda
|
|
36
39
|
# :escaper :: Object used for escaping output of <tt><%= %></tt>, when :escape is used,
|
37
40
|
# overriding the default. If given, object should respond to +escape_xml+ with
|
38
41
|
# a single argument and return an output string.
|
39
|
-
# :ext :: The file extension to assume for view files, defaults to the :engine
|
40
|
-
# option.
|
41
42
|
# :layout :: The base name of the layout file, defaults to 'layout'.
|
42
43
|
# :layout_opts :: The options to use when rendering the layout, if different
|
43
44
|
# from the default options.
|
44
|
-
# :template_opts :: The tilt options used when rendering templates
|
45
|
-
#
|
45
|
+
# :template_opts :: The tilt options used when rendering all templates. defaults to:
|
46
|
+
# <tt>{:outvar=>'@_out_buf', :default_encoding=>Encoding.default_external}</tt>.
|
47
|
+
# :engine_opts :: The tilt options to use per template engine. Keys are
|
48
|
+
# engine strings, values are hashes of template options.
|
46
49
|
# :views :: The directory holding the view files, defaults to the 'views' subdirectory of the
|
47
50
|
# application's :root option (the process's working directory by default).
|
48
51
|
#
|
52
|
+
# = Render/View Method Options
|
53
|
+
#
|
49
54
|
# Most of these options can be overridden at runtime by passing options
|
50
55
|
# to the +view+ or +render+ methods:
|
51
56
|
#
|
52
|
-
# view('foo', :
|
57
|
+
# view('foo', :engine=>'html.erb')
|
53
58
|
# render('foo', :views=>'admin_views')
|
54
59
|
#
|
55
|
-
# There are
|
60
|
+
# There are additional options to +view+ and +render+ that are
|
56
61
|
# available at runtime:
|
57
62
|
#
|
63
|
+
# :cache :: Set to false to not cache this template, even when
|
64
|
+
# caching is on by default. Set to true to force caching for
|
65
|
+
# this template, even when the default is to not cache (e.g.
|
66
|
+
# when using the :template_block option).
|
67
|
+
# :cache_key :: Explicitly set the hash key to use when caching.
|
58
68
|
# :content :: Only respected by +view+, provides the content to render
|
59
69
|
# inside the layout, instead of rendering a template to get
|
60
70
|
# the content.
|
@@ -62,13 +72,14 @@ class Roda
|
|
62
72
|
# for template code in a file.
|
63
73
|
# :locals :: Hash of local variables to make available inside the template.
|
64
74
|
# :path :: Use the value given as the full pathname for the file, instead
|
65
|
-
# of using the :views and :
|
75
|
+
# of using the :views and :engine option in combination with the
|
66
76
|
# template name.
|
67
77
|
# :template :: Provides the name of the template to use. This allows you
|
68
78
|
# pass a single options hash to the render/view method, while
|
69
79
|
# still allowing you to specify the template name.
|
70
80
|
# :template_block :: Pass this block when creating the underlying template,
|
71
|
-
# ignored when using :inline.
|
81
|
+
# ignored when using :inline. Disables caching of the
|
82
|
+
# template by default.
|
72
83
|
# :template_class :: Provides the template class to use, inside of using
|
73
84
|
# Tilt or <tt>Tilt[:engine]</tt>.
|
74
85
|
#
|
@@ -80,6 +91,27 @@ class Roda
|
|
80
91
|
# If you pass a hash as the first argument to +view+ or +render+, it should
|
81
92
|
# have either +:template+, +:inline+, +:path+, or +:content+ (for +view+) as
|
82
93
|
# one of the keys.
|
94
|
+
#
|
95
|
+
# = Speeding Up Template Rendering
|
96
|
+
#
|
97
|
+
# By default, determining the cache key to use for the template can be a lot
|
98
|
+
# of work. If you specify the +:cache_key+ option, you can save Roda from
|
99
|
+
# having to do that work, which will make your application faster. However,
|
100
|
+
# if you do this, you need to make sure you choose a correct key.
|
101
|
+
#
|
102
|
+
# If your application uses a unique template per path, in that the same
|
103
|
+
# path never uses more than one template, you can use the +view_options+ plugin
|
104
|
+
# and do:
|
105
|
+
#
|
106
|
+
# set_view_options :cache_key=>r.path_info
|
107
|
+
#
|
108
|
+
# at the top of your route block. You can even do this if you do have paths
|
109
|
+
# that use more than one template, as long as you specify +:cache_key+
|
110
|
+
# specifically when rendering in those paths.
|
111
|
+
#
|
112
|
+
# If you use a single layout in your application, you can also make layout
|
113
|
+
# rendering faster by specifying +:cache_key+ inside the +:layout_opts+
|
114
|
+
# plugin option.
|
83
115
|
module Render
|
84
116
|
OPTS={}.freeze
|
85
117
|
|
@@ -98,12 +130,12 @@ class Roda
|
|
98
130
|
app.opts[:render][:orig_opts] = opts
|
99
131
|
|
100
132
|
opts = app.opts[:render]
|
101
|
-
opts[:engine]
|
102
|
-
opts[:
|
103
|
-
opts[:
|
133
|
+
opts[:engine] = (opts[:engine] || opts[:ext] || "erb").dup.freeze
|
134
|
+
opts[:views] = File.expand_path(opts[:views]||"views", app.opts[:root]).freeze
|
135
|
+
opts[:cache] = app.thread_safe_cache if opts.fetch(:cache, ENV['RACK_ENV'] != 'development')
|
136
|
+
|
104
137
|
opts[:layout_opts] = (opts[:layout_opts] || {}).dup
|
105
138
|
opts[:layout_opts][:_is_layout] = true
|
106
|
-
|
107
139
|
if layout = opts.fetch(:layout, true)
|
108
140
|
opts[:layout] = true unless opts.has_key?(:layout)
|
109
141
|
|
@@ -116,6 +148,7 @@ class Roda
|
|
116
148
|
opts[:layout_opts][:template] = layout
|
117
149
|
end
|
118
150
|
end
|
151
|
+
opts[:layout_opts].freeze
|
119
152
|
|
120
153
|
template_opts = opts[:template_opts] = (opts[:template_opts] || {}).dup
|
121
154
|
template_opts[:outvar] ||= '@_out_buf'
|
@@ -131,9 +164,14 @@ class Roda
|
|
131
164
|
::Erubis::XmlHelper
|
132
165
|
end
|
133
166
|
end
|
134
|
-
|
135
|
-
|
136
|
-
opts[:
|
167
|
+
template_opts.freeze
|
168
|
+
|
169
|
+
engine_opts = opts[:engine_opts] = (opts[:engine_opts] || {}).dup
|
170
|
+
engine_opts.to_a.each do |k,v|
|
171
|
+
engine_opts[k] = v.dup.freeze
|
172
|
+
end
|
173
|
+
engine_opts.freeze
|
174
|
+
|
137
175
|
opts.freeze
|
138
176
|
end
|
139
177
|
|
@@ -157,13 +195,9 @@ class Roda
|
|
157
195
|
module InstanceMethods
|
158
196
|
# Render the given template. See Render for details.
|
159
197
|
def render(template, opts = OPTS, &block)
|
160
|
-
opts =
|
161
|
-
|
162
|
-
|
163
|
-
current_template_opts = opts[:template_opts]
|
164
|
-
template_opts = template_opts.merge(current_template_opts) if current_template_opts
|
165
|
-
opts[:template_class].new(opts[:path], 1, template_opts, &opts[:template_block])
|
166
|
-
end.render(self, (opts[:locals]||OPTS), &block)
|
198
|
+
opts = parse_template_opts(template, opts)
|
199
|
+
merge_render_locals(opts)
|
200
|
+
retrieve_template(opts).render(self, (opts[:locals]||OPTS), &block)
|
167
201
|
end
|
168
202
|
|
169
203
|
# Return the render options for the instance's class. While this
|
@@ -196,8 +230,7 @@ class Roda
|
|
196
230
|
# If caching templates, attempt to retrieve the template from the cache. Otherwise, just yield
|
197
231
|
# to get the template.
|
198
232
|
def cached_template(opts, &block)
|
199
|
-
if cache = render_opts[:cache]
|
200
|
-
key = opts[:key]
|
233
|
+
if (cache = render_opts[:cache]) && (key = opts[:cache_key])
|
201
234
|
unless template = cache[key]
|
202
235
|
template = cache[key] = yield
|
203
236
|
end
|
@@ -210,49 +243,86 @@ class Roda
|
|
210
243
|
# Given the template name and options, set the template class, template path/content,
|
211
244
|
# template block, and locals to use for the render in the passed options.
|
212
245
|
def find_template(opts)
|
246
|
+
render_opts = render_opts()
|
247
|
+
engine_override = opts[:engine] ||= opts[:ext]
|
248
|
+
engine = opts[:engine] ||= render_opts[:engine]
|
213
249
|
if content = opts[:inline]
|
214
250
|
path = opts[:path] = content
|
215
|
-
template_class = opts[:template_class] ||= ::Tilt[
|
251
|
+
template_class = opts[:template_class] ||= ::Tilt[engine]
|
216
252
|
opts[:template_block] = Proc.new{content}
|
217
253
|
else
|
254
|
+
opts[:views] ||= render_opts[:views]
|
218
255
|
path = opts[:path] ||= template_path(opts)
|
219
256
|
template_class = opts[:template_class]
|
220
257
|
opts[:template_class] ||= ::Tilt
|
221
258
|
end
|
222
259
|
|
223
260
|
if render_opts[:cache]
|
224
|
-
|
225
|
-
|
261
|
+
if (cache = opts[:cache]).nil?
|
262
|
+
cache = content || !opts[:template_block]
|
263
|
+
end
|
264
|
+
|
265
|
+
if cache
|
266
|
+
template_block = opts[:template_block] unless content
|
267
|
+
template_opts = opts[:template_opts]
|
226
268
|
|
227
|
-
|
228
|
-
|
269
|
+
opts[:cache_key] ||= if template_class || engine_override || template_opts || template_block
|
270
|
+
[path, template_class, engine_override, template_opts, template_block]
|
271
|
+
else
|
272
|
+
path
|
273
|
+
end
|
229
274
|
else
|
230
|
-
|
275
|
+
opts.delete(:cache_key)
|
231
276
|
end
|
232
|
-
opts[:key] = key
|
233
277
|
end
|
234
278
|
|
279
|
+
opts
|
280
|
+
end
|
281
|
+
|
282
|
+
# Merge any :locals specified in the render_opts into the :locals option given.
|
283
|
+
def merge_render_locals(opts)
|
235
284
|
if !opts[:_is_layout] && (r_locals = render_opts[:locals])
|
236
285
|
opts[:locals] = if locals = opts[:locals]
|
237
|
-
r_locals.merge(locals)
|
286
|
+
Hash[r_locals].merge!(locals)
|
238
287
|
else
|
239
288
|
r_locals
|
240
289
|
end
|
241
290
|
end
|
242
|
-
|
243
|
-
opts
|
244
291
|
end
|
245
292
|
|
246
293
|
# Return a single hash combining the template and opts arguments.
|
247
294
|
def parse_template_opts(template, opts)
|
248
|
-
|
249
|
-
|
295
|
+
opts = Hash[opts]
|
296
|
+
if template.is_a?(Hash)
|
297
|
+
opts.merge!(template)
|
298
|
+
else
|
299
|
+
opts[:template] = template
|
300
|
+
opts
|
301
|
+
end
|
250
302
|
end
|
251
303
|
|
252
304
|
# The default render options to use. These set defaults that can be overridden by
|
253
305
|
# providing a :layout_opts option to the view/render method.
|
254
306
|
def render_layout_opts
|
255
|
-
render_opts[:layout_opts]
|
307
|
+
Hash[render_opts[:layout_opts]]
|
308
|
+
end
|
309
|
+
|
310
|
+
# Retrieve the Tilt::Template object for the given template and opts.
|
311
|
+
def retrieve_template(opts)
|
312
|
+
unless opts[:cache_key] && opts[:cache] != false
|
313
|
+
found_template_opts = opts = find_template(opts)
|
314
|
+
end
|
315
|
+
cached_template(opts) do
|
316
|
+
opts = found_template_opts || find_template(opts)
|
317
|
+
template_opts = render_opts[:template_opts]
|
318
|
+
if engine_opts = render_opts[:engine_opts][opts[:engine]]
|
319
|
+
template_opts = Hash[template_opts].merge!(engine_opts)
|
320
|
+
end
|
321
|
+
if current_template_opts = opts[:template_opts]
|
322
|
+
template_opts = Hash[template_opts].merge!(current_template_opts)
|
323
|
+
end
|
324
|
+
opts[:template_class].new(opts[:path], 1, template_opts, &opts[:template_block])
|
325
|
+
end
|
256
326
|
end
|
257
327
|
|
258
328
|
# The name to use for the template. By default, just converts the :template option to a string.
|
@@ -262,8 +332,7 @@ class Roda
|
|
262
332
|
|
263
333
|
# The template path for the given options.
|
264
334
|
def template_path(opts)
|
265
|
-
|
266
|
-
"#{opts[:views] || render_opts[:views]}/#{template_name(opts)}.#{opts[:ext] || render_opts[:ext] || render_opts[:engine]}"
|
335
|
+
"#{opts[:views]}/#{template_name(opts)}.#{opts[:engine]}"
|
267
336
|
end
|
268
337
|
|
269
338
|
# If a layout should be used, return a hash of options for
|
@@ -274,7 +343,7 @@ class Roda
|
|
274
343
|
layout_opts = render_layout_opts
|
275
344
|
if l_opts = opts[:layout_opts]
|
276
345
|
if (l_locals = l_opts[:locals]) && (layout_locals = layout_opts[:locals])
|
277
|
-
set_locals = layout_locals.merge(l_locals)
|
346
|
+
set_locals = Hash[layout_locals].merge!(l_locals)
|
278
347
|
end
|
279
348
|
layout_opts.merge!(l_opts)
|
280
349
|
if set_locals
|
@@ -294,7 +363,6 @@ class Roda
|
|
294
363
|
layout_opts
|
295
364
|
end
|
296
365
|
end
|
297
|
-
|
298
366
|
end
|
299
367
|
end
|
300
368
|
|