module Roda::RodaPlugins::Base::ClassMethods

  1. lib/roda.rb

Class methods for the Roda class.

Attributes

app [R]

The rack application that this class uses.

inherit_middleware [RW]

Whether middleware from the current class should be inherited by subclasses. True by default, should be set to false when using a design where the parent class accepts requests and uses run to dispatch the request to a subclass.

opts [R]

The settings/options hash for the current class.

route_block [R]

The route block that this class uses.

Public Instance methods

call (env)

Call the internal rack application with the given environment. This allows the class itself to be used as a rack application. However, for performance, it's better to use app to get direct access to the underlying rack app.

[show source]
    # File lib/roda.rb
138 def call(env)
139   app.call(env)
140 end
clear_middleware! ()

Clear the middleware stack

[show source]
    # File lib/roda.rb
143 def clear_middleware!
144   @middleware.clear
145   build_rack_app
146 end
define_roda_method (meth, expected_arity, &block)

Define an instance method using the block with the provided name and expected arity. If the name is given as a Symbol, it is used directly. If the name is given as a String, a unique name will be generated using that string. The expected arity should be either 0 (no arguments), 1 (single argument), or :any (any number of arguments).

If the :check_arity app option is not set to false, Roda will check that the arity of the block matches the expected arity, and compensate for cases where it does not. If it is set to :warn, Roda will warn in the cases where the arity does not match what is expected.

If the expected arity is :any, Roda must perform a dynamic arity check when the method is called, which can hurt performance even in the case where the arity matches. The :check_dynamic_arity app option can be set to false to turn off the dynamic arity checks. The :check_dynamic_arity app option can be to :warn to warn if Roda needs to adjust arity dynamically.

Roda only checks arity for regular blocks, not lambda blocks, as the fixes Roda uses for regular blocks would not work for lambda blocks.

Roda does not support blocks with required keyword arguments if the expected arity is 0 or 1.

[show source]
    # File lib/roda.rb
171 def define_roda_method(meth, expected_arity, &block)
172   if meth.is_a?(String)
173     meth = roda_method_name(meth)
174   end
175 
176   if (check_arity = opts.fetch(:check_arity, true)) && !block.lambda?
177     required_args, optional_args, rest, keyword = _define_roda_method_arg_numbers(block)
178 
179     if keyword == :required && (expected_arity == 0 || expected_arity == 1)
180       raise RodaError, "cannot use block with required keyword arguments when calling define_roda_method with expected arity #{expected_arity}"
181     end
182 
183     case expected_arity
184     when 0
185       unless required_args == 0
186         if check_arity == :warn
187           RodaPlugins.warn "Arity mismatch in block passed to define_roda_method. Expected Arity 0, but arguments required for #{block.inspect}"
188         end
189         b = block
190         block = lambda{instance_exec(&b)} # Fallback
191       end
192     when 1
193       if required_args == 0 && optional_args == 0 && !rest
194         if check_arity == :warn
195           RodaPlugins.warn "Arity mismatch in block passed to define_roda_method. Expected Arity 1, but no arguments accepted for #{block.inspect}"
196         end
197         b = block
198         block = lambda{|_| instance_exec(&b)} # Fallback
199       end
200     when :any
201       if check_dynamic_arity = opts.fetch(:check_dynamic_arity, check_arity)
202         if keyword
203           # Complexity of handling keyword arguments using define_method is too high,
204           # Fallback to instance_exec in this case.
205           b = block
206           block = lambda{|*a| instance_exec(*a, &b)} # Keyword arguments fallback
207         else
208           arity_meth = meth
209           meth = :"#{meth}_arity"
210         end
211       end
212     else
213       raise RodaError, "unexpected arity passed to define_roda_method: #{expected_arity.inspect}"
214     end
215   end
216 
217   define_method(meth, &block)
218   private meth
219 
220   if arity_meth
221     required_args, optional_args, rest, keyword = _define_roda_method_arg_numbers(instance_method(meth))
222     max_args = required_args + optional_args
223     define_method(arity_meth) do |*a|
224       arity = a.length
225       if arity > required_args
226         if arity > max_args && !rest
227           if check_dynamic_arity == :warn
228             RodaPlugins.warn "Dynamic arity mismatch in block passed to define_roda_method. At most #{max_args} arguments accepted, but #{arity} arguments given for #{block.inspect}"
229           end
230           a = a.slice(0, max_args)
231         end
232       elsif arity < required_args
233         if check_dynamic_arity == :warn
234           RodaPlugins.warn "Dynamic arity mismatch in block passed to define_roda_method. #{required_args} args required, but #{arity} arguments given for #{block.inspect}"
235         end
236         a.concat([nil] * (required_args - arity))
237       end
238 
239       send(meth, *a)
240     end
241     private arity_meth
242     arity_meth
243   else
244     meth
245   end
246 end
expand_path (path, root=opts[:root])

Expand the given path, using the root argument as the base directory.

[show source]
    # File lib/roda.rb
249 def expand_path(path, root=opts[:root])
250   ::File.expand_path(path, root)
251 end
freeze ()

Freeze the internal state of the class, to avoid thread safety issues at runtime. It's optional to call this method, as nothing should be modifying the internal state at runtime anyway, but this makes sure an exception will be raised if you try to modify the internal state after calling this.

Note that freezing the class prevents you from subclassing it, mostly because it would cause some plugins to break.

[show source]
    # File lib/roda.rb
260 def freeze
261   @opts.freeze
262   @middleware.freeze
263 
264   unless opts[:subclassed]
265     # If the _roda_run_main_route instance method has not been overridden,
266     # make it an alias to _roda_main_route for performance
267     if instance_method(:_roda_run_main_route).owner == InstanceMethods
268       class_eval("alias _roda_run_main_route _roda_main_route")
269     end
270     self::RodaResponse.class_eval do
271       if instance_method(:set_default_headers).owner == ResponseMethods &&
272          instance_method(:default_headers).owner == ResponseMethods
273 
274         def set_default_headers
275           @headers['Content-Type'] ||= 'text/html'
276         end
277       end
278     end
279   end
280 
281   super
282 end
include (*a)

Rebuild the _roda_before and _roda_after methods whenever a plugin might have added a roda_before* or roda_after* method.

[show source]
    # File lib/roda.rb
286 def include(*a)
287   res = super
288   def_roda_before
289   def_roda_after
290   res
291 end
inherited (subclass)

When inheriting Roda, copy the shared data into the subclass, and setup the request and response subclasses.

[show source]
    # File lib/roda.rb
295 def inherited(subclass)
296   raise RodaError, "Cannot subclass a frozen Roda class" if frozen?
297 
298   # Mark current class as having been subclassed, as some optimizations
299   # depend on the class not being subclassed
300   opts[:subclassed] = true
301 
302   super
303   subclass.instance_variable_set(:@inherit_middleware, @inherit_middleware)
304   subclass.instance_variable_set(:@middleware, @inherit_middleware ? @middleware.dup : [])
305   subclass.instance_variable_set(:@opts, opts.dup)
306   subclass.opts.delete(:subclassed)
307   subclass.opts.to_a.each do |k,v|
308     if (v.is_a?(Array) || v.is_a?(Hash)) && !v.frozen?
309       subclass.opts[k] = v.dup
310     end
311   end
312   if block = @raw_route_block
313     subclass.route(&block)
314   end
315   
316   request_class = Class.new(self::RodaRequest)
317   request_class.roda_class = subclass
318   request_class.match_pattern_cache = RodaCache.new
319   subclass.const_set(:RodaRequest, request_class)
320 
321   response_class = Class.new(self::RodaResponse)
322   response_class.roda_class = subclass
323   subclass.const_set(:RodaResponse, response_class)
324 end
plugin (plugin, *args, &block)

Load a new plugin into the current class. A plugin can be a module which is used directly, or a symbol represented a registered plugin which will be required and then used. Returns nil.

Roda.plugin PluginModule
Roda.plugin :csrf
[show source]
    # File lib/roda.rb
332 def plugin(plugin, *args, &block)
333   raise RodaError, "Cannot add a plugin to a frozen Roda class" if frozen?
334   plugin = RodaPlugins.load_plugin(plugin) if plugin.is_a?(Symbol)
335   plugin.load_dependencies(self, *args, &block) if plugin.respond_to?(:load_dependencies)
336   include(plugin::InstanceMethods) if defined?(plugin::InstanceMethods)
337   extend(plugin::ClassMethods) if defined?(plugin::ClassMethods)
338   self::RodaRequest.send(:include, plugin::RequestMethods) if defined?(plugin::RequestMethods)
339   self::RodaRequest.extend(plugin::RequestClassMethods) if defined?(plugin::RequestClassMethods)
340   self::RodaResponse.send(:include, plugin::ResponseMethods) if defined?(plugin::ResponseMethods)
341   self::RodaResponse.extend(plugin::ResponseClassMethods) if defined?(plugin::ResponseClassMethods)
342   plugin.configure(self, *args, &block) if plugin.respond_to?(:configure)
343   nil
344 end
route (&block)

Setup routing tree for the current Roda application, and build the underlying rack application using the stored middleware. Requires a block, which is yielded the request. By convention, the block argument should be named r. Example:

Roda.route do |r|
  r.root do
    "Root"
  end
end

This should only be called once per class, and if called multiple times will overwrite the previous routing.

[show source]
    # File lib/roda.rb
359 def route(&block)
360   unless block
361     RodaPlugins.warn "no block passed to Roda.route"
362     return
363   end
364 
365   @raw_route_block = block
366   @route_block = block = convert_route_block(block)
367   @rack_app_route_block = block = rack_app_route_block(block)
368   public define_roda_method(:_roda_main_route, 1, &block)
369   build_rack_app
370 end
set_default_headers ()
[show source]
    # File lib/roda.rb
274 def set_default_headers
275   @headers['Content-Type'] ||= 'text/html'
276 end
use (*args, &block)

Add a middleware to use for the rack application. Must be called before calling route to have an effect. Example:

Roda.use Rack::ShowExceptions
[show source]
    # File lib/roda.rb
376 def use(*args, &block)
377   @middleware << [args, block].freeze
378   build_rack_app
379 end