roda 3.64.0 → 3.66.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: 715e94da6acab0fa1c5e8409051f3faf0800ca56874d8640fa8f00c2ac432322
4
- data.tar.gz: 79d9f75da1af54c8288918f722dff874433c7935a996180311344a24acc751ec
3
+ metadata.gz: 52a6bb08790a0a3414771a5d772a27d98089a96a306812ddb781ce31724289f0
4
+ data.tar.gz: 8679931319b2abf3b8aba64151664cf2d154517fbfd5020ded910e5ba32452a0
5
5
  SHA512:
6
- metadata.gz: 507d5395d337ae4e6c13a3ace905a605ecad5690bf8ceabd8d40bf3c9e2b162649a2a6cb79d1422c8c560396f4abdf891d8f3543b9866cd4f20e944acb4b6347
7
- data.tar.gz: 37cdd77e3b2894ca55370d638e411e3bf10005101d83939a970b242a4750aa2df929994343c20546cdd6046553897e4d7cde19788f10c2406eb5b446d31e97d5
6
+ metadata.gz: 4ace69f14606887e7243a5ec00598d8a3ded4230e8fbdf849c801579f38c9242ca87c5ab48887aeba65911cbcf49e675e768c077d20680e9693c5989180218b7
7
+ data.tar.gz: 7986400736b3631524c3427f135a8d3cb9da04da0d1413f59d764fd8c5b7c4ee6f81be9a3b92d848c5546229b7bd7f236278cfe41cb12410c34def542762d1f9
data/CHANGELOG CHANGED
@@ -1,3 +1,19 @@
1
+ = 3.66.0 (2023-03-13)
2
+
3
+ * Support overriding exception page assets via exception_page_{css,js} instance methods (jeremyevans) (#306)
4
+
5
+ * Avoid keeping reference to Roda instance that caches an inline template (jeremyevans)
6
+
7
+ * Add render_coverage plugin, using tilt 2.1 features to allow for compiled templates in Ruby <3.2 (jeremyevans)
8
+
9
+ = 3.65.0 (2023-02-13)
10
+
11
+ * Make indifferent_params plugin work with changes in rack main branch (jeremyevans)
12
+
13
+ * Add autoload_named_routes plugin for autoloading file for a named route when there is a request for that route (jeremyevans)
14
+
15
+ * Make path method in path plugin accept class name string/symbol with :class_name option to register classes without forcing autoloads (jeremyevans)
16
+
1
17
  = 3.64.0 (2023-01-12)
2
18
 
3
19
  * Automatically expand paths for autoload_hash_branches files, so that relative paths work (jeremyevans)
@@ -0,0 +1,12 @@
1
+ = New Features
2
+
3
+ * An autoload_named_routes plugin has been added for autoloading files
4
+ for a named route setup by the named_routes plugin when there is a
5
+ request for that route.
6
+
7
+ = Other Improvements
8
+
9
+ * The path method in the path plugin now supports a :class_name option.
10
+ You can set this option to true and use a class name String/Symbol
11
+ to register paths for classes without referencing the related class,
12
+ useful when autoloading the class.
@@ -0,0 +1,23 @@
1
+ = New Features
2
+
3
+ * A render_coverage plugin has been added, which will cause compiled
4
+ template code to be saved to a folder and loaded using load instead
5
+ of eval. This allows for coverage to work for the compiled template
6
+ code in Ruby versions before 3.2. It can also allow for verbose
7
+ syntax warnings in compiled template code (ignored by eval), and
8
+ can also be useful for static analysis of compiled template code.
9
+ This plugin requires tilt 2.1+.
10
+
11
+ * The exception_page plugin now supports exception_page_{css,js}
12
+ instance methods for overriding the CSS and JavaScript on the
13
+ generated exception page.
14
+
15
+ = Other Improvements
16
+
17
+ * Using inline templates (render/view :inline option) no longer keeps
18
+ a reference to the Roda instance that caches the template.
19
+
20
+ = Backwards Compatibility
21
+
22
+ * The Render::TemplateMtimeWrapper API has changed. Any external
23
+ use of this class needs to be updated.
@@ -0,0 +1,65 @@
1
+ # frozen-string-literal: true
2
+
3
+ #
4
+ class Roda
5
+ module RodaPlugins
6
+ # The autoload_named_routes plugin builds on the named_routes plugin and allows for
7
+ # delaying loading of a file containing a named route for an application until there
8
+ # is a request that uses the named route. This can be useful in development
9
+ # to improvement startup time by not loading all named routes up front. It can also be
10
+ # useful in testing subsets of an application by only loading the named routes being
11
+ # tested.
12
+ #
13
+ # You can specify a single hash branch for autoloading:
14
+ #
15
+ # plugin :autoload_named_route
16
+ # autoload_named_route(:route_name, '/absolute/path/to/file')
17
+ # autoload_named_route(:namespace, :route_name, 'relative/path/to/file')
18
+ #
19
+ # Note that unlike the +route+ method defined by the named_routes plugin, when providing
20
+ # a namespace, the namespace comes before the route name and not after.
21
+ #
22
+ # When the autoloaded file is required, it should redefine the same
23
+ # named route. If it does not, requests to the named route will be ignored (as if the
24
+ # related named route block was empty).
25
+ #
26
+ # When freezing an application, all named routes are automatically loaded, because
27
+ # autoloading named routes does not work for frozen applications.
28
+ module AutoloadNamedRoutes
29
+ def self.load_dependencies(app)
30
+ app.plugin :named_routes
31
+ end
32
+
33
+ def self.configure(app)
34
+ app.opts[:autoload_named_route_files] ||= []
35
+ end
36
+
37
+ module ClassMethods
38
+ # Autoload the given file when there is request for the named route.
39
+ # The given file should configure the named route specified.
40
+ def autoload_named_route(namespace=nil, name, file)
41
+ file = File.expand_path(file)
42
+ opts[:autoload_named_route_files] << file
43
+ routes = opts[:namespaced_routes][namespace] ||= {}
44
+ meth = routes[name] = define_roda_method(routes[name] || "named_routes_#{namespace}_#{name}", 1) do |r|
45
+ loc = method(routes[name]).source_location
46
+ require file
47
+ # Avoid infinite loop in case method is not overridden
48
+ if method(meth).source_location != loc
49
+ send(meth, r)
50
+ end
51
+ end
52
+ nil
53
+ end
54
+
55
+ # Eagerly load all autoloaded named routes when freezing the application.
56
+ def freeze
57
+ opts.delete(:autoload_named_route_files).each{|file| require file}
58
+ super
59
+ end
60
+ end
61
+ end
62
+
63
+ register_plugin(:autoload_named_routes, AutoloadNamedRoutes)
64
+ end
65
+ end
@@ -227,7 +227,7 @@ END
227
227
 
228
228
  css = case css_file
229
229
  when nil
230
- "<style type=\"text/css\">#{ExceptionPage.css}</style>"
230
+ "<style type=\"text/css\">#{exception_page_css}</style>"
231
231
  when false
232
232
  # :nothing
233
233
  else
@@ -236,7 +236,7 @@ END
236
236
 
237
237
  js = case js_file
238
238
  when nil
239
- "<script type=\"text/javascript\">\n//<!--\n#{ExceptionPage.js}\n//-->\n</script>"
239
+ "<script type=\"text/javascript\">\n//<!--\n#{exception_page_js}\n//-->\n</script>"
240
240
  when false
241
241
  # :nothing
242
242
  else
@@ -399,6 +399,16 @@ END
399
399
  end
400
400
  end
401
401
 
402
+ # The CSS to use on the exception page
403
+ def exception_page_css
404
+ ExceptionPage.css
405
+ end
406
+
407
+ # The JavaScript to use on the exception page
408
+ def exception_page_js
409
+ ExceptionPage.js
410
+ end
411
+
402
412
  private
403
413
 
404
414
  if RUBY_VERSION >= '3.2'
@@ -420,11 +430,11 @@ END
420
430
  def exception_page_assets
421
431
  get 'exception_page.css' do
422
432
  response['Content-Type'] = "text/css"
423
- ExceptionPage.css
433
+ scope.exception_page_css
424
434
  end
425
435
  get 'exception_page.js' do
426
436
  response['Content-Type'] = "application/javascript"
427
- ExceptionPage.js
437
+ scope.exception_page_js
428
438
  end
429
439
  end
430
440
  end
@@ -53,9 +53,18 @@ class Roda
53
53
 
54
54
  class Params < Rack::QueryParser::Params
55
55
  if Rack.release >= '3'
56
- def initialize
57
- @size = 0
58
- @params = Hash.new(&INDIFFERENT_PROC)
56
+ # rack main branch compatibility
57
+ # :nocov:
58
+ if Params < Hash
59
+ def initialize
60
+ super(&INDIFFERENT_PROC)
61
+ end
62
+ # :nocov:
63
+ else
64
+ def initialize
65
+ @size = 0
66
+ @params = Hash.new(&INDIFFERENT_PROC)
67
+ end
59
68
  end
60
69
  else
61
70
  def initialize(limit = Rack::Utils.key_space_limit)
@@ -26,6 +26,9 @@ class Roda
26
26
  # path Quux do |quux, path|
27
27
  # "/quux/#{quux.id}/#{path}"
28
28
  # end
29
+ # path 'FooBar', class_name: true do |foobar|
30
+ # "/foobar/#{foobar.id}"
31
+ # end
29
32
  #
30
33
  # route do |r|
31
34
  # r.post 'foo' do
@@ -65,9 +68,18 @@ class Roda
65
68
  #
66
69
  # Note that if :add_script_name, :relative, :url, or :url_only is used, the path method will also create a
67
70
  # <tt>_*_path</tt> private method.
71
+ #
72
+ # If the path class method is passed a string or symbol as the first argument, and the second argument
73
+ # is a hash with the :class_name option passed, the symbol/string is treated as a class name.
74
+ # This enables the use of class-based paths without forcing autoloads for the related
75
+ # classes. If the plugin is not registering classes by name, this will use the symbol or
76
+ # string to find the related class.
68
77
  module Path
69
78
  DEFAULT_PORTS = {'http' => 80, 'https' => 443}.freeze
70
79
 
80
+ # Regexp for valid constant names, to prevent code execution.
81
+ VALID_CONSTANT_NAME_REGEXP = /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/.freeze
82
+
71
83
  # Initialize the path classes when loading the plugin. Options:
72
84
  # :by_name :: Register classes by name, which is friendlier when reloading code (defaults to
73
85
  # true in development mode)
@@ -97,11 +109,20 @@ class Roda
97
109
 
98
110
  # Create a new instance method for the named path. See plugin module documentation for options.
99
111
  def path(name, path=nil, opts=OPTS, &block)
100
- if name.is_a?(Class)
101
- raise RodaError, "can't provide path or options when calling path with a class" unless path.nil? && opts.empty?
112
+ if name.is_a?(Class) || (path.is_a?(Hash) && (class_name = path[:class_name]))
113
+ raise RodaError, "can't provide path when calling path with a class" if path && !class_name
114
+ raise RodaError, "can't provide options when calling path with a class" unless opts.empty?
102
115
  raise RodaError, "must provide a block when calling path with a class" unless block
103
116
  if self.opts[:path_class_by_name]
104
- name = name.name
117
+ if class_name
118
+ name = name.to_s
119
+ else
120
+ name = name.name
121
+ end
122
+ elsif class_name
123
+ name = name.to_s
124
+ raise RodaError, "invalid class name passed when using class_name option" unless VALID_CONSTANT_NAME_REGEXP =~ name
125
+ name = Object.class_eval(name, __FILE__, __LINE__)
105
126
  end
106
127
  path_classes[name] = block
107
128
  self.opts[:path_class_methods][name] = define_roda_method("path_#{name}", :any, &block)
@@ -320,14 +320,16 @@ class Roda
320
320
  # template file has been modified. This is an internal class and
321
321
  # the API is subject to change at any time.
322
322
  class TemplateMtimeWrapper
323
- def initialize(template_class, path, dependencies, *template_args)
324
- @template_class = template_class
325
- @path = path
326
- @template_args = template_args
327
- @dependencies = ([path] + Array(dependencies)) if dependencies
323
+ def initialize(roda_class, opts, template_opts)
324
+ @roda_class = roda_class
325
+ @opts = opts
326
+ @template_opts = template_opts
327
+ reset_template
328
328
 
329
- @mtime = template_last_modified if File.file?(path)
330
- @template = template_class.new(path, *template_args)
329
+ @path = opts[:path]
330
+ deps = opts[:dependencies]
331
+ @dependencies = ([@path] + Array(deps)) if deps
332
+ @mtime = template_last_modified
331
333
  end
332
334
 
333
335
  # If the template file exists and the modification time has
@@ -358,7 +360,7 @@ class Roda
358
360
  else
359
361
  if mtime != @mtime
360
362
  @mtime = mtime
361
- @template = @template_class.new(@path, *@template_args)
363
+ reset_template
362
364
  return true
363
365
  end
364
366
  end
@@ -407,6 +409,14 @@ class Roda
407
409
  end
408
410
  end
409
411
  end
412
+
413
+ private
414
+
415
+ # Reset the template, done every time the template or one of its
416
+ # dependencies is modified.
417
+ def reset_template
418
+ @template = @roda_class.create_template(@opts, @template_opts)
419
+ end
410
420
  end
411
421
 
412
422
  module ClassMethods
@@ -427,6 +437,17 @@ class Roda
427
437
  end
428
438
  end
429
439
 
440
+ # Return an Tilt::Template object based on the given opts and template_opts.
441
+ def create_template(opts, template_opts)
442
+ opts[:template_class].new(opts[:path], 1, template_opts, &opts[:template_block])
443
+ end
444
+
445
+ # A proc that returns content, used for inline templates, so that the template
446
+ # doesn't hold a reference to the instance of the class
447
+ def inline_template_block(content)
448
+ Proc.new{content}
449
+ end
450
+
430
451
  # Copy the rendering options into the subclass, duping
431
452
  # them as necessary to prevent changes in the subclass
432
453
  # affecting the parent class.
@@ -656,7 +677,7 @@ class Roda
656
677
  if content = opts[:inline]
657
678
  path = opts[:path] = content
658
679
  template_class = opts[:template_class] ||= ::Tilt[engine]
659
- opts[:template_block] = Proc.new{content}
680
+ opts[:template_block] = self.class.inline_template_block(content)
660
681
  else
661
682
  opts[:views] ||= render_opts[:views]
662
683
  path = opts[:path] ||= template_path(opts)
@@ -730,14 +751,14 @@ class Roda
730
751
  !opts[:inline]
731
752
 
732
753
  if render_opts[:check_template_mtime] && !opts[:template_block] && !cache
733
- template = TemplateMtimeWrapper.new(opts[:template_class], opts[:path], opts[:dependencies], 1, template_opts)
754
+ template = TemplateMtimeWrapper.new(self.class, opts, template_opts)
734
755
 
735
756
  if define_compiled_method
736
757
  method_name = :"_roda_template_#{self.class.object_id}_#{method_cache_key}"
737
758
  method_cache[method_cache_key] = template.define_compiled_method(self.class, method_name)
738
759
  end
739
760
  else
740
- template = opts[:template_class].new(opts[:path], 1, template_opts, &opts[:template_block])
761
+ template = self.class.create_template(opts, template_opts)
741
762
 
742
763
  if define_compiled_method && cache != false
743
764
  begin
@@ -0,0 +1,86 @@
1
+ # frozen-string-literal: true
2
+
3
+ require 'tilt'
4
+ # :nocov:
5
+ raise 'Tilt version does not support coverable templates' unless Tilt::Template.method_defined?(:compiled_path=)
6
+ # :nocov:
7
+
8
+ #
9
+ class Roda
10
+ module RodaPlugins
11
+ # The render_coverage plugin builds on top of the render plugin
12
+ # and sets compiled_path on created templates. This allows
13
+ # Ruby's coverage library before Ruby 3.2 to consider code created
14
+ # by templates. You may not need this plugin on Ruby 3.2+, since
15
+ # on Ruby 3.2+, coverage can consider code loaded with +eval+.
16
+ # This plugin is only supported when using tilt 2.1+, since it
17
+ # requires the compiled_path supported added in tilt 2.1.
18
+ #
19
+ # By default, the render_coverage plugin will use +coverage/views+
20
+ # as the directory containing the compiled template files. You can
21
+ # change this by passing the :dir option when loading the plugin.
22
+ # By default, the plugin will set the compiled_path by taking the
23
+ # template file path, stripping off any of the allowed_paths used
24
+ # by the render plugin, and converting slashes to dashes. You can
25
+ # override the allowed_paths to strip by passing the :strip_paths
26
+ # option when loading the plugin. Paths outside :strip_paths (or
27
+ # the render plugin allowed_paths if :strip_paths is not set) will
28
+ # not have a compiled_path set.
29
+ #
30
+ # Due to how Ruby's coverage library works in regards to loading
31
+ # a compiled template file with identical code more than once,
32
+ # it may be beneficial to run coverage testing with the
33
+ # +RODA_RENDER_COMPILED_METHOD_SUPPORT+ environment variable set
34
+ # to +no+ if using this plugin.
35
+ module RenderCoverage
36
+ def self.load_dependencies(app, opts=OPTS)
37
+ app.plugin :render
38
+ end
39
+
40
+ # Use the :dir option to set the directory to store the compiled
41
+ # template files, and the :strip_paths directory for paths to
42
+ # strip.
43
+ def self.configure(app, opts=OPTS)
44
+ app.opts[:render_coverage_strip_paths] = opts[:strip_paths].map{|f| File.expand_path(f)} if opts.has_key?(:strip_paths)
45
+ coverage_dir = app.opts[:render_coverage_dir] = opts[:dir] || app.opts[:render_coverage_dir] || 'coverage/views'
46
+ Dir.mkdir(coverage_dir) unless File.directory?(coverage_dir)
47
+ end
48
+
49
+ module ClassMethods
50
+ # Set a compiled path on the created template, if the path for
51
+ # the template is in one of the allowed_views.
52
+ def create_template(opts, template_opts)
53
+ template = super
54
+ return template if opts[:template_block]
55
+
56
+ path = File.expand_path(opts[:path])
57
+ (self.opts[:render_coverage_strip_paths] || render_opts[:allowed_paths]).each do |dir|
58
+ if path.start_with?(dir + '/')
59
+ template.compiled_path = File.join(self.opts[:render_coverage_dir], path[dir.length+1, 10000000].gsub('/', '-'))
60
+ break
61
+ end
62
+ end
63
+
64
+ template
65
+ end
66
+ end
67
+
68
+ module InstanceMethods
69
+ private
70
+
71
+ # Convert template paths to real paths to try to ensure the same template is cached.
72
+ def template_path(opts)
73
+ path = super
74
+
75
+ if File.file?(path)
76
+ File.realpath(path)
77
+ else
78
+ path
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ register_plugin(:render_coverage, RenderCoverage)
85
+ end
86
+ end
@@ -90,7 +90,7 @@ class Roda
90
90
  # action attribute, and returns a path you can pass to csrf_token
91
91
  # that should be valid for the form submission. The argument should
92
92
  # either be nil or a string representing a relative path, absolute
93
- # path, or full URL.
93
+ # path, or full URL (using appropriate URL encoding).
94
94
  # csrf_tag(path=nil, method='POST') :: An HTML hidden input tag string containing the CSRF token, suitable
95
95
  # for placing in an HTML form. Takes the same arguments as csrf_token.
96
96
  # csrf_token(path=nil, method='POST') :: The value of the csrf token, in case it needs to be accessed
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 = 64
7
+ RodaMinorVersion = 66
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roda
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.64.0
4
+ version: 3.66.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-12 00:00:00.000000000 Z
11
+ date: 2023-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -237,6 +237,8 @@ extra_rdoc_files:
237
237
  - doc/release_notes/3.62.0.txt
238
238
  - doc/release_notes/3.63.0.txt
239
239
  - doc/release_notes/3.64.0.txt
240
+ - doc/release_notes/3.65.0.txt
241
+ - doc/release_notes/3.66.0.txt
240
242
  - doc/release_notes/3.7.0.txt
241
243
  - doc/release_notes/3.8.0.txt
242
244
  - doc/release_notes/3.9.0.txt
@@ -308,6 +310,8 @@ files:
308
310
  - doc/release_notes/3.62.0.txt
309
311
  - doc/release_notes/3.63.0.txt
310
312
  - doc/release_notes/3.64.0.txt
313
+ - doc/release_notes/3.65.0.txt
314
+ - doc/release_notes/3.66.0.txt
311
315
  - doc/release_notes/3.7.0.txt
312
316
  - doc/release_notes/3.8.0.txt
313
317
  - doc/release_notes/3.9.0.txt
@@ -325,6 +329,7 @@ files:
325
329
  - lib/roda/plugins/assets.rb
326
330
  - lib/roda/plugins/assets_preloading.rb
327
331
  - lib/roda/plugins/autoload_hash_branches.rb
332
+ - lib/roda/plugins/autoload_named_routes.rb
328
333
  - lib/roda/plugins/backtracking_array.rb
329
334
  - lib/roda/plugins/branch_locals.rb
330
335
  - lib/roda/plugins/caching.rb
@@ -406,6 +411,7 @@ files:
406
411
  - lib/roda/plugins/recheck_precompiled_assets.rb
407
412
  - lib/roda/plugins/relative_path.rb
408
413
  - lib/roda/plugins/render.rb
414
+ - lib/roda/plugins/render_coverage.rb
409
415
  - lib/roda/plugins/render_each.rb
410
416
  - lib/roda/plugins/render_locals.rb
411
417
  - lib/roda/plugins/request_aref.rb
@@ -463,7 +469,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
463
469
  - !ruby/object:Gem::Version
464
470
  version: '0'
465
471
  requirements: []
466
- rubygems_version: 3.4.1
472
+ rubygems_version: 3.4.6
467
473
  signing_key:
468
474
  specification_version: 4
469
475
  summary: Routing tree web toolkit