roda 3.88.0 → 3.90.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bfe23e68b3616d0513c55ca0d6006639eaa5595c36e48445e3a4b961f17dc483
4
- data.tar.gz: 110d2f273b1b2eee5d0717d7238c3c131d7dd9fd2f7eb5a7ff0b575ce0256af0
3
+ metadata.gz: c42c5f4d45d0f7fe64602e9d2a32bec3c690606c98709f7bd0bde537dd7a6463
4
+ data.tar.gz: c5f4edfdf88244770bd0184220df443ea5183c017f11c7342b4192db158d8d6c
5
5
  SHA512:
6
- metadata.gz: 2c60bdca82724f3f1f468ca408e6d58a7fb61bb49e5819835afdb59743b82a8f1ad77e98775f8ce720bcc4aff4d72ae2e3526419cba509d60588eefd585aedba
7
- data.tar.gz: 223ed6296acb9829902050161f88356e4bd6d2e5da5dc06e43175b4a59cd38c312a63c79cd93b5f2c648d7bf25e84f66a1ab02c72b603b42866ad434851350d1
6
+ metadata.gz: 19ccf7e44d75f36bc372f2dcad01bb9bf23831c12397d142ea94d1e5f10d0cfe1be3f62f8f45e584ab9e4d0510c8d6acba7dd13e5d9df0680dece3838a43f5a6
7
+ data.tar.gz: 48e7c8fc21d75df1adfb616fa5fa88c2b410b290168b2fa1b14ab2c53f8f33167e039f310007dc19831e0571c4ce2b2d34c023b8b88c07d4ff666afe4b426ce9
@@ -56,14 +56,15 @@ class Roda
56
56
 
57
57
  env = @_request.env
58
58
 
59
- line = "#{env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-"} - #{env["REMOTE_USER"] || "-"} [#{Time.now.strftime("%d/%b/%Y:%H:%M:%S %z")}] \"#{env["REQUEST_METHOD"]} #{env["SCRIPT_NAME"]}#{env["PATH_INFO"]}#{"?#{env["QUERY_STRING"]}" if ((qs = env["QUERY_STRING"]) && !qs.empty?)} #{@_request.http_version}\" #{status} #{((length = headers[RodaResponseHeaders::CONTENT_LENGTH]) && (length unless length == '0')) || '-'} #{elapsed_time}\n"
59
+ line = "#{env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-"} - #{env["REMOTE_USER"] || "-"} [#{Time.now.strftime("%d/%b/%Y:%H:%M:%S %z")}] \"#{env["REQUEST_METHOD"]} #{env["SCRIPT_NAME"]}#{env["PATH_INFO"]}#{"?#{env["QUERY_STRING"]}" if ((qs = env["QUERY_STRING"]) && !qs.empty?)} #{@_request.http_version}\" #{status} #{((length = headers[RodaResponseHeaders::CONTENT_LENGTH]) && (length unless length == '0')) || '-'} #{elapsed_time} "
60
60
  if MUTATE_LINE
61
- line.gsub!(/[^[:print:]\n]/){|c| sprintf("\\x%x", c.ord)}
61
+ line.gsub!(/[^[:print:]]/){|c| sprintf("\\x%x", c.ord)}
62
62
  # :nocov:
63
63
  else
64
- line = line.gsub(/[^[:print:]\n]/){|c| sprintf("\\x%x", c.ord)}
64
+ line = line.gsub(/[^[:print:]]/){|c| sprintf("\\x%x", c.ord)}
65
65
  # :nocov:
66
66
  end
67
+ line[-1] = "\n"
67
68
  opts[:common_logger_meth].call(line)
68
69
  end
69
70
 
@@ -151,6 +151,9 @@ class Roda
151
151
  mail
152
152
  end
153
153
  end
154
+ # :nocov:
155
+ ruby2_keywords(:mail) if respond_to?(:ruby2_keywords, true)
156
+ # :nocov:
154
157
 
155
158
  # Calls +mail+ with given arguments and immediately sends the resulting mail.
156
159
  def sendmail(*args)
@@ -158,6 +161,9 @@ class Roda
158
161
  m.deliver
159
162
  end
160
163
  end
164
+ # :nocov:
165
+ ruby2_keywords(:sendmail) if respond_to?(:ruby2_keywords, true)
166
+ # :nocov:
161
167
  end
162
168
 
163
169
  module RequestMethods
@@ -134,9 +134,7 @@ class Roda
134
134
  # and store +app+ as the next middleware to call.
135
135
  def initialize(mid, app, *args, &block)
136
136
  @mid = Class.new(mid)
137
- # :nocov:
138
- @mid.set_temporary_name("#{mid.name}(middleware)") if mid.name && RUBY_VERSION >= "3.3"
139
- # :nocov:
137
+ RodaPlugins.set_temp_name(@mid){"#{mid}::middleware_subclass"}
140
138
  if @mid.opts[:middleware_next_if_not_found]
141
139
  @mid.plugin(:not_found, &NEXT_PROC)
142
140
  end
@@ -79,6 +79,7 @@ class Roda
79
79
  mod = instance_variable_get(iv)
80
80
  else
81
81
  mod = instance_variable_set(iv, Module.new)
82
+ RodaPlugins.set_temp_name(mod){"#{klass}::module_include"}
82
83
  klass.send(:include, mod)
83
84
  end
84
85
 
@@ -0,0 +1,76 @@
1
+ # frozen-string-literal: true
2
+
3
+ #
4
+ class Roda
5
+ module RodaPlugins
6
+ # The part plugin adds a part method, which is a
7
+ # render-like method that treats all keywords as locals.
8
+ #
9
+ # # Can replace this:
10
+ # render(:template, locals: {foo: 'bar'})
11
+ #
12
+ # # With this:
13
+ # part(:template, foo: 'bar')
14
+ #
15
+ # On Ruby 2.7+, the part method takes a keyword splat, so you
16
+ # must pass keywords and not a positional hash for the locals.
17
+ #
18
+ # If you are using the :assume_fixed_locals render plugin option,
19
+ # template caching is enabled, you are using Ruby 3+, and you
20
+ # are freezing your Roda application, in addition to providing a
21
+ # simpler API, this also provides a significant performance
22
+ # improvement (more significant on Ruby 3.4+).
23
+ module Part
24
+ def self.load_dependencies(app)
25
+ app.plugin :render
26
+ end
27
+
28
+ module ClassMethods
29
+ # When freezing, optimize the part method if assuming fixed locals
30
+ # and caching templates.
31
+ def freeze
32
+ if render_opts[:assume_fixed_locals] && !render_opts[:check_template_mtime]
33
+ include AssumeFixedLocalsInstanceMethods
34
+ end
35
+
36
+ super
37
+ end
38
+ end
39
+
40
+ module InstanceMethods
41
+ if RUBY_VERSION >= '2.7'
42
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
43
+ def part(template, **locals, &block)
44
+ render(template, :locals=>locals, &block)
45
+ end
46
+ RUBY
47
+ # :nocov:
48
+ else
49
+ def part(template, locals=OPTS, &block)
50
+ render(template, :locals=>locals, &block)
51
+ end
52
+ end
53
+ # :nocov:
54
+ end
55
+
56
+ module AssumeFixedLocalsInstanceMethods
57
+ # :nocov:
58
+ if RUBY_VERSION >= '3.0'
59
+ # :nocov:
60
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
61
+ def part(template, ...)
62
+ if optimized_method = _optimized_render_method_for_locals(template, OPTS)
63
+ send(optimized_method[0], ...)
64
+ else
65
+ super
66
+ end
67
+ end
68
+ RUBY
69
+ end
70
+ end
71
+ end
72
+
73
+ register_plugin(:part, Part)
74
+ end
75
+ end
76
+
@@ -48,6 +48,9 @@ class Roda
48
48
  #
49
49
  # :allowed_paths :: Set the template paths to allow. Attempts to render paths outside
50
50
  # of these paths will raise an error. Defaults to the +:views+ directory.
51
+ # :assume_fixed_locals :: Set if you are sure all templates in your application use fixed locals
52
+ # to allow for additional optimization. This is ignored unless both
53
+ # compiled methods and fixed locals are not supported.
51
54
  # :cache :: nil/false to explicitly disable permanent template caching. By default, permanent
52
55
  # template caching is disabled by default if RACK_ENV is development. When permanent
53
56
  # template caching is disabled, for templates with paths in the file system, the
@@ -155,6 +158,11 @@ class Roda
155
158
  # were passed, instead of showing that the takes no arguments (if you use <tt>'()'</tt>),
156
159
  # or that no keywords are accepted (if you pass <tt>(**nil)</tt>).
157
160
  #
161
+ # If you are sure your application works with all templates using fixed locals,
162
+ # set the :assume_fixed_locals render plugin option, which will allow the plugin
163
+ # to optimize cache lookup for renders with locals, and avoid duplicate compiled
164
+ # methods for templates rendered both with and without locals.
165
+ #
158
166
  # See Tilt's documentation for more information regarding fixed locals.
159
167
  #
160
168
  # = Speeding Up Template Rendering
@@ -175,7 +183,9 @@ class Roda
175
183
  # Here are the recommended values of :template_opts for new applications (a couple
176
184
  # are Erubi-specific and can be ignored if you are using other templates engines):
177
185
  #
178
- # plugin :render, template_opts: {
186
+ # plugin :render,
187
+ # assume_fixed_locals: true, # Optimize plugin by assuming all templates use fixed locals
188
+ # template_opts: {
179
189
  # scope_class: self, # Always uses current class as scope class for compiled templates
180
190
  # freeze: true, # Freeze string literals in templates
181
191
  # extract_fixed_locals: true, # Support fixed locals in templates
@@ -324,6 +334,7 @@ class Roda
324
334
  opts[:allowed_paths] ||= [opts[:views]].freeze
325
335
  opts[:allowed_paths] = opts[:allowed_paths].map{|f| app.expand_path(f, nil)}.uniq.freeze
326
336
  opts[:check_paths] = true unless opts.has_key?(:check_paths)
337
+ opts[:assume_fixed_locals] &&= FIXED_LOCALS_COMPILED_METHOD_SUPPORT
327
338
 
328
339
  unless opts.has_key?(:check_template_mtime)
329
340
  opts[:check_template_mtime] = if opts[:cache] == false || opts[:explicit_cache]
@@ -531,6 +542,12 @@ class Roda
531
542
  nil
532
543
  end
533
544
 
545
+ # Optimize _call_optimized_template_method if you know all templates
546
+ # are going to be using fixed locals.
547
+ if render_opts[:assume_fixed_locals] && !render_opts[:check_template_mtime]
548
+ include AssumeFixedLocalsInstanceMethods
549
+ end
550
+
534
551
  super
535
552
  end
536
553
  end
@@ -673,21 +690,29 @@ class Roda
673
690
  # of the template render if the optimized path is used, or nil if the optimized
674
691
  # path is not used and the long method needs to be used.
675
692
  def _optimized_render_method_for_locals(template, locals)
693
+ render_opts = self.render_opts
676
694
  return unless method_cache = render_opts[:template_method_cache]
677
695
 
678
696
  case template
679
697
  when String, Symbol
680
- key = [:_render_locals, template]
681
- if optimized_template = _cached_template_method_lookup(method_cache, key)
682
- # Fixed locals case
683
- return optimized_template
684
- end
698
+ if fixed_locals = render_opts[:assume_fixed_locals]
699
+ key = template
700
+ if optimized_template = _cached_template_method_lookup(method_cache, key)
701
+ return optimized_template
702
+ end
703
+ else
704
+ key = [:_render_locals, template]
705
+ if optimized_template = _cached_template_method_lookup(method_cache, key)
706
+ # Fixed locals case
707
+ return optimized_template
708
+ end
685
709
 
686
- locals_keys = locals.keys.sort
687
- key << locals_keys
688
- if optimized_template = _cached_template_method_lookup(method_cache, key)
689
- # Regular locals case
690
- return optimized_template
710
+ locals_keys = locals.keys.sort
711
+ key << locals_keys
712
+ if optimized_template = _cached_template_method_lookup(method_cache, key)
713
+ # Regular locals case
714
+ return optimized_template
715
+ end
691
716
  end
692
717
  else
693
718
  return
@@ -695,9 +720,10 @@ class Roda
695
720
 
696
721
  if method_cache_key = _cached_template_method_key(key)
697
722
  template_obj = retrieve_template(render_template_opts(template, NO_CACHE))
698
- key.pop if fixed_locals = Render.tilt_template_fixed_locals?(template_obj)
699
- key.freeze
700
- method_cache_key.freeze
723
+ unless fixed_locals
724
+ key.pop if fixed_locals = Render.tilt_template_fixed_locals?(template_obj)
725
+ key.freeze
726
+ end
701
727
  method_name = :"_roda_template_locals_#{self.class.object_id}_#{method_cache_key}"
702
728
 
703
729
  method_cache[method_cache_key] = case template_obj
@@ -953,6 +979,18 @@ class Roda
953
979
  end
954
980
  end
955
981
  end
982
+
983
+ module AssumeFixedLocalsInstanceMethods
984
+ # :nocov:
985
+ if RUBY_VERSION >= '3.0'
986
+ # :nocov:
987
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
988
+ def _call_optimized_template_method((meth,_), locals, &block)
989
+ send(meth, **locals, &block)
990
+ end
991
+ RUBY
992
+ end
993
+ end
956
994
  end
957
995
 
958
996
  register_plugin(:render, Render)
@@ -354,9 +354,7 @@ class Roda
354
354
  res.status = opts[:status] || s
355
355
  headers.delete(RodaResponseHeaders::CONTENT_LENGTH)
356
356
  headers.replace(h.merge!(headers))
357
- res.body = b
358
-
359
- halt
357
+ halt res.finish_with_body(b)
360
358
  rescue Errno::ENOENT
361
359
  not_found
362
360
  end
@@ -23,8 +23,11 @@ class Roda
23
23
  # :foo
24
24
  # end
25
25
  module SymbolViews
26
- def self.configure(app)
26
+ def self.load_dependencies(app)
27
27
  app.plugin :custom_block_results
28
+ end
29
+
30
+ def self.configure(app)
28
31
  app.opts[:custom_block_results][Symbol] = :view
29
32
  end
30
33
  end
@@ -133,7 +133,7 @@ class Roda
133
133
  return if @_view_options || @_layout_options
134
134
 
135
135
  if subdir = @_view_subdir
136
- template = [subdir, template]
136
+ template = [subdir, template].freeze
137
137
  end
138
138
 
139
139
  super
data/lib/roda/plugins.rb CHANGED
@@ -49,5 +49,20 @@ class Roda
49
49
  end
50
50
  # :nocov:
51
51
  end
52
+
53
+ if RUBY_VERSION >= '3.3'
54
+ # Create a new module using the block, and set the temporary name
55
+ # on it using the given a containing module and name.
56
+ def self.set_temp_name(mod)
57
+ mod.set_temporary_name(yield)
58
+ mod
59
+ end
60
+ # :nocov:
61
+ else
62
+ def self.set_temp_name(mod)
63
+ mod
64
+ end
65
+ end
66
+ # :nocov:
52
67
  end
53
68
  end
@@ -159,6 +159,7 @@ class RodaSessionMiddleware
159
159
  # Setup the middleware, passing +opts+ as the Roda sessions plugin options.
160
160
  def initialize(app, opts)
161
161
  mid = Class.new(Roda)
162
+ Roda::RodaPlugins.set_temp_name(mid){"RodaSessionMiddleware::_RodaSubclass"}
162
163
  mid.plugin :sessions, opts
163
164
  @req_class = mid::RodaRequest
164
165
  @req_class.send(:include, RequestMethods)
data/lib/roda/version.rb CHANGED
@@ -4,7 +4,7 @@ class Roda
4
4
  RodaMajorVersion = 3
5
5
 
6
6
  # The minor version of Roda, updated for new feature releases of Roda.
7
- RodaMinorVersion = 88
7
+ RodaMinorVersion = 90
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roda
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.88.0
4
+ version: 3.90.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-01-14 00:00:00.000000000 Z
10
+ date: 2025-03-11 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rack
@@ -253,6 +253,7 @@ files:
253
253
  - lib/roda/plugins/padrino_render.rb
254
254
  - lib/roda/plugins/param_matchers.rb
255
255
  - lib/roda/plugins/params_capturing.rb
256
+ - lib/roda/plugins/part.rb
256
257
  - lib/roda/plugins/partials.rb
257
258
  - lib/roda/plugins/pass.rb
258
259
  - lib/roda/plugins/path.rb