require 'glue/aspects' require 'nitro/render' require 'nitro/scaffold' require 'nitro/caching' module Nitro # Encapsulates metadata that describe an action # parameter. # # [+default+] # The default value. # # [+format+] # The expected format. # # [+required+] # Is this parameter required? ActionParam = Struct.new(:default, :format, :required) # Encapsulates metadata that describe an action. class ActionMeta < Hash # The arguments of the given method. attr_accessor :params # Initialize the metadata. def initialize(options) @params = {} update(options) end # Update the metadata. # # [+options+] # A hash containing the metadata. Options with Symbol # keys are considered metadata, options with # String keys are the named parameters for the action. def update(options) options.each do |k, v| case k when String # A key of type String denotes a parameter. case v when Regexp @params[k] = ActionParam.new(nil, v, nil) when ActionParam @params[k] = v else if v == :required @params[k] = ActionParam.new(nil, nil, true) else @params[k] = ActionParam.new(v, nil, nil) end end when Symbol self[k] = v else raise TypeError.new('The keys must be either Symbols or Strings.') end end end end # The Controller part in the MVC paradigm. # A Controller encpsulates a set of actions. class Controller include Render include Glue::Aspects include Scaffolding include Caching # A hash containing metadata for the action # methods. cattr_accessor :action_metadata, {} def initialize(context, base = nil) super self.class.template_root ||= "#{@context.dispatcher.template_root}#{base}" end # Use the method_missing hook to compile the actions # for this controller. def method_missing(action, *args) if Rendering.compile_action(self.class, action, self.class.template_root) send(action, *args) else super end end class << self alias __old_inherited inherited #-- # gmosx, FIXME: the template_root hack is temporary. # this should be moved to the render. #++ def inherited(child) child.class_eval %{ if caller[2].to_s.split(':').last =~ /[0-9]+/ DEF_FILE = caller[2].to_s.strip.gsub( /:[0-9]+$/ , '') else DEF_FILE = caller[3].to_s.strip.gsub( /:[0-9]+$/ , '') end @template_root = 'public' def self.template_root @template_root end def self.template_root=(root) @template_root = root end } __old_inherited(child) end # Define metadata for an action. This is a helper # macro. def action(name, options) if meta = @@action_metadata[name] meta.update(options) else @@action_metadata[name] = ActionMeta.new(options) end end # Return the 'action' methods for this Controller. # Some dangerous methods from ancestors are removed. def action_methods classes = self.ancestors.reject do |a| [Object, Kernel, Render, Controller].include?(a) end classes.delete(PP::ObjectMixin) if defined?(PP::ObjectMixin) methods = classes.inject([]) do |action_methods, klass| action_methods + klass.public_instance_methods(false) end # methods.delete('method_missing') return methods end end end end