require 'glue/aspects' require 'glue/helper' require 'nitro' require 'nitro/render' require 'nitro/scaffold' require 'nitro/caching' require 'nitro/flash' require 'nitro/mixin/markup' module Nitro # Encapsulates metadata that describe an action # parameter. # # [+default+] # The default value. # # [+format+] # The expected format. # # [+required+] # Is this parameter required? unless const_defined? :ActionParam ActionParam = Struct.new(:default, :format, :required) end # 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 # Include this Mixin to a class to make objects of this class # publishable, ie accessible through a standard web (REST) # interface. module Publishable def self.append_features(base) base.module_eval do include Render include Glue::Aspects include Flashing include Glue::Helpers class << self attr_accessor :template_root end end # Define metadata for an action. This is a helper # macro. base.module_eval do def self.action(name, options) if meta = action_metadata[name] meta.update(options) else action_metadata[name] = ActionMeta.new(options) end end end # Aliases an action #-- # gmosx, FIXME: better implementation needed. #++ base.module_eval do def self.alias_action(new, old) alias_method new, old md = action_metadata[old] || ActionMeta.new md[:view] = old action_metadata[new] = md end end # Return the 'action' methods for this Controller. # Some dangerous methods from ancestors are removed. # All private methods are ignored. base.module_eval do def self.action_methods classes = self.ancestors.reject do |a| [Object, Kernel, Render, Controller, Caching].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 # gmosx: add the default action (leave this?) # methods << 'index' return methods end end # A hash containing metadata for the action # methods. base.module_eval do def self.action_metadata # FIXME: improve this. @action_metadata ||= {} @action_metadata end end # Use the method_missing hook to compile the actions # for this controller. base.module_eval do def method_missing(action, *args) if Compiler.new.compile(self.class, action) send(action, *args) else super end end end # Cookie helpers. base.module_eval do private def cookies @context.cookies end def send_cookie(name, value = nil) @context.add_cookie(name, value) end end end end # The Controller part in the MVC paradigm. The controller's # published methods are called actrions. The controller class # contains the Publishable mixin and additional helper mixins. class Controller include Publishable include Scaffolding include Caching include Markup # The default action. =begin def index print %{ This is the placeholder action is provided as a default for #{self.class.name}.
You probably want to implement your custom action here. } end =end end # A simple controller, only handles templates. # Useful to implement php/asp/jsp style applications. # The Dispatcher uses this as the default Controller. #-- # gmosx: At the moment used to have a separate # template_root. #++ class SimpleController < Controller end end