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