lib/roda/plugins/path.rb in roda-3.64.0 vs lib/roda/plugins/path.rb in roda-3.65.0
- old
+ new
@@ -24,10 +24,13 @@
# "/baz/#{baz.id}/#{paths.join('/')}"
# end
# path Quux do |quux, path|
# "/quux/#{quux.id}/#{path}"
# end
+ # path 'FooBar', class_name: true do |foobar|
+ # "/foobar/#{foobar.id}"
+ # end
#
# route do |r|
# r.post 'foo' do
# r.redirect foo_path # /foo
# end
@@ -63,13 +66,22 @@
# method. If a Symbol or String, uses the value as the url method name.
# :url_only :: Do not create a path method, just a url method.
#
# Note that if :add_script_name, :relative, :url, or :url_only is used, the path method will also create a
# <tt>_*_path</tt> private method.
+ #
+ # If the path class method is passed a string or symbol as the first argument, and the second argument
+ # is a hash with the :class_name option passed, the symbol/string is treated as a class name.
+ # This enables the use of class-based paths without forcing autoloads for the related
+ # classes. If the plugin is not registering classes by name, this will use the symbol or
+ # string to find the related class.
module Path
DEFAULT_PORTS = {'http' => 80, 'https' => 443}.freeze
+ # Regexp for valid constant names, to prevent code execution.
+ VALID_CONSTANT_NAME_REGEXP = /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/.freeze
+
# Initialize the path classes when loading the plugin. Options:
# :by_name :: Register classes by name, which is friendlier when reloading code (defaults to
# true in development mode)
def self.configure(app, opts=OPTS)
app.instance_eval do
@@ -95,14 +107,23 @@
super
end
# Create a new instance method for the named path. See plugin module documentation for options.
def path(name, path=nil, opts=OPTS, &block)
- if name.is_a?(Class)
- raise RodaError, "can't provide path or options when calling path with a class" unless path.nil? && opts.empty?
+ if name.is_a?(Class) || (path.is_a?(Hash) && (class_name = path[:class_name]))
+ raise RodaError, "can't provide path when calling path with a class" if path && !class_name
+ raise RodaError, "can't provide options when calling path with a class" unless opts.empty?
raise RodaError, "must provide a block when calling path with a class" unless block
if self.opts[:path_class_by_name]
- name = name.name
+ if class_name
+ name = name.to_s
+ else
+ name = name.name
+ end
+ elsif class_name
+ name = name.to_s
+ raise RodaError, "invalid class name passed when using class_name option" unless VALID_CONSTANT_NAME_REGEXP =~ name
+ name = Object.class_eval(name, __FILE__, __LINE__)
end
path_classes[name] = block
self.opts[:path_class_methods][name] = define_roda_method("path_#{name}", :any, &block)
return
end