packages feed

transformations 0.1.1.0 → 0.2.0.0

raw patch · 127 files changed

+50526/−3676 lines, 127 filesdep +QuickCheckdep +criteriondep −rewritingdep −zipperdep ~basedep ~regulardep ~template-haskellsetup-changednew-component:exe:Benchmark

Dependencies added: QuickCheck, criterion

Dependencies removed: rewriting, zipper

Dependency ranges changed: base, regular, template-haskell

Files

− Generics/MultiRec/Any.hs
@@ -1,17 +0,0 @@-{-# LANGUAGE RankNTypes            #-}-{-# LANGUAGE GADTs                 #-}--module Generics.MultiRec.Any where--import Generics.MultiRec--data Any phi where-  Any :: phi ix -> ix -> Any phi---- | Unify an 'Any' with an @a@.-matchAny :: forall phi ix. EqS phi => phi ix -> Any phi -> Maybe ix-matchAny p (Any w x) = match' w x p where-  match' :: EqS s => s b -> b -> s a -> Maybe a-  match' w x w' = case eqS w w' of-    Nothing -> Nothing-    Just Refl -> Just x
+ Generics/MultiRec/CountIs.hs view
@@ -0,0 +1,36 @@+{-# LANGUAGE KindSignatures             #-}
+{-# LANGUAGE GADTs                      #-}
+{-# LANGUAGE RankNTypes                 #-}
+{-# LANGUAGE TypeOperators              #-}
+{-# LANGUAGE FlexibleInstances          #-}
+{-# LANGUAGE FlexibleContexts           #-}
+{-# LANGUAGE ScopedTypeVariables        #-}
+
+module Generics.MultiRec.CountIs where
+
+import Generics.MultiRec
+
+--------------------------------------------------------------------------------
+-- Count I's (for showing)
+--------------------------------------------------------------------------------
+
+class CountIs (f :: (* -> *) -> * -> *) where
+  countIs :: f r ix -> Int
+
+instance CountIs (I ix)    where countIs _ = 1
+instance CountIs (t :.: f) where countIs _ = 1
+instance CountIs (K a)     where countIs _ = 0
+instance CountIs U         where countIs _ = 0
+
+instance (CountIs f) => CountIs (C c f)    where
+  countIs (_ :: C c f r ix)      = countIs (undefined :: f r ix)
+instance (CountIs f) => CountIs (f :>: ix) where
+  countIs (_ :: (f :>: ix) r xi) = countIs (undefined :: f r ix)
+
+instance (CountIs f, CountIs g) => CountIs (f :+: g) where
+  countIs (L x) = countIs x
+  countIs (R x) = countIs x
+
+instance (CountIs f, CountIs g) => CountIs (f :*: g) where
+  countIs (_ :: (f :*: g) r ix) = countIs (undefined :: f r ix)
+                                + countIs (undefined :: g r ix)
− Generics/MultiRec/HZip.hs
@@ -1,76 +0,0 @@-{-# LANGUAGE FlexibleContexts      #-}-{-# LANGUAGE FlexibleInstances     #-}-{-# LANGUAGE TypeOperators         #-}-{-# LANGUAGE ScopedTypeVariables   #-}-{-# LANGUAGE RankNTypes            #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE GADTs                 #-}--module Generics.MultiRec.HZip where--import Generics.MultiRec-import Control.Monad (liftM, liftM2, zipWithM)--class HZip phi f where-  hzipM :: Monad m =>-           (forall ix. El phi ix => phi ix -> r ix -> r' ix -> m (r'' ix)) ->-           f r ix -> f r' ix -> m (f r'' ix)--instance El phi xi => HZip phi (I xi) where-  hzipM f (I x) (I y) = liftM I (f proof x y)--instance Eq a => HZip phi (K a) where-  hzipM f (K x) (K y) | x == y    = return (K x)-                      | otherwise = fail "zip failed in K"--instance HZip phi U where-  hzipM f U U = return U--instance (HZip phi a, HZip phi b) => HZip phi (a :+: b) where-  hzipM f (L x) (L y) = liftM L (hzipM f x y)-  hzipM f (R x) (R y) = liftM R (hzipM f x y)-  hzipM f _     _     = fail "zip failed"--instance (HZip phi a, HZip phi b) => HZip phi (a :*: b) where-  hzipM f (x1 :*: y1) (x2 :*: y2) = liftM2 (:*:) (hzipM f x1 x2) (hzipM f y1 y2)--instance HZip phi f => HZip phi (f :>: xi) where-  hzipM f (Tag x) (Tag y) = liftM Tag (hzipM f x y)--instance HZip phi f => HZip phi (C c f) where-  hzipM f (C x) (C y) = liftM C (hzipM f x y)--instance HZip phi f => HZip phi ([] :.: f) where-  hzipM f (D x) (D y) = liftM D (zipWithM (hzipM f) x y)---- | Monadic zip but argument is not monadic-hzip :: (HZip phi f, Monad m) =>-        (forall ix. El phi ix => phi ix -> r ix -> s ix -> t ix) ->-        phi ix -> f r ix -> f s ix -> m (f t ix)-hzip f p = hzipM (\w x y -> return (f w x y))---- | Unsafe zip-hzip' :: (HZip phi f) =>-         (forall ix. El phi ix => phi ix -> r ix -> s ix -> t ix) ->-         phi ix -> f r ix -> f s ix -> f t ix-hzip' f p a b = case hzip (\p x y -> f p x y) p a b of-  Nothing  -> error "generic zip failed"-  Just res -> res---- | Combine two structures monadically only-combine :: forall phi f r r' m ix. (Monad m, HZip phi f) =>-           (forall ix. El phi ix => phi ix -> r ix -> r' ix -> m ()) ->-           phi ix -> f r ix -> f r' ix -> m ()-combine f l x y = hzipM wrapf x y >> return ()-  where-    wrapf :: forall ix' b. El phi ix' => phi ix' -> r ix' -> r' ix' -> m (K0 () b)-    wrapf ix x y = f ix x y >> return (K0 ())---- | Generic equality-geq :: (Fam phi, HZip phi (PF phi)) => phi ix -> ix -> ix -> Bool-geq ix x y = maybe False (const True) (geq' ix (I0 x) (I0 y))---- | Monadic generic equality (just for the sake of the monad!)-geq' :: (Monad m, Fam phi, HZip phi (PF phi))-        => phi ix -> I0 ix -> I0 ix -> m ()-geq' p (I0 x) (I0 y) = combine geq' p (from p x) (from p y)
− Generics/MultiRec/LR.hs
@@ -1,94 +0,0 @@-{-# LANGUAGE FlexibleContexts      #-}-{-# LANGUAGE FlexibleInstances     #-}-{-# LANGUAGE TypeFamilies          #-}-{-# LANGUAGE TypeOperators         #-}-{-# LANGUAGE ScopedTypeVariables   #-}-{-# LANGUAGE RankNTypes            #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE GADTs                 #-}--module Generics.MultiRec.LR where--import Generics.MultiRec---------------------------------------------------------------------------------- Functions for generating values that are different on top-level.---------------------------------------------------------------------------------- | The @LRBase@ class defines two functions, @leftb@ and @rightb@, which --- should produce different values.-class LRBase a where-  leftb  :: a-  rightb :: a--instance LRBase Int where-  leftb  = 0-  rightb = 1--instance LRBase Integer where-  leftb  = 0-  rightb = 1--instance LRBase Char where-  leftb  = 'L'-  rightb = 'R'- -instance LRBase Bool where-  leftb  = True-  rightb = False--instance LRBase a => LRBase [a] where-  leftb  = []-  rightb = [rightb]---- | The @LR@ class defines two functions, @leftf@ and @rightf@, which should --- produce different functorial values.-class LR phi (f :: (* -> *) -> * -> *) where---    leftf  :: s ix -> (forall ix . Ix s ix => s ix -> r ix) -> [f s r ix]-  leftf  :: phi ix -> (forall ix'. El phi ix' => phi ix' -> r ix') -> [f r ix]-  rightf :: phi ix -> (forall ix'. El phi ix' => phi ix' -> r ix') -> [f r ix]- -instance El phi xi => LR phi (I xi) where-  leftf  _ f = [I (f proof)]-  rightf _ f = [I (f proof)]--instance LRBase a => LR phi (K a) where-  leftf  _ _ = [K leftb]-  rightf _ _ = [K rightb]--instance LR phi U where-  leftf  _ _ = [U]-  rightf _ _ = [U]--instance (LR phi f, LR phi g) => LR phi (f :+: g) where-  leftf  p f = map L (leftf  p f) ++ map R (leftf  p f)-  rightf p f = map R (rightf p f) ++ map L (rightf p f)--instance (LR phi f, LR phi g) => LR phi (f :*: g) where-  leftf  p f = zipWith (:*:) (leftf  p f) (leftf  p f)-  rightf p f = zipWith (:*:) (rightf p f) (rightf p f)--instance LR phi f => LR phi (C c f) where-  leftf  p f = map C (leftf  p f)-  rightf p f = map C (rightf p f)--instance (El phi ix, LR phi f, EqS phi) => LR phi (f :>: ix) where-  leftf  p f = case eqS (proof :: phi ix) p of-    Just Refl -> map Tag (leftf  p f)-    Nothing   -> []-  rightf p f = case eqS (proof :: phi ix) p of-    Just Refl -> map Tag (rightf  p f)-    Nothing   -> []--instance LR phi f => LR phi ([] :.: f) where-  leftf  p f = [D []]-  rightf p f = map (\v -> D [v]) $ rightf p f--left :: (Fam phi, LR phi (PF phi)) => phi ix -> ix-left p = to p $ safeHead $ leftf p (I0 . left)--right :: (Fam phi, LR phi (PF phi)) => phi ix -> ix-right p = to p $ safeHead $ rightf p (I0 . right)--safeHead [] = error "Internal error, left or right returned []"-safeHead (x:xs) = x
− Generics/MultiRec/Ord.hs
@@ -1,57 +0,0 @@-{-# LANGUAGE RankNTypes            #-}-{-# LANGUAGE GADTs                 #-}-{-# LANGUAGE TypeOperators         #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FlexibleInstances     #-}-{-# LANGUAGE FlexibleContexts      #-}--module Generics.MultiRec.Ord where--import Generics.MultiRec-import Data.Monoid (mappend)------------------------------------------------------------------------------------- Generic Ord----------------------------------------------------------------------------------class HOrd phi f where-  hcompare :: (forall ix. phi ix -> r ix -> r ix -> Ordering) -              -> phi ix -> f r ix -> f r ix -> Ordering--instance El phi xi => HOrd phi (I xi) where-  hcompare f _ (I x) (I y) = f proof x y--instance Ord a => HOrd phi (K a) where-  hcompare _ _ (K x) (K y) = compare x y--instance HOrd phi U where-  hcompare _ _ U U = EQ--instance (HOrd phi f, HOrd phi g) => HOrd phi (f :+: g) where-  hcompare f p (L _) (R _) = LT-  hcompare f p (R _) (L _) = GT-  hcompare f p (L x) (L y) = hcompare f p x y-  hcompare f p (R x) (R y) = hcompare f p x y--instance (HOrd phi f, HOrd phi g) => HOrd phi (f :*: g) where-  hcompare f p (v :*: x) (w :*: y) = hcompare f p v w `mappend` hcompare f p x y--instance HOrd phi f => HOrd phi (C c f) where-  hcompare f p (C x) (C y) = hcompare f p x y--instance HOrd phi f => HOrd phi (f :>: ix) where-  hcompare f p (Tag x) (Tag y) = hcompare f p x y--instance (Ord1 f, HOrd phi g) => HOrd phi (f :.: g) where-  hcompare f p (D x) (D y) = compare1 (hcompare f p) x y--class Ord1 f where-  compare1 :: (a -> a -> Ordering) -> f a -> f a -> Ordering--instance Ord1 [] where-  compare1 f [] [] = EQ-  compare1 f [] _  = LT-  compare1 f _  [] = GT-  compare1 f (x:xs) (y:ys) = f x y `mappend` compare1 f xs ys--gcompare :: (Fam phi, HOrd phi (PF phi)) => phi ix -> ix -> ix -> Ordering-gcompare p x1 x2 = hcompare (\ p (I0 x1) (I0 x2) -> gcompare p x1 x2) p (from p x1) (from p x2)
− Generics/MultiRec/Rewriting.hs
@@ -1,7 +0,0 @@-module Generics.MultiRec.Rewriting (-  module Generics.MultiRec.Rewriting.Machinery,-  module Generics.MultiRec.Rewriting.Rules,-) where--import Generics.MultiRec.Rewriting.Machinery-import Generics.MultiRec.Rewriting.Rules
− Generics/MultiRec/Rewriting/Machinery.hs
@@ -1,66 +0,0 @@-{-# LANGUAGE FlexibleContexts      #-}-{-# LANGUAGE FlexibleInstances     #-}-{-# LANGUAGE TypeFamilies          #-}-{-# LANGUAGE TypeOperators         #-}-{-# LANGUAGE UndecidableInstances  #-}-{-# LANGUAGE ScopedTypeVariables   #-}-{-# LANGUAGE RankNTypes            #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE GADTs                 #-}--module Generics.MultiRec.Rewriting.Machinery where--import Generics.MultiRec-import Generics.MultiRec.HZip-import Generics.MultiRec.Rewriting.Rules-import Generics.MultiRec.Any--import qualified Data.Map as M-import Control.Monad.State---------------------------------------------------------------------------------- Class synonym for shorter names-------------------------------------------------------------------------------class (Fam phi, EqS phi, HZip phi (PF phi), HFunctor phi (PF phi))-      => Rewrite phi---------------------------------------------------------------------------------- Actual rewriting-------------------------------------------------------------------------------rewriteM :: Rewrite phi => Rule phi a -> a -> Maybe a-rewriteM (Rule p (lhs :~> rhs)) term = -  match p lhs term >>= return . (\s -> inst s p rhs)--match :: (Monad m, Rewrite phi) => -         phi ix -> Scheme phi ix -> ix -> m (Subst phi)-match p pat term = execStateT (matchM p pat (I0 term)) M.empty--matchM :: (Monad m, Rewrite phi) -          => phi ix -> Scheme phi ix -> I0 ix -> StateT (Subst phi) m ()-matchM p scheme (I0 e) = case scheme of-  HIn (L (K var)) -> do -    subst <- get-    case M.lookup var subst of-      Nothing     -> put (M.insert var (Any p e) subst)-      Just exTerm -> checkEqual p e exTerm-  HIn (R r) -> combine matchM p r (from p e)--checkEqual :: (Monad m, Rewrite phi)-           => phi ix -> ix -> Any phi -> m ()-checkEqual p e (Any p' e') = case eqS p p' of-  Nothing   -> fail "checkEqual"-  Just Refl -> geq' p (I0 e) (I0 e')--inst :: Rewrite phi =>-        Subst phi -> phi ix -> Scheme phi ix -> ix-inst s ix p-  = case p of-     HIn (L (K x)) ->-        case M.lookup x s of-          Just (Any ix' e)-            -> case eqS ix ix' of-                 Just Refl -> e-                 Nothing -> error "Coerce error in inst"-     HIn (R r) -> to ix $ hmap (\ix' -> I0 . inst s ix') ix r--type Subst phi = M.Map Metavar (Any phi)
− Generics/MultiRec/Rewriting/Rules.hs
@@ -1,100 +0,0 @@-{-# LANGUAGE FlexibleContexts      #-}-{-# LANGUAGE FlexibleInstances     #-}-{-# LANGUAGE TypeFamilies          #-}-{-# LANGUAGE TypeOperators         #-}-{-# LANGUAGE UndecidableInstances  #-}-{-# LANGUAGE ScopedTypeVariables   #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE GADTs                 #-}--module Generics.MultiRec.Rewriting.Rules where--import Generics.MultiRec-import Generics.MultiRec.LR-import Generics.MultiRec.HZip---------------------------------------------------------------------------------- Rule specification.---------------------------------------------------------------------------------- | Specifies a rule as a value of a datatype.-infix 5 :~>-data RuleSpec a = a :~> a---- | Returns the left-hand side of a rule.-lhsR :: RuleSpec a -> a-lhsR (x :~> _) = x---- | Returns the right-hand side of a rule.-rhsR :: RuleSpec a -> a-rhsR (_ :~> y) = y---------------------------------------------------------------------------------- Representation of a rule.--------------------------------------------------------------------------------- | Extends a pattern functor with a case for a metavariable.-type Ext phi  = K Metavar :+: PF phi-type Metavar  = Int---- | Recursively extends a type with a case for a metavariable.-type Scheme phi = HFix (Ext phi)---- | Allows metavariables on either side of a rule.-data Rule phi a where -  Rule :: phi ix -> RuleSpec (Scheme phi ix) -> Rule phi ix---- | Constructs a metavariable.-metavar :: phi ix -> Metavar -> Scheme phi ix-metavar _ = HIn . L . K--pf :: phi ix -> PF phi (Scheme phi) ix -> Scheme phi ix-pf _ = HIn . R---------------------------------------------------------------------------------- Builder for transforming a rule specification to a rule.--------------------------------------------------------------------------------class Builder phi a where-  type Target a :: *-  base          :: phi (Target a) -> a -> RuleSpec (Target a)-  diag          :: phi (Target a) -> a -> [RuleSpec (Target a)]--instance Builder phi (RuleSpec a) where-  type Target (RuleSpec a) = a-  base _ x                 = x-  diag _ x                 = [x]--instance (Builder phi a, Fam phi, LR phi (PF phi), El phi b)-         => Builder phi (b -> a) where-  type Target (b -> a) = Target a-  base ix f            = base ix (f (left  (proof :: phi b)))-  diag ix f            = base ix (f (right (proof :: phi b))) :-                         diag ix (f (left  (proof :: phi b)))--rule :: forall phi r. (Fam phi, Builder phi r, HZip phi (PF phi), -                       El phi (Target r), EqS phi, HFunctor phi (PF phi))-        => r -> Rule phi (Target r)-rule f = Rule ix $ foldr1 mergeRules rules-  where-    ix = proof :: phi (Target r)-    mergeRules x y = -      mergeSchemes ix (lhsR x) (lhsR y) :~>-      mergeSchemes ix (rhsR x) (rhsR y)-    rules          = zipWith (ins (base ix f)) (diag ix f) [0..]   -    ins x y v      = -      insertMVar v ix (I0 (lhsR x)) (I0 (lhsR y)) :~>-      insertMVar v ix (I0 (rhsR x)) (I0 (rhsR y))--mergeSchemes :: HZip phi (PF phi)-                => phi ix -> Scheme phi ix -> Scheme phi ix -> Scheme phi ix-mergeSchemes p a@(HIn x) b@(HIn y) = case (x,y) of-  (L _,_) -> a-  (_,L _) -> b-  _       -> HIn (hzip' mergeSchemes p x y)--insertMVar :: forall phi ix. (Fam phi, HZip phi (PF phi), El phi ix)-              => Metavar -> phi ix -> I0 ix -> I0 ix -> Scheme phi ix-insertMVar name p (I0 x) (I0 y) =-  case hzip (insertMVar name) p (from p x) (from p y) of-    Just struc -> pf p struc-    Nothing    -> metavar p name
+ Generics/MultiRec/ShallowEq.hs view
@@ -0,0 +1,48 @@+{-# LANGUAGE KindSignatures             #-}
+{-# LANGUAGE GADTs                      #-}
+{-# LANGUAGE RankNTypes                 #-}
+{-# LANGUAGE TypeOperators              #-}
+{-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE FlexibleInstances          #-}
+{-# LANGUAGE FlexibleContexts           #-}
+{-# LANGUAGE ScopedTypeVariables        #-}
+
+module Generics.MultiRec.ShallowEq where
+
+import Generics.MultiRec
+import Data.Foldable ( toList )
+import Data.Traversable ( Traversable )
+
+--------------------------------------------------------------------------------
+-- Shallow equality
+--------------------------------------------------------------------------------
+
+class SEq phi (f :: (* -> *) -> * -> *) where
+  shallowEq :: phi ix -> f r ix  -> f r ix -> Bool
+
+instance SEq phi (I xi) where
+  shallowEq _ _ _ = True
+
+instance SEq phi U where
+  shallowEq _ _ _ = True
+
+instance Eq a => SEq phi (K a) where
+  shallowEq p (K a) (K b) = a == b
+
+instance (SEq phi f, SEq phi g) => SEq phi (f :+: g) where
+  shallowEq p (L a) (L b) = shallowEq p a b
+  shallowEq p (R a) (R b) = shallowEq p a b
+  shallowEq _ _     _     = False
+
+instance (SEq phi f, SEq phi g) => SEq phi (f :*: g) where
+  shallowEq p (a :*: b) (c :*: d) = shallowEq p a c && shallowEq p b d
+
+instance SEq phi f => SEq phi (f :>: ix) where
+  shallowEq p (Tag a) (Tag b) = shallowEq p a b
+
+instance SEq phi f => SEq phi (C c f) where
+  shallowEq p (C a) (C b) = shallowEq p a b
+
+instance (Traversable t, Eq (t ()), SEq phi f) => SEq phi (t :.: f) where
+  shallowEq p (D a) (D b) = fmap (const ()) a == fmap (const ()) b
+                            && and (zipWith (shallowEq p) (toList a) (toList b))
+ Generics/MultiRec/Transformations/Children.hs view
@@ -0,0 +1,80 @@+{-# LANGUAGE TypeFamilies               #-}
+{-# LANGUAGE GADTs                      #-}
+{-# LANGUAGE RankNTypes                 #-}
+{-# LANGUAGE TypeOperators              #-}
+{-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE FlexibleInstances          #-}
+{-# LANGUAGE FlexibleContexts           #-}
+{-# LANGUAGE OverlappingInstances       #-}
+{-# LANGUAGE ScopedTypeVariables        #-}
+
+module Generics.MultiRec.Transformations.Children where
+
+import Generics.MultiRec hiding ( show, foldM )
+import Data.Foldable ( toList )
+
+import Generics.MultiRec.CountIs
+import Generics.MultiRec.Transformations.Path
+
+--------------------------------------------------------------------------------
+-- Children
+--------------------------------------------------------------------------------
+
+-- | Get all children with their paths
+allChildren :: forall phi ix xi. (Fam phi, Children phi (PF phi) xi)
+            => phi ix -> phi xi -> ix -> [(Path phi xi ix, xi)]
+allChildren p1 p2 = map (\(p,x) -> (p, unI0 x)) . children f p1 p2 (\w z -> Push w z Empty) . from p1 where
+  f :: forall ix'. phi ix' -> phi xi -> (Path phi ix' ix) -> I0 ix'
+    -> [(Path phi xi ix, I0 xi)]
+  f p1' p2' w (I0 y) = map (\(w', x) -> (w <.> w', I0 x)) $ allChildren p1' p2' y
+
+class Children phi (f :: (* -> *) -> * -> *) xi where
+  children :: (forall ix'. phi ix' -> phi xi
+                 -> Path phi ix' ix
+                 -> r ix' -> [(Path phi xi ix,r xi)])
+           -> phi ix -> phi xi -> (forall xi. phi xi -> Dir f xi ix -> Path phi xi ix)
+             -> f r ix -> [(Path phi xi ix, r xi)]
+
+instance (Fam phi, El phi ix)            => Children phi (I ix) ix where
+  children f p1 p2 w (I r) = (w p2 CId, r) : f proof proof (w p2    CId) r
+
+instance (Fam phi, El phi ix, El phi xi) => Children phi (I xi) ix where
+  children f p1 p2 w (I r) =                 f proof proof (w proof CId) r
+
+instance Children phi (K a) ix where
+  children _ _ _ _ _ = []
+
+instance Children phi U ix where
+  children _ _ _ _ _ = []
+
+instance (Children phi f ix, Children phi g ix) => Children phi (f :+: g) ix where
+  children f p1 p2 w (L x) = children f p1 p2 (\w' -> w w' . CL) x
+  children f p1 p2 w (R x) = children f p1 p2 (\w' -> w w' . CR) x
+
+instance (Children phi f ix, Children phi g ix, CountIs g)
+    => Children phi (f :*: g) ix where
+  children f p1 p2 w (x :*: y) =    children f p1 p2 (\w' z -> w w' (C1 z nullY)) x
+                                 ++ children f p1 p2 (\w' z -> w w' (C2 nullX z)) y
+    where nullX = error "nullX" -- fmap (const ()) x
+          nullY = error "nullY" -- fmap (const ()) y
+
+instance (Constructor c, Children phi f ix) => Children phi (C c f) ix where
+  children f p1 p2 w (C x) = children f p1 p2 (\w' -> w w' . CC) x
+
+instance Children phi f ix => Children phi (f :>: xi) ix where
+  children f p1 p2 w (Tag x) = children f p1 p2 (\w' -> w w' . CTag) x
+
+{-
+instance (Traversable t, Children phi f ix) => Children phi (t :.: f) ix where
+ children f p1 p2 w (D x) = concatMap (\(i,x) -> children f p1 p2 (w . TrvI i) x)
+                            $ zip [0..] (toList x)
+-}
+
+instance (Children phi f ix) => Children phi (Maybe :.: f) ix where
+ children f p1 p2 w (D x) = concatMap (children f p1 p2 (\w' -> w w' . CCM)) . toList $ x
+
+instance (Children phi f ix) => Children phi ([] :.: f) ix where
+ children f p1 p2 w (D x) = concatMap (\(i,x) -> children f p1 p2 (\w' z -> w w' (CCL (ll i) z lr)) x)
+                            $ zip [0..] x
+      where ll i = replicate i (error "oops4")
+            lr = error "oops5"
− Generics/MultiRec/Transformations/Explicit.hs
@@ -1,442 +0,0 @@-{-# LANGUAGE TypeFamilies          #-}-{-# LANGUAGE GADTs                 #-}-{-# LANGUAGE RankNTypes            #-}-{-# LANGUAGE TypeOperators         #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FlexibleInstances     #-}-{-# LANGUAGE FlexibleContexts      #-}-{-# LANGUAGE UndecidableInstances  #-}-{-# LANGUAGE ScopedTypeVariables   #-}--module Generics.MultiRec.Transformations.Explicit (-  diff, apply, Transformation, AnyInsert (..), WithRef (..), Path, -  Transform, OrdI (..),-  HasRef (..), NiceTransformation, NiceInsert (..),-  toNiceTransformation, fromNiceTransformation-  ) where--import Generics.MultiRec.Any-import Generics.MultiRec.Eq-import Generics.MultiRec.Ord--import Generics.MultiRec hiding (show, foldM)-import Control.Applicative ( (<|>) )-import Control.Monad (foldM)-import Control.Monad.State hiding (foldM)-import Data.Monoid (mappend)-import qualified Data.Map as Map-import Data.Map (Map)------------------------------------------------------------------------------------- Paths, annotations, edits and existentials----------------------------------------------------------------------------------data WithRef phi f a = InR (PF phi f a)-                     | Ref Path--type Path = [Int]--data AnyInsert phi where-  AnyInsert :: phi ix -> Path -> HFix (WithRef phi) ix -> AnyInsert phi--type Transformation phi = [ AnyInsert phi]--class (Fam phi, Children phi (PF phi), CountI phi (PF phi),-       HFunctor phi (PF phi), SEq phi (PF phi), ExtractN phi (PF phi), -       MapN phi (PF phi), EqS phi, HEq phi (PF phi), HOrd phi (PF phi),-       OrdI phi) => Transform phi------------------------------------------------------------------------------------- Applying------------------------------------------------------------------------------------ | Apply the transformation to the given tree-apply :: forall phi ix. (Transform phi)-         => phi ix -> ix -> Transformation phi -> Maybe ix-apply p t = foldM (apply' p) t where-  apply' :: forall ix. phi ix -> ix -> AnyInsert phi -> Maybe ix-  apply' p' _ (AnyInsert p'' [] c) = case eqS p' p'' of -    Just Refl -> lookupRefs p t p' c-    Nothing   -> Nothing-  apply' p' a (AnyInsert p'' (i:is) c) =-    liftM (to p') $ tmapN f p' $ from p' a where-      f :: forall ix. Int -> phi ix -> I0 ix -> Maybe (I0 ix)-      f j p''' x | i == j    = liftM I0 (apply' p''' (unI0 x) (AnyInsert p'' is c))-                 | otherwise = return x---- | Look up the references using the original structure-lookupRefs :: forall phi ix ix'. (Fam phi, HFunctor phi (PF phi), ExtractN phi (PF phi), EqS phi) -              => phi ix -> ix -> phi ix' -> HFix (WithRef phi) ix' -> Maybe ix'-lookupRefs p r p' = build .  hout where-  build :: WithRef phi (HFix (WithRef phi)) ix' -> Maybe ix'-  build (InR x) = liftM (to p') (hmapM (\p'' -> liftM I0 . lookupRefs p r p'') p' x)-  build (Ref l) = extract l p r >>= matchAny p'---- | Extract the subtree at the given path-extract :: (Fam phi, ExtractN phi (PF phi)) => Path -> phi ix -> ix -> Maybe (Any phi)-extract []     p a = return $ Any p a-extract (i:is) p a = extractN i p a >>= \(Any p' x) -> extract is p' x------------------------------------------------------------------------------------- Memoisation------------------------------------------------------------------------------------ | Comparing index of different types-class OrdI phi where-  compareI :: phi ix -> phi ix' -> Ordering-  compareI p1 p2 = compare (indexI p1) (indexI p2)-  indexI :: phi ix -> Int-  indexI = error "At least compareI or indexI should be implemented."---- | Key used in memoisation table-data MemoKey phi where-  MemoKey :: phi ix -> Bool -> ix -> ix -> MemoKey phi--instance (EqS phi, Fam phi, HEq phi (PF phi)) => Eq (MemoKey phi) where-  (MemoKey p1 a1 b1 c1) == (MemoKey p2 a2 b2 c2) = case eqS p1 p2 of-    Nothing   -> False-    Just Refl -> a1 == a2 && eq p1 b1 b2 && eq p1 c1 c2--instance (EqS phi, Fam phi, OrdI phi, HEq phi (PF phi), HOrd phi (PF phi))-         => Ord (MemoKey phi) where-  compare (MemoKey p1 a1 b1 c1) (MemoKey p2 a2 b2 c2) = case eqS p1 p2 of-    Nothing   -> compareI p1 p2-    Just Refl -> compare a1 a2 `mappend` gcompare p1 b1 b2 -                               `mappend` gcompare p1 c1 c2---- | The type of the memo table-type MemoTable phi = Map (MemoKey phi) (Transformation phi)-type Memo phi a = State (MemoTable phi) a--runMemo :: Memo phi a -> a-runMemo = flip evalState Map.empty--recMemo :: (Fam phi, HEq phi (PF phi), HOrd phi (PF phi), EqS phi, OrdI phi) => -           (forall ix. Bool -> phi ix -> ix -> ix -> Memo phi (Transformation phi))-           -> Bool -> phi ix -> ix -> ix -> Memo phi (Transformation phi)-recMemo f a p b c = do-  mp <- get-  let k = MemoKey p a b c-  case Map.lookup k mp of-    Just r -> return r-    Nothing -> do-      r <- f a p b c-      modify (Map.insert k r)-      return r------------------------------------------------------------------------------------- Diffing------------------------------------------------------------------------------------ | Find a set of insertions to transform the first into the second tree-diff :: forall phi ix. (Transform phi)-        => phi ix -> ix -> ix -> Transformation phi-diff p a b = runMemo (build False p a b)-  where-    childPaths :: [(Any phi, Path)]-    childPaths = childrenPaths p a-    build :: forall ix. Bool -> phi ix -> ix -> ix -> Memo phi (Transformation phi)-    build False p' a' b' | eq p' a' b' = return []-    build ins   p' a' b' = case anyLookup p' b' childPaths of-      Just l  -> return [ AnyInsert p' [] (HIn $ Ref l) ]-      Nothing -> uses >>= maybe insert return -- Only insert when we cannot reuse-        where-          -- Construct the edits for the children based on a root-          construct :: Bool -> ix -> Memo phi (Maybe (Transformation phi))-          construct ins' c = -            if shallowEq p' (from p' c) (from p' b')-            then do r <- zipWithM (\(Any p1 c1) (Any p2 c2) -> case eqS p1 p2 of-                                      Just Refl -> recMemo build ins' p1 c1 c2)-                         (imChildren p' c) (imChildren p' b')-                    return $ Just $ concat $ updateChildPaths r-            else return Nothing-          -- Possible edits reusing the existing tree or using a part of-          -- the original tree. The existing tree is only used if we didn't-          -- just insert it, since we want to keep the inserts small-          uses :: Memo phi (Maybe (Transformation phi))-          uses = reuses >>= \re -> case re of-              Just r | ins -> return re-              _            -> construct ins a' >>= return . pickBest re-          -- Possible edits that include reusing a part of the original tree-          reuses :: Memo phi (Maybe (Transformation phi))-          reuses = foldM f Nothing childPaths where-            addRef :: Path -> Maybe (Transformation phi) -                       -> Maybe (Transformation phi)-            addRef l = liftM ((AnyInsert p' [] (HIn $ Ref l)):)-            f c (Any p'' x, l) = case eqS p' p'' of-              Just Refl -> construct False x >>= return . pickBest c . addRef l-              Nothing   -> return c-          -- Best edit including insertion, only chosen if nothing can be reused-          insert :: Memo phi (Transformation phi)-          insert = do-            Just r <- construct True b'-            let (r',e')  = partialApply p' (annotate p' b') r-            return $ (AnyInsert p' [] r') : e'---- | Pick the best edit-pickBest :: Maybe (Transformation phi) -> Maybe (Transformation phi) -> Maybe (Transformation phi)-pickBest e1 e2 = case (e1,e2) of-  (Just e1', Just e2') -> Just (pickShortest e1' e2')-  _                    -> e1 <|> e2---- | Pick the shortest of two lists lazily-pickShortest :: [a] -> [a] -> [a]-pickShortest a b = if f a b then a else b-  where f []     _      = True-        f _      []     = False-        f (_:xs) (_:ys) = f xs ys---- | Lookup with a specific type-anyLookup :: (Fam phi, EqS phi, HEq phi (PF phi))-             => phi ix -> ix -> [(Any phi, a)] -> Maybe a-anyLookup p _ [] = Nothing-anyLookup p x ((Any p' y,r) : ys) = case eqS p p' of-  Just Refl | eq p x y -> Just r-  _                    -> anyLookup p x ys---- | Lift a tree to an edit structure-annotate :: (Fam phi, HFunctor phi (PF phi)) => phi ix -> ix -> HFix (WithRef phi) ix-annotate p = HIn . InR . hmap (\p' (I0 x) -> annotate p' x) p . from p---- | Extend the paths of edits for the children with the child number-updateChildPaths :: [Transformation phi] -> [Transformation phi]-updateChildPaths = zipWith (\n -> map (\(AnyInsert p l c) -> (AnyInsert p (n:l) c))) [0..]---- | Try to apply as much edits to the edit structure as possible---   to make the final edit smaller-partialApply :: (Fam phi, CountI phi (PF phi), ExtractN phi (PF phi), MapN phi (PF phi), EqS phi)-                => phi ix -> HFix (WithRef phi) ix -> Transformation phi -> (HFix (WithRef phi) ix, Transformation phi)-partialApply _ a [] = (a, [])-partialApply p a (AnyInsert p' l x : xs) = case replace p' l x p a of-  Just a' -> partialApply p a' xs-  Nothing -> let (a',xs') = partialApply p a xs in (a', AnyInsert p' l x : xs')---- | Replace a subtree in an edit structure-replace :: forall phi ix ix'. (Fam phi, EqS phi, MapN phi (PF phi))-           => phi ix -> Path -> HFix (WithRef phi) ix-           -> phi ix' -> HFix (WithRef phi) ix' -> Maybe (HFix (WithRef phi) ix')-replace p [] r p' _ = case eqS p p' of-  Just Refl -> Just r-  Nothing   -> Nothing-replace p (i:is) r p' a = case hout a of-  Ref _  -> Nothing-  InR a' -> liftM HIn . liftM InR . tmapN f p' $ a'-    where f :: forall ix. Int -> phi ix -> HFix (WithRef phi) ix -> Maybe (HFix (WithRef phi) ix)-          f j p'' = if i == j then replace p is r p'' else Just------------------------------------------------------------------------------------- Shallow equality-----------------------------------------------------------------------------------class SEq phi (f :: (* -> *) -> * -> *) where-  shallowEq :: phi ix -> f r ix  -> f r ix -> Bool--instance El phi xi => SEq phi (I xi) where-  shallowEq _ (I _) (I _) = True--instance SEq phi U where-  shallowEq _ U U = True--instance Eq a => SEq phi (K a) where-  shallowEq p (K a) (K b) = a == b--instance (SEq phi f, SEq phi g) => SEq phi (f :+: g) where-  shallowEq p (L a) (L b) = shallowEq p a b-  shallowEq p (R a) (R b) = shallowEq p a b-  shallowEq _ _     _     = False--instance (SEq phi f, SEq phi g) => SEq phi (f :*: g) where-  shallowEq p (a :*: b) (c :*: d) = shallowEq p a c && shallowEq p b d--instance SEq phi f => SEq phi (f :>: ix) where-  shallowEq p (Tag a) (Tag b) = shallowEq p a b--instance SEq phi f => SEq phi (C c f) where-  shallowEq p (C a) (C b) = shallowEq p a b---- Todo: is this the best choice?-instance SEq phi ([] :.: ix) where-  shallowEq p (D a) (D b) = length a == length b------------------------------------------------------------------------------------- ExtractN-----------------------------------------------------------------------------------extractN :: (Fam phi, ExtractN phi (PF phi), Monad m) -            => Int -> phi ix -> ix -> m (Any phi)-extractN i p v = extractN' (\p (I0 v) -> Any p v) i p (from p v)--class ExtractN phi (f :: (* -> *) -> * -> *) where-  extractN' :: Monad m => (forall ix. phi ix -> r ix -> r')-                          -> Int -> phi ix -> f r ix -> m r'--instance El phi xi => ExtractN phi (I xi) where-  extractN' mka 0 _ (I r) = return $ mka proof r-  extractN' _   _ _ (I _) = fail "extractN"--instance ExtractN phi (K a) where-  extractN' mka _ _ (K _) = fail "extractN"--instance ExtractN phi U where-  extractN' mka _ _ U = fail "extractN"--instance (ExtractN phi f, ExtractN phi g) => ExtractN phi (f :+: g) where-  extractN' mka i p (L x) = extractN' mka i p x-  extractN' mka i p (R x) = extractN' mka i p x--instance (CountI phi f, ExtractN phi f, ExtractN phi g) => ExtractN phi (f :*: g) where-  extractN' mka i p (x :*: y) = let n = countI p x-                                in if i < n then extractN' mka i     p x-                                            else extractN' mka (i-n) p y--instance ExtractN phi f => ExtractN phi (f :>: ix) where-  extractN' mka i p (Tag x) = extractN' mka i p x--instance ExtractN phi f => ExtractN phi (C c f) where-  extractN' mka i p (C x) = extractN' mka i p x---- Todo: is this the best choice?-instance ExtractN phi f => ExtractN phi ([] :.: f) where-  extractN' mka i p (D x) = extractN' mka 0 p (x !! i)------------------------------------------------------------------------------------- MapN------------------------------------------------------------------------------------- | Map a function with child index at a top-level structure-tmapN :: (Fam phi, MapN phi f, Monad m)-         => (forall ix. Int -> phi ix -> r ix -> m (r' ix))-         -> phi ix -> f r ix -> m (f r' ix)-tmapN = mapN 0--class MapN phi (f :: (* -> *) -> * -> *) where-  mapN :: Monad m => Int -> (forall ix. Int -> phi ix -> r ix -> m (r' ix))-                           -> phi ix -> f r ix -> m (f r' ix)--instance El phi xi => MapN phi (I xi) where-  mapN i f p (I x) = liftM I (f i proof x)--instance MapN phi (K a) where-  mapN _ _ _ (K x)  = return $ K x--instance MapN phi U where-  mapN _ _ _ U = return U--instance (MapN phi f, MapN phi g) => MapN phi (f :+: g) where-  mapN i f p (L x) = liftM L (mapN i f p x)-  mapN i f p (R x) = liftM R (mapN i f p x)---- Here we increment our parameter. Does not require right-nested products-instance (CountI phi f, MapN phi f, MapN phi g) => MapN phi (f :*: g) where-  mapN i f p (x :*: y) = liftM2 (:*:) (mapN i f p x) (mapN (i + countI p x) f p y)--instance MapN phi f => MapN phi (f :>: ix) where-  mapN i f p (Tag x) = liftM Tag (mapN i f p x)--instance MapN phi f => MapN phi (C c f) where-  mapN i f p (C x) = liftM C (mapN i f p x)---- Todo: is this the best choice?-instance (CountI phi f, MapN phi f) => MapN phi ([] :.: f) where-  mapN i f p (D [])     = return $ D []-  mapN i f p (D (x:xs)) = do h <- mapN i f p x-                             t <- mapN (i + countI p x) f p (D xs)-                             return $ D (h : unD t)------------------------------------------------------------------------------------- CountI-----------------------------------------------------------------------------------class CountI phi (f :: (* -> *) -> * -> *) where-  -- | Count the number of recursive occurrences-  countI :: phi ix -> f r ix -> Int--instance El phi xi => CountI phi (I xi) where-  countI _ _ = 1--instance CountI phi (K a) where-  countI _ _ = 0--instance CountI phi U where-  countI _ _ = 0--instance (CountI phi f, CountI phi g) => CountI phi (f :+: g) where-  countI p (L x) = countI p x-  countI p (R x) = countI p x--instance (CountI phi f, CountI phi g) => CountI phi (f :*: g) where-  countI p (x :*: y) = countI p x + countI p y--instance CountI phi f => CountI phi (f :>: ix) where-  countI p (Tag x) = countI p x--instance CountI phi f => CountI phi (C c f) where-  countI p (C x) = countI p x---- Todo: is this the best choice?-instance CountI phi f => CountI phi ([] :.: f) where-  countI p (D x) = sum (map (countI p) x)------------------------------------------------------------------------------------- Children------------------------------------------------------------------------------------- | Get the immediate children-imChildren :: (Fam phi, Children phi (PF phi)) => phi ix -> ix -> [Any phi]-imChildren p x = children (\p (I0 v) -> Any p v) p (from p x)---- | Get all children with their paths-childrenPaths :: (Fam phi, Children phi (PF phi)) => phi ix -> ix -> [(Any phi, Path)]-childrenPaths p a = (Any p a, []) : -                    [ (r, n : p)-                    | (n, Any p' c) <- zip [0..] (imChildren p a)-                    , (r, p) <- childrenPaths p' c ]--class Children phi (f :: (* -> *) -> * -> *) where-  children :: (forall ix. phi ix -> r ix -> Any phi) -> phi ix -> f r ix -> [Any phi]--instance (Fam phi, El phi xi) => Children phi (I xi) where-  children mka _ (I r) = [mka proof r]--instance Children phi (K a) where-  children _ _ (K _) = []--instance Children phi U where-  children _ _ U = []--instance (Children phi f, Children phi g) => Children phi (f :+: g) where-  children mka p (L x) = children mka p x-  children mka p (R x) = children mka p x--instance (Children phi f, Children phi g) => Children phi (f :*: g) where-  children mka p (x :*: y) = children mka p x ++ children mka p y--instance Children phi f => Children phi (C c f) where-  children mka p (C x) = children mka p x--instance Children phi f => Children phi (f :>: ix) where-  children mka p (Tag x) = children mka p x---- Todo: is this the best choice?-instance Children phi f => Children phi ([] :.: f) where-  children mka p (D x) = concatMap (children mka p) x------------------------------------------------------------------------------------- Nicer interface-----------------------------------------------------------------------------------class HasRef phi where-  type RefRep phi ix-  -- I don't like HFix here but can't figure out how to do a-  -- single step unwrapping-  toRef   :: phi ix -> HFix (WithRef phi) ix -> RefRep phi ix-  fromRef :: phi ix -> RefRep phi ix -> HFix (WithRef phi) ix--data NiceInsert phi where-  NiceInsert :: phi ix -> Path -> RefRep phi ix -> NiceInsert phi--type NiceTransformation phi = [ NiceInsert phi]--toNiceTransformation :: HasRef phi => Transformation phi -> NiceTransformation phi-toNiceTransformation = map f-  where f (AnyInsert p l x) = NiceInsert p l (toRef p x)--fromNiceTransformation :: HasRef phi => NiceTransformation phi -> Transformation phi-fromNiceTransformation = map f-  where f (NiceInsert p l x) = AnyInsert p l (fromRef p x)
+ Generics/MultiRec/Transformations/Main.hs view
@@ -0,0 +1,190 @@+{-# LANGUAGE TypeFamilies               #-}
+{-# LANGUAGE GADTs                      #-}
+{-# LANGUAGE RankNTypes                 #-}
+{-# LANGUAGE TypeOperators              #-}
+{-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE FlexibleInstances          #-}
+{-# LANGUAGE FlexibleContexts           #-}
+{-# LANGUAGE ScopedTypeVariables        #-}
+{-# LANGUAGE DataKinds                  #-}
+{-# LANGUAGE PolyKinds                  #-}
+{-# LANGUAGE ConstraintKinds            #-}
+
+module Generics.MultiRec.Transformations.Main
+    ( diff, apply, Ixs, Transformation )
+  where
+
+import Generics.MultiRec hiding ( show, foldM )
+import Control.Applicative ( (<|>) )
+import Control.Monad ( foldM )
+import Control.Monad.State hiding ( foldM, mapM )
+
+import Generics.MultiRec.ShallowEq
+import Generics.MultiRec.Transformations.Path
+import Generics.MultiRec.Transformations.Children
+import Generics.MultiRec.Transformations.ZipChildren
+import Generics.MultiRec.Transformations.MemoTable
+
+--------------------------------------------------------------------------------
+-- Synonyms
+--------------------------------------------------------------------------------
+
+-- | A constraint synonym for convenience
+type Transform phi = ( Fam phi, HFunctor phi (PF phi), SEq phi (PF phi)
+                     , ZipChildren phi (PF phi), MapP phi (PF phi)
+                     , Extract (PF phi))
+
+-- | Transformations are just sequences of insertions
+type Transformation phi top = [ Insert phi top top ]
+
+--------------------------------------------------------------------------------
+-- Applying
+--------------------------------------------------------------------------------
+
+-- | Apply the transformation to the given tree
+apply :: forall phi ix. (Transform phi)
+         => phi ix -> ix -> Transformation phi ix -> Maybe ix
+apply p t = foldM apply' t where
+  apply' :: ix -> Insert phi ix ix -> Maybe ix
+  apply' a (Insert _ loc repl) = mapP p loc (\pt _ -> lookupRefs p t pt repl) a
+
+-- | Look up the references using the original structure
+lookupRefs :: forall phi top a. (Transform phi)
+              => phi top -> top -> phi a -> HWithRef phi top a -> Maybe a
+lookupRefs p r p' (HIn (InR x))   = liftM (to p') $
+                                    hmapM (\p'' -> liftM I0 . lookupRefs p r p'') p' x
+lookupRefs p r _  (HIn (Ref loc)) = extract p loc r
+
+-- | Extract the subtree at the given path
+extract :: forall phi i t. (Transform phi)
+        => phi i -> Path phi t i -> i -> Maybe t
+extract w1 Empty          x = Just x
+extract w1 (Push w2 p ps) x =     fmap unI0 (extract' return w1 p (from w1 x))
+                              >>= extract w2 ps
+
+class Extract f where
+  extract' :: (r t -> Maybe (r t))
+           -> phi ix -> Dir f t ix -> f r ix -> Maybe (r t)
+
+instance (Extract f, Extract g) => Extract (f :+: g) where
+  extract' f w (CL p) (L x) = extract' f w p x
+  extract' f w (CR p) (R x) = extract' f w p x
+  extract' f w _      _     = Nothing
+
+instance (Extract f, Extract g) => Extract (f :*: g) where
+  extract' f w (C1 p _) (x :*: _) = extract' f w p x
+  extract' f w (C2 _ p) (_ :*: y) = extract' f w p y
+
+instance Extract (I ix) where
+  extract' f w CId (I x) = f x
+
+instance Extract U     where extract' _ _ _ _ = Nothing
+instance Extract (K a) where extract' _ _ _ _ = Nothing
+
+instance (Extract f) => Extract (f :>: ix) where
+  extract' f w (CTag p) (Tag x) = extract' f w p x
+
+instance (Extract f) => Extract (C c f) where
+  extract' f w (CC p) (C x) = extract' f w p x
+
+instance (Extract f) => Extract (Maybe :.: f) where
+  extract' f w (CCM p) (D x) = x >>= extract' f w p
+
+instance (Extract f) => Extract ([] :.: f) where
+  extract' f w (CCL l p _) (D x) = extract' f w p (x !! length l)
+
+--------------------------------------------------------------------------------
+-- Diffing
+--------------------------------------------------------------------------------
+-- | Find a set of insertions to transform the first into the second tree
+
+-- Jeroen says: we could make the code of uses nicer if we define a
+-- <|>' :: Monad m => m (Maybe a) -> m (Maybe a) -> m (Maybe a)
+-- then it will look similar to the version in the paper
+diff :: forall phi top. (Transform phi, Children phi (PF phi) top, EmptyMemo phi top (Ixs phi), ChildrenTable phi top (Ixs phi), GetChildrenTable phi (Ixs phi) top, Eq top)
+        => phi top -> top -> top -> Transformation phi top
+diff p a b = runMemo (Proxy :: Proxy '(phi,top)) $ build False p a b where
+  chTbl :: HList (ChildTable phi top (Ixs phi))
+  chTbl = childrenTable (Proxy :: Proxy '(phi, top,Ixs phi)) a
+  build :: forall a. (Children phi (PF phi) a, Eq a, GetChildrenTable phi (Ixs phi) a) => Bool -> phi a -> a -> a -> Memo phi top [ Insert phi top a ]
+  build False p' a' b' | a' == b' = return []
+  build ins   p' a' b' =
+    let -- All children of this type
+        allChildren :: Children phi (PF phi) a => [(Path phi a top, a)]
+        allChildren = getChTable (Proxy :: Proxy '(phi, top, Ixs phi)) chTbl
+    in case childLookup p' b' allChildren of
+      Just l  -> return [ Insert p' Empty (HIn (Ref l)) ]
+      Nothing -> uses >>= maybe insert return where -- Only insert when we cannot reuse
+        -- Construct the edits for the children based on a root
+        construct :: Bool -> a -> Memo phi top (Maybe [ Insert phi top a ])
+        construct ins' c =
+          if shallowEq p' (from p' c) (from p' b')
+          then do r <- zipChildrenM p'
+                       (\p1 l1 c1 c2 -> recMemo build ins' p1 c1 c2
+                                        >>= return . map (updatePath l1)
+                       ) c b'
+                  return $ Just $ concat r
+          else return Nothing
+        -- Possible edits reusing the existing tree or using a part of
+        -- the original tree. The existing tree is only used if we didn't
+        -- just insert it, since we want to keep the inserts small
+        uses :: Memo phi top (Maybe [ Insert phi top a ])
+        uses =  reuses >>= \re -> case re of
+          Just r | ins -> return re
+          _            -> construct ins a' >>= return . pickBest re
+        -- Possible edits that include reusing a part of the original tree
+        reuses :: Memo phi top (Maybe [ Insert phi top a ])
+        reuses = foldM f Nothing allChildren where
+          f :: Maybe [ Insert phi top a ] -> (Path phi a top, a)
+               -> Memo phi top (Maybe [ Insert phi top a ])
+          f c (l,x) = construct False x >>= return . pickBest c . addRef l
+          addRef :: Path phi a top -> Maybe [ Insert phi top a ]
+                    -> Maybe [ Insert phi top a ]
+          addRef l = liftM ((Insert p' Empty (HIn (Ref l))):)
+        -- Best edit including insertion, only chosen if nothing can be reused
+        insert :: Memo phi top [ Insert phi top a ]
+        insert = do
+          Just r <- construct True b'
+          let (r',e') = partialApply p' (annotate p' b') r
+          return $ (Insert p' Empty r') : e'
+
+-- | Update insert location
+updatePath :: Path phi a b -> Insert phi top a -> Insert phi top b
+updatePath p (Insert w loc v) = Insert w (p <.> loc) v
+
+-- | Pick the best edit
+pickBest :: Maybe [a] -> Maybe [a] -> Maybe [a]
+pickBest e1 e2 = case (e1,e2) of
+  (Just e1', Just e2') -> Just (pickShortest e1' e2')
+  _                    -> e1 <|> e2
+
+-- | Pick the shortest of two lists lazily
+pickShortest :: [a] -> [a] -> [a]
+pickShortest a b = if f a b then a else b
+  where f []     _      = True
+        f _      []     = False
+        f (_:xs) (_:ys) = f xs ys
+
+-- | Lookup a child with a given type
+childLookup :: (Fam phi, Eq t)
+             => phi t -> t -> [(Path phi t ix, t)] -> Maybe (Path phi t ix)
+childLookup p _ [] = Nothing
+childLookup p x ((r,y) : ys) | x == y    = Just r
+                             | otherwise = childLookup p x ys
+
+-- | Lift a tree to an edit structure
+annotate :: (Fam phi, HFunctor phi (PF phi))
+         => phi ix -> ix -> HWithRef phi top ix
+annotate p = HIn . InR . hmap (\p' (I0 x) -> annotate p' x) p . from p
+
+-- | Try to apply as much edits to the edit structure as possible
+--   to make the final edit smaller
+partialApply :: forall phi top a. (Transform phi)
+                => phi a
+                -> HWithRef phi top a
+                -> [Insert phi top a]
+                -> (HWithRef phi top a, [Insert phi top a])
+partialApply _ a [] = (a, [])
+partialApply p a (Insert w l x : xs) = case mapPR p l (\_ _ -> Just x) a of
+  Just a' -> partialApply p a' xs
+  Nothing -> let (a',xs') = partialApply p a xs in (a', Insert w l x : xs')
+ Generics/MultiRec/Transformations/MemoTable.hs view
@@ -0,0 +1,165 @@+{-# LANGUAGE TypeFamilies               #-}
+{-# LANGUAGE GADTs                      #-}
+{-# LANGUAGE RankNTypes                 #-}
+{-# LANGUAGE TypeOperators              #-}
+{-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE FlexibleInstances          #-}
+{-# LANGUAGE FlexibleContexts           #-}
+{-# LANGUAGE OverlappingInstances       #-}
+{-# LANGUAGE UndecidableInstances       #-}
+{-# LANGUAGE ScopedTypeVariables        #-}
+{-# LANGUAGE DataKinds                  #-}
+{-# LANGUAGE PolyKinds                  #-}
+
+module Generics.MultiRec.Transformations.MemoTable where
+
+import Generics.MultiRec hiding ( show, foldM )
+import Control.Monad.State hiding ( foldM, mapM )
+import qualified Data.Map as Map
+import Data.Map ( Map )
+
+import Generics.MultiRec.Transformations.Path
+import Generics.MultiRec.Transformations.Children
+
+--------------------------------------------------------------------------------
+-- One memotable per type in the family
+--------------------------------------------------------------------------------
+type family Ixs (phi :: * -> *) :: [*] -- one instance per family; tells us
+                                       -- which types are in the family
+
+data HList (l :: [*]) where -- heterogeneous collections
+  HNil  :: HList '[]
+  HCons :: h -> HList t -> HList (h ': t)
+
+type family HMap (h :: * -> * -> *) (f :: * -> *) (g :: * -> *) (l :: [*]) :: [*]
+type instance HMap h f g '[]      = '[]
+type instance HMap h f g (t ': ts) = h (f t) (g t) ': HMap h f g ts
+
+-- Memotables are now heterogenous collections mapping keys to values, indexed
+-- over each family type
+-- type MemoTable' phi top ixs = HList (HMap Map (MemKey' top) (MemVal' top) ixs)
+type MemoTable phi top ixs = HList (MemoTable' One phi top ixs)
+
+data Type = One | Two
+
+type family MemoTable' (x :: Type) (phi :: * -> *) top (ixs :: [*]) :: [*]
+type instance MemoTable' x phi top '[]       = '[]
+type instance MemoTable' x phi top (t ': ts) = MemCell x phi top t
+                                            ': MemoTable' x phi top ts
+
+type family MemCell (x :: Type) (phi :: * -> *) top (t :: *) :: *
+-- type instance MemCell One phi top t = Map (MemKey t) (MemVal phi top  t)
+type instance MemCell One phi top t = Map (MemKey t) (MemVal phi top  t)
+
+type MemKey         t = (Bool, t, t)
+type MemVal phi top t = [Insert phi top t]
+
+-- Lookup on the right table
+data Proxy (t :: k) = Proxy
+class {- (ixs ~ Ixs phi) => -} Lookup phi (ixs :: [*]) ix where
+  lookupMT :: Proxy '(phi, ixs)
+           -- I don't like this Proxy too much, but I'm afraid it's necessary
+           -> MemoTable phi top ixs -> MemKey ix -> Maybe (MemVal phi top ix)
+  insertMT :: Proxy '(phi, ixs)
+              -> MemKey ix -> MemVal phi top ix
+              -> MemoTable phi top ixs -> MemoTable phi top ixs
+
+-- this probably shouldn't happen...
+instance Lookup phi '[] ix where
+  lookupMT _ HNil _ = Nothing
+  insertMT _ _ _ HNil = HNil
+
+-- this is the right memotable for this type
+instance (Ord t) => Lookup phi (t ': ts) t where
+  lookupMT _ (HCons mt _) k = Map.lookup k mt
+  insertMT _ k v (HCons mt tl) = HCons (Map.insert k v mt) tl
+
+-- this is not the right memotable for this type, keep searching
+instance (Lookup phi ts ix) => Lookup phi (t ': ts) ix where
+  lookupMT _ (HCons _ mts) = lookupMT (Proxy :: Proxy '(phi, ts)) mts
+  insertMT _ k v (HCons hd mts) = HCons hd (insertMT (Proxy :: Proxy '(phi, ts)) k v mts)
+
+type Memo phi top a = State (MemoTable phi top (Ixs phi)) a
+
+class EmptyMemo (phi :: * -> *) top (ixs :: [*]) where
+  emptyMemo :: Proxy '(phi,top,ixs) -> MemoTable phi top ixs
+
+instance EmptyMemo phi top '[] where
+  emptyMemo _ = HNil
+
+instance (EmptyMemo phi top t) => EmptyMemo phi top (h ': t) where
+  emptyMemo _ = HCons Map.empty (emptyMemo (Proxy :: Proxy '(phi,top,t)))
+
+runMemo :: forall phi top a. (EmptyMemo phi top (Ixs phi))
+        => Proxy '(phi,top) -> Memo phi top a -> a
+runMemo _ = flip evalState (emptyMemo (Proxy :: Proxy '(phi,top,Ixs phi)))
+
+recMemo :: forall phi top ix.
+           (Fam phi, Children phi (PF phi) ix, Lookup phi (Ixs phi) ix, Eq ix, GetChildrenTable phi (Ixs phi) ix) =>
+           (forall ix. (Children phi (PF phi) ix, Lookup phi (Ixs phi) ix, Eq ix, GetChildrenTable phi (Ixs phi) ix) => Bool -> phi ix -> ix -> ix -> Memo phi top [ Insert phi top ix ])
+           -> Bool -> phi ix -> ix -> ix -> Memo phi top [ Insert phi top ix ]
+recMemo f a p b c = do
+  mp <- get
+  let k = (a,b,c)
+  case lookupMT (Proxy :: Proxy '(phi, Ixs phi)) mp k of
+    Just r -> return r
+    Nothing -> do
+      r <- f a p b c
+      modify $ insertMT (Proxy :: Proxy '(phi, Ixs phi)) k r
+      return r
+
+--------------------------------------------------------------------------------
+-- Memo table of all children
+--------------------------------------------------------------------------------
+-- type family ChildTable (phi :: * -> *) top (ixs :: [*]) :: [*]
+-- type instance ChildTable phi top '[]       = '[]
+-- type instance ChildTable phi top (t ': ts) = [(Path phi top t, t)] ': ChildTable phi top ts
+
+type ChildTable phi top ixs = MemoTable' Two phi top ixs
+type instance MemCell Two phi top t = [(Path phi t top, t)]
+
+{-
+This is a first step to merge ChildTable and MemoTable. However, it would be
+really cool if we can also merge getChTable and lookupMT, for example. But for
+that we have to change MemCell to either be a Map or a list of pairs.
+
+Changing ChildTable to use a Map as its cell isn't nice, because we start
+requiring |Ord (r ix)| constraints in AllChildren.
+
+The other way around, changing MemoTable to use a list of pairs instead, is
+probably possible, but I'm not sure if it's a good idea. I'd like Jeroen's
+opinion on this.
+-}
+
+class ChildrenTable (phi :: * -> *) top (ixs :: [*]) where
+  childrenTable :: Proxy '(phi,top,ixs) -> top -> HList (ChildTable phi top ixs)
+
+instance ChildrenTable phi top '[] where
+  childrenTable _ _ = HNil
+
+-- For the toplevel type, also include full tree
+instance (Fam phi, El phi top, Children phi (PF phi) top, ChildrenTable phi top t)
+         => ChildrenTable phi top (top ': t) where
+  childrenTable _ top = HCons ((Empty,top) : allChildren proof proof top)
+                        (childrenTable (Proxy :: Proxy '(phi,top,t)) top)
+
+-- For all other types
+instance (Fam phi, El phi h, El phi top, Children phi (PF phi) h, ChildrenTable phi top t)
+         => ChildrenTable phi top (h ': t) where
+  childrenTable _ top = HCons (allChildren proof proof top)
+                        (childrenTable (Proxy :: Proxy '(phi,top,t)) top)
+
+class GetChildrenTable phi (ixs :: [*]) ix where
+  getChTable :: Proxy '(phi, top, ixs)
+           -- I don't like this Proxy too much, but I'm afraid it's necessary
+           -> HList (ChildTable phi top ixs) -> MemCell Two phi top ix
+
+instance GetChildrenTable phi '[] ix where
+  getChTable _ HNil = error "This shouldn't happen"
+
+instance (Ord t) => GetChildrenTable phi (t ': ts) t where
+  getChTable _ (HCons mt _) = mt
+
+-- this is not the right memotable for this type, keep searching
+instance (GetChildrenTable phi ts ix) => GetChildrenTable phi (t ': ts) ix where
+  getChTable _ (HCons _ mts) = getChTable (Proxy :: Proxy '(phi, top, ts)) mts
+ Generics/MultiRec/Transformations/Path.hs view
@@ -0,0 +1,250 @@+{-# LANGUAGE TypeFamilies               #-}
+{-# LANGUAGE GADTs                      #-}
+{-# LANGUAGE RankNTypes                 #-}
+{-# LANGUAGE TypeOperators              #-}
+{-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE FlexibleInstances          #-}
+{-# LANGUAGE FlexibleContexts           #-}
+{-# LANGUAGE UndecidableInstances       #-}
+{-# LANGUAGE ScopedTypeVariables        #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+
+module Generics.MultiRec.Transformations.Path (
+    module Generics.MultiRec.Transformations.Path
+  , Ctxs(..), Ctx(..)
+  ) where
+
+import Prelude as P hiding ( mapM, sequence )
+import Generics.MultiRec hiding ( show, foldM )
+import Generics.MultiRec.Zipper ( Ctxs(..), Ctx(..) )
+import Control.Monad.State hiding ( foldM, mapM, sequence )
+import Data.Traversable ( Traversable, mapM, sequence )
+
+import Generics.MultiRec.CountIs
+
+--------------------------------------------------------------------------------
+-- Paths, annotations and edits
+--------------------------------------------------------------------------------
+-- | A path is a list of connecting directions on a datatype. This is equivalent
+-- to a zipper context where the recursive positions are ignored (set to a
+-- constant type).
+type Path  phi t i = Ctxs phi t (K0 ()) i
+
+-- | A direction points to a single recursive position in a datatype.
+type Dir f t i = Ctx f t (K0 ()) i
+
+-- | The type of pattern functors extended with references
+data WithRef phi top r a = InR { unInR :: PF phi r a }
+                         | Ref { unRef :: Path phi a top }
+
+-- | Closed functors extended with references
+type HWithRef phi top t = HFix (WithRef phi top) t
+
+-- | Insertions contain a path of where to insert, and what to insert
+data Insert phi top ix where
+  Insert :: phi t -> Path phi t ix -> HWithRef phi top t -> Insert phi top ix
+
+--------------------------------------------------------------------------------
+-- Path helpers
+--------------------------------------------------------------------------------
+
+-- | Concatenate two paths
+(<.>) :: forall phi a b c. Path phi b a -> Path phi c b -> Path phi c a
+Empty         <.> p2 = p2
+(Push p x xs) <.> p2 = Push p x (xs <.> p2)
+--------------------------------------------------------------------------------
+-- Show instances
+--------------------------------------------------------------------------------
+
+newtype ConIndex = CI Int deriving (Eq, Num)
+
+instance Show ConIndex where
+  show (CI (-1)) = ""
+  show (CI n   ) = "_" ++ show n ++ " "
+
+class ShowPath f where
+  showsPrecPath :: ShowS -> ConIndex -> Int -> Dir f i t -> ShowS
+
+instance (ShowPath f, ShowPath g) => ShowPath (f :+: g) where
+  showsPrecPath r d n (CL p) = showsPrecPath r d n p
+  showsPrecPath r d n (CR p) = showsPrecPath r d n p
+
+instance (ShowPath f, ShowPath g, CountIs f) => ShowPath (f :*: g) where
+  -- Going left on a product is unproblematic
+  showsPrecPath r d n (C1 p _) = showsPrecPath r d n p
+  -- Going right, however, we have to increase |d| by the number of children to
+  -- our left, unless |d == -1|, which means we are under a composition, and
+  -- shouldn't print any indices anymore.
+  showsPrecPath r d n (C2 _ p) =
+    let newd = if d == -1 then -1 else d + CI (countIs (undefined :: f r ix))
+    in showsPrecPath r newd n p
+
+instance (ShowPath f) => ShowPath (f :>: ix) where
+  showsPrecPath r d n (CTag p) = showsPrecPath r d n p
+
+instance (ShowPath f, Constructor c) => ShowPath (C c f) where
+  showsPrecPath r d n (CC p) = let name = conName (undefined :: C c f r ix)
+                               in showParen (n > 10) $ showString name
+                                                     . showsPrecPath r 0 11 p
+
+instance ShowPath (K a) where showsPrecPath _ _ _ _ = id
+instance ShowPath U     where showsPrecPath _ _ _ _ = id
+
+instance ShowPath (I ix) where
+  showsPrecPath r d n CId = shows d . r
+
+instance (ShowPath f) => ShowPath (Maybe :.: f) where
+  showsPrecPath r d n (CCM p) = shows d
+                              . showParen (n > 10)
+                                  ( showString "Maybe_0 "
+                                  . showsPrecPath r (-1) 11 p)
+
+instance (ShowPath f) => ShowPath ([] :.: f) where
+  showsPrecPath r d n (CCL l1 p l2) = shows d
+                                    . showParen (n > 10)
+                                        ( showString "List_"
+                                        . P.showsPrec 11 (length l1)
+                                        . showChar ' '
+                                        . showsPrecPath r (-1) 11 p)
+
+showsPrecPathC :: (ShowPath (PF phi))
+               => ConIndex -> Int -> Path phi t i -> ShowS
+showsPrecPathC d n Empty         = showString "End"
+showsPrecPathC d n (Push w p ps) = showsPrecPath (showsPrecPathC d n ps) d n p
+
+instance (ShowPath (PF phi)) => Show (Path phi t i) where
+  showsPrec = showsPrecPathC 0
+
+instance (HFunctor phi (PF phi), HShow phi (PF phi), El phi ix, ShowPath (PF phi))
+         => Show (HWithRef phi top ix) where
+  showsPrec = showWR proof
+
+instance (HFunctor phi (PF phi), HShow phi (PF phi), El phi ix, ShowPath (PF phi))
+         => Show (Insert phi top ix) where
+  showsPrec n (Insert w p r) = showParen (n > 10) $
+    showString "Insert " . spaces [P.showsPrec 11 p, showWR w 11 r]
+
+-- showsPrec for WithRef working with type index
+showWR :: forall phi top ix.
+          (HFunctor phi (PF phi), HShow phi (PF phi), ShowPath (PF phi))
+       => phi ix -> Int -> HWithRef phi top ix -> ShowS
+showWR w n (HIn (InR p)) = showParen (n > 10) $ spaces (("InR"++) : map ($ 11) x) where
+    f :: forall ix. phi ix -> HWithRef phi top ix -> K0 [Int -> ShowS] ix
+    f w wr = K0 [\n -> showWR w n wr]
+    r :: PF phi (K0 [Int -> ShowS]) ix
+    r = hmap f w p
+    x :: [Int -> ShowS]
+    x = hShowsPrecAlg w r
+showWR w n (HIn (Ref p)) = showParen (n > 10) $ showString "Ref " . P.showsPrec 11 p
+
+{-
+showsPrecPath :: forall phi f i t. ConIndex -> Int -> Dir phi f i t -> ShowS
+showsPrecPath d _ End        = showString "End"
+showsPrecPath d n (PlusL p)  = showsPrecPath d     n p
+showsPrecPath d n (PlusR p)  = showsPrecPath d     n p
+
+-- Going left on a product is unproblematic
+showsPrecPath d n (ProdL p)  = showsPrecPath d     n p
+
+-- Going right, however, we have to increase |d| by the number of children to
+-- our left, unless |d == -1|, which means we are under a composition, and
+-- shouldn't print any indices anymore.
+showsPrecPath d n (ProdR (p :: Dir phi g i t))  =
+  let newd = if d == -1 then -1 else d + CI (countIs (undefined :: g r ix))
+  in showsPrecPath newd n p
+
+showsPrecPath d n (TagP  p)  = showsPrecPath d     n p
+showsPrecPath d n (ConsP p)  = let name = conName (undefined :: f r i)
+                               in showParen (n > 10) $ showString name
+                                                     . showsPrecPath 0 11 p
+showsPrecPath d n (RecP _ p) = shows d . showsPrecPath d n p
+showsPrecPath d n (TrvI i p) = shows d
+                             . showParen (n > 10) (spaces
+                                 [ showString "TrvI"
+                                 , P.showsPrec 11 i
+                                 , showsPrecPath (-1) 11 p])
+
+instance Show (Dir phi f i t) where
+  showsPrec = showsPrecPath 0
+
+instance (HFunctor phi (PF phi), HShow phi (PF phi), El phi ix)
+         => Show (WithRef phi top ix) where
+  showsPrec = showWR proof
+
+instance (HFunctor phi (PF phi), HShow phi (PF phi), El phi ix)
+         => Show (Insert phi top ix) where
+  showsPrec n (Insert w p r) = showParen (n > 10) $
+    showString "Insert " . spaces [P.showsPrec 11 p, showWR w 11 r]
+
+-- showsPrec for WithRef working with type index
+showWR :: forall phi top ix. (HFunctor phi (PF phi), HShow phi (PF phi))
+       => phi ix -> Int -> WithRef phi top ix -> ShowS
+showWR w n (InR p) = showParen (n > 10) $ spaces (("InR"++) : map ($ 11) x) where
+    f :: forall ix. phi ix -> WithRef phi top ix -> K0 [Int -> ShowS] ix
+    f w wr = K0 [\n -> showWR w n wr]
+    r :: PF phi (K0 [Int -> ShowS]) ix
+    r = hmap f w p
+    x :: [Int -> ShowS]
+    x = hShowsPrecAlg w r
+showWR w n (Ref p) = showParen (n > 10) $ showString "Ref " . P.showsPrec 11 p
+-}
+
+--------------------------------------------------------------------------------
+-- MapP
+--------------------------------------------------------------------------------
+mapP :: forall m phi i t. (Monad m, Fam phi, MapP phi (PF phi))
+     => phi i -> Path phi t i -> (phi t -> t -> m t) -> i -> m i
+mapP w1 Empty         f = f w1
+mapP w1 (Push w2 y p) f =
+  liftM (to w1) . mapP' (\w -> liftM I0 . mapP w p f . unI0) w1 y . from w1
+
+class MapP phi f where
+  mapP' :: Monad m
+        => (phi t -> r t -> m (r t))
+        -> phi ix -> Dir f t ix -> f r ix -> m (f r ix)
+
+instance MapP phi U     where mapP' f phi p = return
+instance MapP phi (K a) where mapP' f phi p = return
+
+instance (El phi ix) => MapP phi (I ix) where
+  mapP' f phi CId (I x) = liftM I (f proof x)
+
+instance (MapP phi f, MapP phi g) => MapP phi (f :+: g) where
+  mapP' f phi (CL p) (L x) = liftM L (mapP' f phi p x)
+  mapP' f phi (CR p) (R x) = liftM R (mapP' f phi p x)
+  mapP' _ _   _      _     = fail "mapP': inconsistent sum"
+
+instance (MapP phi f, MapP phi g) => MapP phi (f :*: g) where
+  mapP' f phi (C1 p _) (x :*: y) = liftM2 (:*:) (mapP' f phi p x) (return y)
+  mapP' f phi (C2 _ p) (x :*: y) = liftM2 (:*:) (return x) (mapP' f phi p y)
+
+instance (MapP phi f) => MapP phi (C c f) where
+  mapP' f phi (CC p) (C x) = liftM C (mapP' f phi p x)
+
+instance (MapP phi f) => MapP phi (f :>: ix) where
+  mapP' f phi (CTag p) (Tag x) = liftM Tag (mapP' f phi p x)
+
+instance (MapP phi f) => MapP phi (Maybe :.: f) where
+  mapP' f phi (CCM p) = liftM D . sequence . liftM (mapP' f phi p) . unD
+
+instance (MapP phi f) => MapP phi ([] :.: f) where
+  mapP' f phi (CCL x p _) = liftM D . mapMwithI (\i ->
+                                                  if i == length x
+                                                  then mapP' f phi p
+                                                  else return) . unD
+
+
+mapPR :: forall phi top t a. (Fam phi, MapP phi (PF phi))
+         => phi a -> Path phi t a
+         -> (phi t -> HWithRef phi top t -> Maybe (HWithRef phi top t))
+         -> HWithRef phi top a -> Maybe (HWithRef phi top a)
+mapPR _  _             _  (HIn (Ref _)) = Nothing
+mapPR w1 Empty         f       a        = f w1 a
+mapPR w1 (Push w2 y p) f  (HIn (InR a)) =
+  liftM (HIn . InR) . mapP' (\w -> mapPR w p f) w1 y $ a
+
+mapMwithI :: (Monad m, Traversable t) => (Int -> a -> m b) -> t a -> m (t b)
+mapMwithI f ta = evalStateT (mapM g ta) 0 where
+  g a = do i <- get
+           put $ i+1
+           lift $ f i a
− Generics/MultiRec/Transformations/RewriteRules.hs
@@ -1,59 +0,0 @@-{-# LANGUAGE FlexibleContexts           #-}-{-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE TypeFamilies               #-}-{-# LANGUAGE UndecidableInstances       #-}-{-# LANGUAGE TemplateHaskell            #-}-{-# LANGUAGE ScopedTypeVariables        #-}-{-# LANGUAGE EmptyDataDecls             #-}-{-# LANGUAGE RankNTypes                 #-}-{-# LANGUAGE GADTs                      #-}--module Generics.MultiRec.Transformations.RewriteRules (-  Transformation, Transform, apply, insert, AnyInsert (..)-  ) where--import Generics.MultiRec hiding ( foldM )-import Generics.MultiRec.Rewriting-import Generics.MultiRec.Zipper (Zipper, Loc, leave, enter, update)--import Data.Maybe ( fromJust )-import Control.Monad ( (>=>), foldM )------------------------------------------------------------------------------------- Patch------------------------------------------------------------------------------------ Basically, a class synonym-class (Zipper phi (PF phi), Rewrite phi) => Transform phi-instance Transform phi => Rewrite phi---- An edit is a list of:-type Transformation phi a = [ AnyInsert phi a ]---- Existential for insertion-data AnyInsert phi a where -  AnyInsert ::-    -- Proof-    phi ix-    -- A path to the location to edit-    -> (Loc phi I0 a -> Maybe (Loc phi I0 a)) -    -- The rewrite rule to apply there-    -> Rule phi ix-    -> AnyInsert phi a--insert :: El phi ix => (Loc phi I0 a -> Maybe (Loc phi I0 a)) -> Rule phi ix-          -> AnyInsert phi a-insert = AnyInsert proof---- Patching is terribly simple: at the given locations, apply all the rules,--- then exit the zipper.-apply :: Transform phi => Transformation phi a -> phi a -> a -> Maybe a-apply rs p x = fmap leave $ foldM appRule (enter p x) rs-  where appRule a (AnyInsert p' l r) = l a >>=-                            updateM (\p'' -> case eqS p' p'' of-                                              Nothing   -> const Nothing-                                              Just Refl -> rewriteM r)--updateM :: (forall xi. phi xi -> xi -> Maybe xi)-        -> Loc phi I0 ix -> Maybe (Loc phi I0 ix)--- updateM f (Loc p (I0 x) s) = f p x >>= \y -> Loc p (I0 y) s-updateM f = Just . update (\p -> maybe (error "updateM") id . f p)
− Generics/MultiRec/Transformations/TH.hs
@@ -1,241 +0,0 @@-{-# LANGUAGE TemplateHaskell            #-}-{-# LANGUAGE FlexibleContexts           #-}-{-# LANGUAGE TypeFamilies               #-}-{-# LANGUAGE UndecidableInstances       #-}-{-# LANGUAGE GADTs                      #-}-{-# LANGUAGE MultiParamTypeClasses      #-}-{-# LANGUAGE PatternGuards              #-}--module Generics.MultiRec.Transformations.TH ( -  deriveRefRep, prefix, postfix-  ) where--import Generics.MultiRec hiding (show)-import Generics.MultiRec.TH-import Language.Haskell.TH hiding (Stmt ())-import Generics.MultiRec.Transformations.Explicit-import Control.Monad-import Control.Applicative-import Debug.Trace---- | Derive data type with references and 'HasRef' instance. For a data type---   N the name of the constructor for a reference is RefN, and the given---   function is used to change the rest of the constructors and the data---   type name itself. For example, for the following definition:------ > data Tree = Leaf Int | Bin Tree Tree--- > data TreeAST :: * -> * where--- >    Tree :: TreeAST Tree--- > $(deriveRefRep ''TreeAST (postfix "R"))------ The following data type is generated:------ > data TreeR = LeafR Int | BinR TreeR TreeR | RefTree Path--- > instance HasRef TreeAST-deriveRefRep :: Name -> (Name -> Name) -> Q [Dec]-deriveRefRep n namef =-  do-    info <- reify n-    let ps  = init (extractParameters info)-    let nps = map (\ (n, ps) -> (remakeName n, ps)) (extractConstructorNames ps info)-    let ns  = map fst nps-    d <- deriveDatas n namef ps ns-    r <- deriveHasRef n namef ps ns-    return $ d ++ r--prefix :: String -> Name -> Name-prefix pref n = mkName $ pref ++ nameBase n--postfix :: String -> Name -> Name-postfix post n = mkName $ nameBase n ++ post---- | Turn a record-constructor into a normal constructor by just--- removing all the field names.-stripRecordNames :: Con -> Con-stripRecordNames (RecC n f) =-  NormalC n (map (\(_, s, t) -> (s, t)) f)-stripRecordNames c = c--unApp :: Type -> [Type]-unApp (AppT f a) = unApp f ++ [a]-unApp t          = [t]---- | Process the reified info of the index GADT, and extract--- its constructor names, which are also the names of the datatypes--- that are part of the family.-extractConstructorNames :: [Name] -> Info -> [(Name, [Name])]-extractConstructorNames ps (TyConI (DataD _ _ _ cs _)) = concatMap extractFrom cs-  where-    extractFrom :: Con -> [(Name, [Name])]-    extractFrom (ForallC _ eqs c) = map (\ (n, _) -> (n, concatMap extractEq eqs)) (extractFrom c)-    extractFrom (InfixC _ n _)    = [(n, [])]-    extractFrom (RecC n _)        = [(n, [])]-    extractFrom (NormalC n [])    = [(n, [])]-    extractFrom _                 = []--    extractEq :: Pred -> [Name]-    extractEq (EqualP t1 t2) = filter (\ p -> p `elem` ps) (extractArgs t1 ++ extractArgs t2)-    extractEq _              = []--    extractArgs :: Type -> [Name]-    extractArgs (AppT x (VarT n)) = extractArgs x ++ [n]-    extractArgs (VarT n)          = [n]-    extractArgs _                 = []-extractConstructorNames _  _                           = []---- | Process the reified info of the index GADT, and extract--- its type parameters.-extractParameters :: Info -> [Name]-extractParameters (TyConI (DataD _ _ ns _ _)) = concatMap extractFromBndr ns-extractParameters (TyConI (TySynD _ ns _))    = concatMap extractFromBndr ns-extractParameters _                           = []--extractFromBndr :: TyVarBndr -> [Name]-extractFromBndr (PlainTV n)    = [n]-extractFromBndr (KindedTV n _) = [n]--deriveDatas :: Name -> (Name -> Name) -> [Name] -> [Name] -> Q [Dec]-deriveDatas s namef ps ns = zipWithM (deriveData s namef ps ns) [0..] ns--deriveData :: Name -> (Name -> Name) -> [Name] -> [Name] -> Int -> Name -> Q Dec-deriveData s namef ps ns i n = do-  let nm = namef n-  i <- reify n-  cons <- case i of-    TyConI (DataD _ _ _ cs _) -> mapM (mkCon n namef ns) cs-  r <- normalC (prefix "Ref" n) [return (NotStrict, ConT ''Path)]-  dataD (cxt []) nm (typeVariables i) (map return $ r : cons) []--mkCon :: Name -> (Name -> Name) -> [Name] -> Con -> Q Con-mkCon t namef ns (NormalC a b) = normalC (namef a) (map f b) where-  f :: (Strict, Type) -> Q (Strict, Type)-  f (s,t) = g t >>= return . (,) s-  g :: Type -> Q Type-  g (ConT n) | remakeName n `elem` ns = return (ConT $ namef n)-  g (AppT f a) = g a >>= return . AppT f-  g x          = return x--typeVariables :: Info -> [TyVarBndr]-typeVariables (TyConI (DataD    _ _ tv _ _)) = tv-typeVariables (TyConI (NewtypeD _ _ tv _ _)) = tv-typeVariables _                           = []--deriveHasRef :: Name -> (Name -> Name) -> [Name] -> [Name] -> Q [Dec]-deriveHasRef s namef ps ns =-  do-    let tyInsts = [tySynInstD ''RefRep [conT s, conT n] (conT $ namef n) | n <- ns]-    fcs <- liftM concat $ zipWithM (mkFrom ns namef (length ns)) [0..] ns-    tcs <- liftM concat $ zipWithM (mkTo   ns namef (length ns)) [0..] ns-    return <$>-      instanceD (cxt []) (conT ''HasRef `appT` (foldl appT (conT s) (map varT ps)))-        (tyInsts ++ [funD 'toRef tcs, funD 'fromRef fcs])--mkFrom :: [Name] -> (Name -> Name) -> Int -> Int -> Name -> Q [Q Clause]-mkFrom ns namef m i n = do-  let wrapE e = conE 'HIn `appE` (conE 'InR `appE` lrE m i (conE 'Tag `appE` e))-  i <- reify n-  let dn = remakeName n-  let r = clause [conP dn [], conP (prefix "Ref" dn) [varP (field 0)]]-               (normalB $ conE 'HIn `appE` (conE 'Ref `appE` varE (field 0))) []-  let b = case i of-            TyConI (DataD _ _ _ cs _) ->-               zipWith (fromCon wrapE ns dn namef (length cs)) [0..] cs-            TyConI (TySynD t _ _) ->-              [clause [conP dn [], varP (field 0)] (normalB (wrapE $ conE 'K `appE` varE (field 0))) []]-            _ -> error "unknown construct"-  return (r : b)--mkTo :: [Name] -> (Name -> Name) -> Int -> Int -> Name -> Q [Q Clause]-mkTo ns namef m i n = do-  let wrapP p = conP 'HIn [conP 'InR [lrP m i (conP 'Tag [p])]]-  i <- reify n-  let dn = remakeName n-  let r = clause [conP dn [], conP 'HIn [conP 'Ref [varP (field 0)]]] -               (normalB $ conE (prefix "Ref" dn) `appE` varE (field 0)) []-  let b = case i of-             TyConI (DataD _ _ _ cs _) ->-                  zipWith (toCon wrapP ns dn namef (length cs)) [0..] cs-             TyConI (TySynD t _ _) ->-                  [clause [conP dn [], wrapP $ conP 'K [varP (field 0)]] (normalB $ varE (field 0)) []]-             _ -> error "unknown construct"-  return (r : b)---fromCon :: (Q Exp -> Q Exp) -> [Name] -> Name -> (Name -> Name) -> Int -> Int -> Con -> Q Clause-fromCon wrap ns n namef m i (NormalC cn []) =-    clause-      [conP n [], conP (namef cn) []]-      (normalB $ wrap $ lrE m i $ conE 'C `appE` (conE 'U)) []-fromCon wrap ns n namef m i (NormalC cn fs) =-    -- runIO (putStrLn ("constructor " ++ show ix)) >>-    clause-      [conP n [], conP (namef cn) (map (varP . field) [0..length fs - 1])]-      (normalB $ wrap $ lrE m i $ conE 'C `appE` foldr1 prod (zipWith (fromField ns) [0..] (map snd fs))) []-  where-    prod x y = conE '(:*:) `appE` x `appE` y-fromCon wrap ns n namef m i r@(RecC _ _) =-  fromCon wrap ns n namef m i (stripRecordNames r)-fromCon wrap ns n namef m i (InfixC t1 cn t2) =-  fromCon wrap ns n namef m i (NormalC cn [t1,t2])-fromCon wrap ns n namef m i (ForallC _ _ c) =-  fromCon wrap ns n namef m i c--toCon :: (Q Pat -> Q Pat) -> [Name] -> Name -> (Name -> Name) -> Int -> Int -> Con -> Q Clause-toCon wrap ns n namef m i (NormalC cn []) =-    clause-      [conP n [], wrap $ lrP m i $ conP 'C [conP 'U []]]-      (normalB $ conE $ namef cn) []-toCon wrap ns n namef m i (NormalC cn fs) =-    -- runIO (putStrLn ("constructor " ++ show ix)) >>-    clause-      [conP n [], wrap $ lrP m i $ conP 'C [foldr1 prod (map (varP . field) [0..length fs - 1])]]-      (normalB $ foldl appE (conE $ namef cn) (zipWith (toField ns) [0..] (map snd fs))) []-  where-    prod x y = conP '(:*:) [x,y]-toCon wrap ns n namef m i r@(RecC _ _) =-  toCon wrap ns n namef m i (stripRecordNames r)-toCon wrap ns n namef m i (InfixC t1 cn t2) =-  toCon wrap ns n namef m i (NormalC cn [t1,t2])-toCon wrap ns n namef m i (ForallC _ _ c) =-  toCon wrap ns n namef m i c--fromField :: [Name] -> Int -> Type -> Q Exp-fromField ns nr t = [| $(fromFieldFun ns t) $(varE (field nr)) |]--fromFieldFun :: [Name] -> Type -> Q Exp-fromFieldFun ns t@(ConT n)-  | remakeName n `elem` ns   = [| I . fromRef $(conE $ remakeName n) |]-fromFieldFun ns t-  | ConT n : a <- unApp t, remakeName n `elem` ns-                             = [| I . fromRef $(conE $ remakeName n) |]-fromFieldFun ns t@(AppT f a) = [| D . fmap $(fromFieldFun ns a) |]-fromFieldFun ns t            = [| K |]--toField :: [Name] -> Int -> Type -> Q Exp-toField ns nr t = [| $(toFieldFun ns t) $(varE (field nr)) |]--toFieldFun :: [Name] -> Type -> Q Exp-toFieldFun ns t@(ConT n)-  | remakeName n `elem` ns = [| toRef $(conE $ remakeName n) . unI |]-toFieldFun ns t-  | ConT n : a <- unApp t, remakeName n `elem` ns-                           = [| toRef $(conE $ remakeName n) . unI |]-toFieldFun ns t@(AppT f a) = [| fmap $(toFieldFun ns a) . unD |]-toFieldFun ns t            = [| unK |]--field :: Int -> Name-field n = mkName $ "f" ++ show n--lrP :: Int -> Int -> (Q Pat -> Q Pat)-lrP 1 0 p = p-lrP m 0 p = conP 'L [p]-lrP m i p = conP 'R [lrP (m-1) (i-1) p]--lrE :: Int -> Int -> (Q Exp -> Q Exp)-lrE 1 0 e = e-lrE m 0 e = conE 'L `appE` e-lrE m i e = conE 'R `appE` lrE (m-1) (i-1) e---- Should we, under certain circumstances, maintain the module name?-remakeName :: Name -> Name-remakeName n = mkName (nameBase n)
+ Generics/MultiRec/Transformations/ZipChildren.hs view
@@ -0,0 +1,94 @@+{-# LANGUAGE TypeFamilies               #-}
+{-# LANGUAGE GADTs                      #-}
+{-# LANGUAGE RankNTypes                 #-}
+{-# LANGUAGE TypeOperators              #-}
+{-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE FlexibleInstances          #-}
+{-# LANGUAGE FlexibleContexts           #-}
+{-# LANGUAGE UndecidableInstances       #-}
+{-# LANGUAGE ScopedTypeVariables        #-}
+
+module Generics.MultiRec.Transformations.ZipChildren where
+
+import Generics.MultiRec hiding ( show, foldM )
+import Control.Monad.State hiding ( foldM, mapM )
+import Data.Foldable ( toList )
+
+import Generics.MultiRec.CountIs
+import Generics.MultiRec.Transformations.Path
+import Generics.MultiRec.Transformations.Children
+import Generics.MultiRec.Transformations.MemoTable
+
+--------------------------------------------------------------------------------
+-- Zip immediate children
+--------------------------------------------------------------------------------
+
+zipChildrenM :: (Monad m, Fam phi, ZipChildren phi (PF phi))
+             => phi ix
+             -> (forall t. (Lookup phi (Ixs phi) t, Children phi (PF phi) t, GetChildrenTable phi (Ixs phi) t,  Eq t) => phi t -> Path phi t ix -> t -> t -> m a)
+             -> ix
+             -> ix
+             -> m [a]
+{-
+zipChildrenM p f a b = zipChildren p (\p w (I0 l) (I0 r) -> f p w l r)
+                         (\z -> Push p z Empty)
+                         (from p a) (from p b)
+-}
+zipChildrenM p f a b = zipChildren p (\p w (I0 l) (I0 r) -> f p w l r)
+                         (\z -> Push (error "oops") z Empty)
+                         (from p a) (from p b)
+
+class ZipChildren phi (f :: (* -> *) -> * -> *) where
+  zipChildren :: (Monad m)
+              => phi ix
+              -> (forall t. (Lookup phi (Ixs phi) t, Children phi (PF phi) t, GetChildrenTable phi (Ixs phi) t, Eq t)
+                  => phi t -> Path phi t ix -> r t -> r t -> m a)
+              -> (forall t. Dir f t ix -> Path phi t ix)
+              -> f r ix
+              -> f r ix
+              -> m [a]
+
+instance ( Lookup phi (Ixs phi) xi, El phi xi, Children phi (PF phi) xi
+         , GetChildrenTable phi (Ixs phi) xi, Eq xi) => ZipChildren phi (I xi) where
+  zipChildren _ f w (I l) (I r) = f proof (w CId) l r >>= \x -> return [x]
+
+instance ZipChildren phi (K a) where
+  zipChildren _ _ _ _ _ = return []
+
+instance ZipChildren phi U where
+  zipChildren _ _ _ _ _ = return []
+
+instance (ZipChildren phi f, ZipChildren phi g) => ZipChildren phi (f :+: g) where
+  zipChildren p f w (L l) (L r) = zipChildren p f (w . CL) l r
+  zipChildren p f w (R l) (R r) = zipChildren p f (w . CR) l r
+
+instance (ZipChildren phi f, ZipChildren phi g, CountIs g)
+    => ZipChildren phi (f :*: g) where
+  zipChildren p f w (l1 :*: l2) (r1 :*: r2) =
+    liftM2 (++) (zipChildren p f (\z -> w (C1 z nullY)) l1 r1)
+                (zipChildren p f (\z -> w (C2 nullX z)) l2 r2)
+      where nullX = error "nullX" -- fmap (const ()) x
+            nullY = error "nullY" -- fmap (const ()) y
+
+instance (ZipChildren phi f, Constructor c) => ZipChildren phi (C c f) where
+  zipChildren p f w (C l) (C r) = zipChildren p f (w . CC) l r
+
+instance ZipChildren phi f => ZipChildren phi (f :>: ix) where
+  zipChildren p f w (Tag l) (Tag r) = zipChildren p f (w . CTag) l r
+{-
+instance (Traversable t, ZipChildren phi f) => ZipChildren phi (t :.: f) where
+  zipChildren p f w (D l) (D r) = liftM concat $ sequence $
+                                  zipWith3 (\i -> zipChildren p f (w . TrvI i))
+                                  [0..] (toList l) (toList r)
+-}
+instance (ZipChildren phi f) => ZipChildren phi (Maybe :.: f) where
+  zipChildren p f w (D l) (D r) = liftM concat $ sequence $
+                                  zipWith3 (\i -> zipChildren p f (w . CCM))
+                                  [0..] (toList l) (toList r)
+
+instance (ZipChildren phi f) => ZipChildren phi ([] :.: f) where
+  zipChildren p f w (D l) (D r) = liftM concat $ sequence $
+                                  zipWith3 (\i -> zipChildren p f (\x -> w (CCL (ll i) x lr)))
+                                  [0..] l r
+    where ll i = replicate i (error "oops3")
+          lr = error "oops2"
− Generics/MultiRec/Transformations/ZipperState.hs
@@ -1,72 +0,0 @@-{-# LANGUAGE RankNTypes                 #-}-{-# LANGUAGE FlexibleContexts           #-}-{-# LANGUAGE GADTs                      #-}--module Generics.MultiRec.Transformations.ZipperState (-  ZipperMonad, ZipperState, upMonad, downMonad, leftMonad, rightMonad, -  navigate, saveMonad, loadMonad, topMonad, updateMonad-  ) where--import Control.Monad-import Control.Monad.State--import Generics.MultiRec-import Generics.MultiRec.Zipper-import Generics.MultiRec.Any------------------------------------------------------------------------------------- A zipper with state-----------------------------------------------------------------------------------type ZipperState phi r a = ([Any phi], Loc phi r a)-type ZipperMonad phi r a b = StateT (ZipperState phi r a) Maybe b--enterMonad :: (El phi a, Fam phi, Zipper phi (PF phi))-           => a -> ZipperMonad phi I0 a (Any phi)-enterMonad x = put ([], enter proof x) >> return (Any proof x)--moveMonad :: (EqS phi, El phi a)-          => (Loc phi I0 a -> Maybe (Loc phi I0 a))-          -> ZipperMonad phi I0 a (Any phi)-moveMonad d = StateT (\(s,l) -> do l' <- d l-                                   let a = on (\p (I0 x) -> Any p x) l'-                                   return (a, (s,l')))--upMonad, downMonad, leftMonad, rightMonad :: (EqS phi, El phi a)-                                          => ZipperMonad phi I0 a (Any phi)-upMonad    = moveMonad up-downMonad  = moveMonad down-leftMonad  = moveMonad left-rightMonad = moveMonad right--updateMonad :: (EqS phi, El phi a)-            => (forall xi. phi xi -> xi -> Maybe xi) -               -> ZipperMonad phi I0 a (Any phi)-updateMonad f = do (s,l) <- get-                   let l' = update (\p -> maybe (error "updateMonad") id . f p) l-                       a  = on (\p (I0 x) -> Any p x) l'-                   put (s,l')-                   return a-saveMonad :: (EqS phi, El phi a) => ZipperMonad phi I0 a (Any phi)-saveMonad = do (s,l) <- get-               let a = on (\p (I0 x) -> Any p x) l-               put (s++[a],l)-               return a--loadMonad :: (EqS phi, El phi a) => ZipperMonad phi I0 a (Any phi)-loadMonad = do (s:ss,l) <- get-               let l' = update (\p x -> maybe x id (matchAny p s)) l-               put (ss,l')-               return s--topMonad :: (EqS phi, El phi a) => ZipperMonad phi I0 a (Any phi)-topMonad = moveMonad goUp where-  goUp l = maybe (Just l) goUp (up l)--leaveMonad :: (EqS phi, El phi a) -              => Loc phi I0 a -> ZipperMonad phi I0 a b -> Maybe a-leaveMonad s m = maybe Nothing (matchAny proof) $ evalStateT (m >> topMonad) ([],s)--navigate :: (Fam phi, EqS phi, El phi a, Zipper phi (PF phi))-            => phi a -> a -> ZipperMonad phi I0 a b -> Maybe a-navigate p x = leaveMonad (enter p x)
+ Generics/MultiRec/Zipper.hs view
@@ -0,0 +1,220 @@+{-# LANGUAGE FlexibleContexts      #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE GADTs                 #-}
+{-# LANGUAGE KindSignatures        #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE RankNTypes            #-}
+{-# LANGUAGE TypeFamilies          #-}
+{-# LANGUAGE TypeOperators         #-}
+{-# LANGUAGE EmptyDataDecls        #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Generics.MultiRec.Zipper
+-- Copyright   :  (c) 2008--2009 Universiteit Utrecht
+-- License     :  BSD3
+--
+-- Maintainer  :  generics@haskell.org
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+--
+-- The generic zipper.
+--
+-----------------------------------------------------------------------------
+
+module Generics.MultiRec.Zipper where
+
+import Prelude hiding (last)
+
+import Control.Monad
+import Control.Applicative
+import Data.Traversable
+
+import Generics.MultiRec.Base
+import Generics.MultiRec.HFunctor
+
+-- * Locations and context stacks
+
+-- | Abstract type of locations. A location contains the current focus
+-- and its context. A location is parameterized over the family of
+-- datatypes and over the type of the complete value.
+
+data Loc :: (* -> *) -> (* -> *) -> * -> * where
+  Loc :: (Fam phi, Zipper phi (PF phi))
+      => phi ix -> r ix -> Ctxs phi ix r a -> Loc phi r a
+
+data Ctxs :: (* -> *) -> * -> (* -> *) -> * -> * where
+  Empty :: Ctxs phi a r a
+  -- Push  :: phi ix -> Ctx (PF phi) b r ix -> Ctxs phi ix r a -> Ctxs phi b r a
+  Push  :: phi a -> Ctx (PF phi) a r ix -> Ctxs phi b  r a -> Ctxs phi b r ix
+
+-- * Context frames
+
+-- | Abstract type of context frames. Not required for the high-level
+-- navigation functions.
+
+data family Ctx (f :: (* -> *) -> * -> *) :: * -> (* -> *) -> * -> *
+
+data instance Ctx (K a) b r ix
+data instance Ctx U b r ix
+data instance Ctx (f :+: g) b r ix  = CL (Ctx f b r ix)
+                                    | CR (Ctx g b r ix)
+data instance Ctx (f :*: g) b r ix  = C1 (Ctx f b r ix) (g r ix)
+                                    | C2 (f r ix) (Ctx g b r ix)
+
+data instance Ctx ([]    :.: g) b r ix = CCL [g r ix] (Ctx g b r ix) [g r ix]
+data instance Ctx (Maybe :.: g) b r ix = CCM (Ctx g b r ix)
+
+
+data instance Ctx (I xi) b r ix     where CId :: Ctx (I xi) xi r ix
+data instance Ctx (f :>: xi) b r ix where
+  CTag :: Ctx f b r ix -> Ctx (f :>: ix) b r ix
+data instance Ctx (C c f) b r ix    = CC (Ctx f b r ix)
+
+-- * Contexts and locations are functors
+
+instance Zipper phi f => HFunctor phi (Ctx f b) where
+  hmapA = cmapA
+
+instance Zipper phi (PF phi) => HFunctor phi (Ctxs phi b) where
+  hmapA f p' Empty        = pure Empty
+  -- hmapA f p' (Push p c s) = liftA2 (Push p) (hmapA f p c) (hmapA f p' s)
+  hmapA f p' (Push p c s) = liftA2 (Push p) (hmapA f p' c) (hmapA f p s)
+
+instance HFunctor phi (Loc phi) where
+  hmapA f p' (Loc p x s)  = liftA2 (Loc p) (f p x) (hmapA f p' s)
+
+-- * Generic navigation functions
+
+-- | It is in general not necessary to use the generic navigation
+-- functions directly. The functions listed in the ``Interface'' section
+-- below are more user-friendly.
+--
+
+class HFunctor phi f => Zipper phi f where
+  cmapA       :: Applicative a => (forall ix. phi ix -> r ix -> a (r' ix)) ->
+                 phi ix -> Ctx f b r ix -> a (Ctx f b r' ix)
+  fill        :: phi b -> Ctx f b r ix -> r b -> f r ix
+  first, last :: (forall b. phi b -> r b -> Ctx f b r ix -> a)
+              -> f r ix -> Maybe a
+  next, prev  :: (forall b. phi b -> r b -> Ctx f b r ix -> a)
+              -> phi b -> Ctx f b r ix -> r b -> Maybe a
+
+instance El phi xi => Zipper phi (I xi) where
+  cmapA f p CId   = pure CId
+  fill    p CId x = I x
+  first f (I x)   = return (f proof x CId)
+  last  f (I x)   = return (f proof x CId)
+  next  f p CId x = Nothing
+  prev  f p CId x = Nothing
+
+instance Zipper phi (K a) where
+  cmapA f p void   = impossible void
+  fill    p void x = impossible void
+  first f (K a)    = Nothing
+  last  f (K a)    = Nothing
+  next  f p void x = impossible void
+  prev  f p void x = impossible void
+
+instance Zipper phi U where
+  cmapA f p void   = impossible void
+  fill    p void x = impossible void
+  first f U        = Nothing
+  last  f U        = Nothing
+  next  f p void x = impossible void
+  prev  f p void x = impossible void
+
+instance (Zipper phi f, Zipper phi g) => Zipper phi (f :+: g) where
+  cmapA f p (CL c)   = liftA CL (cmapA f p c)
+  cmapA f p (CR c)   = liftA CR (cmapA f p c)
+  fill    p (CL c) x = L (fill p c x)
+  fill    p (CR c) y = R (fill p c y)
+  first f (L x)      = first (\p z -> f p z . CL) x
+  first f (R y)      = first (\p z -> f p z . CR) y
+  last  f (L x)      = last  (\p z -> f p z . CL) x
+  last  f (R y)      = last  (\p z -> f p z . CR) y
+  next  f p (CL c) x = next  (\p z -> f p z . CL) p c x
+  next  f p (CR c) y = next  (\p z -> f p z . CR) p c y
+  prev  f p (CL c) x = prev  (\p z -> f p z . CL) p c x
+  prev  f p (CR c) y = prev  (\p z -> f p z . CR) p c y
+
+instance (Zipper phi f, Zipper phi g) => Zipper phi (f :*: g) where
+  cmapA f p (C1 c y)   = liftA2 C1 (cmapA f p c) (hmapA f p y)
+  cmapA f p (C2 x c)   = liftA2 C2 (hmapA f p x) (cmapA f p c)
+  fill    p (C1 c y) x = fill p c x :*: y
+  fill    p (C2 x c) y = x :*: fill p c y
+  first f (x :*: y)                =
+                first (\p z c  -> f p z (C1 c          y ))   x `mplus`
+                first (\p z c  -> f p z (C2 x          c ))   y
+  last  f (x :*: y)                 =
+                last  (\p z c  -> f p z (C2 x          c ))   y `mplus`
+                last  (\p z c  -> f p z (C1 c          y ))   x
+  next  f p (C1 c y) x =
+                next  (\p' z c' -> f p' z (C1 c'           y )) p c x `mplus`
+                first (\p' z c' -> f p' z (C2 (fill p c x) c'))     y
+  next  f p (C2 x c) y =
+                next  (\p' z c' -> f p' z (C2 x            c')) p c y
+  prev  f p (C1 c y) x =
+                prev  (\p' z c' -> f p' z (C1 c'           y )) p c x
+  prev  f p (C2 x c) y =
+                prev  (\p' z c' -> f p' z (C2 x            c')) p c y `mplus`
+                last  (\p' z c' -> f p' z (C1 c' (fill p c y)))     x
+
+-- For the time being, we support just [] and Maybe. I think we
+-- might be able to support a whole class (Foldable).
+instance (Zipper phi g) => Zipper phi ([] :.: g) where
+  cmapA f p (CCL pb c pe)   =
+    CCL <$> traverse (hmapA f p) pb <*> cmapA f p c <*> traverse (hmapA f p) pe
+  fill    p (CCL pb c pe) x =
+    D (reverse pb ++ fill p c x : pe)
+  first f (D [])            = Nothing
+  first f (D (x : xs))      = first (\p z c -> f p z (CCL [] c xs)) x
+  last  f (D xs)            =
+    case reverse xs of
+      []     -> Nothing
+      y : ys -> last (\p z c -> f p z (CCL ys c [])) y
+  next  f p (CCL pb c pe) x =
+    next (\p z c -> f p z (CCL pb c pe)) p c x `mplus`
+    case pe of
+      []     -> Nothing
+      y : ys -> first (\p' z c' -> f p' z (CCL (fill p c x : pb) c' ys)) y
+  prev  f p (CCL pb c pe) x =
+    prev (\p z c -> f p z (CCL pb c pe)) p c x `mplus`
+    case pb of
+      []     -> Nothing
+      y : ys -> last  (\p' z c' -> f p' z (CCL ys c' (fill p c x : pe))) y
+
+instance (Zipper phi g) => Zipper phi (Maybe :.: g) where
+  cmapA f p (CCM c)    =
+    CCM <$> cmapA f p c
+  fill p (CCM c) x     =
+    D (Just (fill p c x))
+  first f (D Nothing)  = Nothing
+  first f (D (Just x)) = first (\p z -> f p z . CCM) x
+  last  f (D Nothing)  = Nothing
+  last  f (D (Just x)) = last  (\p z -> f p z . CCM) x
+  next  f p (CCM c) x  = next  (\p z -> f p z . CCM) p c x
+  prev  f p (CCM c) x  = prev  (\p z -> f p z . CCM) p c x
+
+instance Zipper phi f => Zipper phi (f :>: xi) where
+  cmapA f p (CTag c)   = liftA CTag (cmapA f p c)
+  fill    p (CTag c) x = Tag (fill p c x)
+  first f (Tag x)      = first (\p z -> f p z . CTag)      x
+  last  f (Tag x)      = last  (\p z -> f p z . CTag)      x
+  next  f p (CTag c) x = next  (\p z -> f p z . CTag)  p c x
+  prev  f p (CTag c) x = prev  (\p z -> f p z . CTag)  p c x
+
+instance (Constructor c, Zipper phi f) => Zipper phi (C c f) where
+  cmapA f p (CC c)   = liftA CC (cmapA f p c)
+  fill    p (CC c) x = C (fill p c x)
+  first f (C x)      = first (\p z -> f p z . CC)     x
+  last  f (C x)      = last  (\p z -> f p z . CC)     x
+  next  f p (CC c) x = next  (\p z -> f p z . CC) p c x
+  prev  f p (CC c) x = prev  (\p z -> f p z . CC) p c x
+
+
+-- * Internal functions
+
+impossible :: a -> b
+impossible x = error "impossible"
Generics/Regular/Functions/GOrd.hs view
@@ -1,41 +1,41 @@-{-# LANGUAGE TypeOperators              #-}-{-# LANGUAGE FlexibleContexts           #-}--module Generics.Regular.Functions.GOrd where--import Generics.Regular-import Data.Monoid (mappend)------------------------------------------------------------------------------------- Generic Ord-----------------------------------------------------------------------------------class GOrd f where-  comparef :: (a -> a -> Ordering) -> f a -> f a -> Ordering--instance GOrd I where-  comparef f (I x) (I y) = f x y--instance Ord a => GOrd (K a) where-  comparef _ (K x) (K y) = compare x y--instance GOrd U where-  comparef _ U U = EQ--instance (GOrd f, GOrd g) => GOrd (f :+: g) where-  comparef _ (L _) (R _) = LT-  comparef _ (R _) (L _) = GT-  comparef f (L x) (L y) = comparef f x y-  comparef f (R x) (R y) = comparef f x y--instance (GOrd f, GOrd g) => GOrd (f :*: g) where-  comparef f (x1 :*: y1) (x2 :*: y2) = comparef f x1 x2 `mappend` comparef f y1 y2--instance GOrd f => GOrd (C c f) where-  comparef f (C x) (C y) = comparef f x y--instance GOrd f => GOrd (S s f) where-  comparef f (S x) (S y) = comparef f x y--gcompare :: (Regular a, GOrd (PF a)) => a -> a -> Ordering-gcompare x y = comparef gcompare (from x) (from y)+{-# LANGUAGE TypeOperators              #-}
+{-# LANGUAGE FlexibleContexts           #-}
+
+module Generics.Regular.Functions.GOrd where
+
+import Generics.Regular
+import Data.Monoid (mappend)
+
+--------------------------------------------------------------------------------
+-- Generic Ord
+--------------------------------------------------------------------------------
+
+class GOrd f where
+  comparef :: (a -> a -> Ordering) -> f a -> f a -> Ordering
+
+instance GOrd I where
+  comparef f (I x) (I y) = f x y
+
+instance Ord a => GOrd (K a) where
+  comparef _ (K x) (K y) = compare x y
+
+instance GOrd U where
+  comparef _ U U = EQ
+
+instance (GOrd f, GOrd g) => GOrd (f :+: g) where
+  comparef _ (L _) (R _) = LT
+  comparef _ (R _) (L _) = GT
+  comparef f (L x) (L y) = comparef f x y
+  comparef f (R x) (R y) = comparef f x y
+
+instance (GOrd f, GOrd g) => GOrd (f :*: g) where
+  comparef f (x1 :*: y1) (x2 :*: y2) = comparef f x1 x2 `mappend` comparef f y1 y2
+
+instance GOrd f => GOrd (C c f) where
+  comparef f (C x) (C y) = comparef f x y
+
+instance GOrd f => GOrd (S s f) where
+  comparef f (S x) (S y) = comparef f x y
+
+gcompare :: (Regular a, GOrd (PF a)) => a -> a -> Ordering
+gcompare x y = comparef gcompare (from x) (from y)
− Generics/Regular/Transformations/Explicit.hs
@@ -1,357 +0,0 @@-{-# LANGUAGE FlexibleContexts           #-}-{-# LANGUAGE GADTs                      #-}-{-# LANGUAGE TypeOperators              #-}-{-# LANGUAGE ScopedTypeVariables        #-}-{-# LANGUAGE UndecidableInstances       #-}-{-# LANGUAGE TypeFamilies               #-}--module Generics.Regular.Transformations.Explicit (-  diff, apply, Transformation, WithRef (..), Path, Transform, -  HasRef (..), NiceTransformation, toNiceTransformation, fromNiceTransformation-  ) where--import Generics.Regular-import Generics.Regular.Functions.GOrd-import Control.Applicative ( (<|>) )-import Control.Monad (foldM, liftM, liftM2)-import Control.Monad.State-import Data.Monoid (mappend)-import qualified Data.Map as Map-import Data.Map (Map)-import qualified Generics.Regular.Functions.Eq as GEq------------------------------------------------------------------------------------- Paths, annotations and edits----------------------------------------------------------------------------------type Path             = [Int]-data WithRef a b      = InR (PF a b)-                      | Ref Path--instance Functor (PF a) => Functor (WithRef a) where-  fmap f (InR x) = InR (fmap f x)-  fmap _ (Ref p) = Ref p--type Transformation a = [ (Path, Fix (WithRef a)) ]--class (Regular a, Children (PF a), CountI (PF a), Functor (PF a),-       SEq (PF a), ExtractN (PF a), MapN (PF a), GMap (PF a), GOrd (PF a),-       GEq.Eq (PF a)) => Transform a------------------------------------------------------------------------------------- Patching------------------------------------------------------------------------------------- | Apply the edits to the given tree-apply :: Transform a => Transformation a -> a -> Maybe a-apply e t = foldM apply' t e where-  apply' _ ([],   c) = lookupRefs t c-  apply' a (i:is, c) = fmap to . tmapN f . from $ a where-    f j x | i == j     = apply' x (is,c)-          | otherwise  = Just x---- | Look up the references using the original structure-lookupRefs :: Transform a => a -> Fix (WithRef a) -> Maybe a-lookupRefs r (In (InR a)) = fmap to (fmapM (lookupRefs r) a)-lookupRefs r (In (Ref p)) = extract p r---- | Extract the subtree at the given path-extract :: Transform a => Path -> a -> Maybe a-extract p a = foldM (\x i -> extractN i $ from x) a p------------------------------------------------------------------------------------- Diffing----------------------------------------------------------------------------------data MemoKey a where-  MemoKey :: Bool -> a -> a -> MemoKey a--instance (Regular a, GEq.Eq (PF a)) => Eq (MemoKey a) where-  (MemoKey a1 b1 c1) == (MemoKey a2 b2 c2) =-    a1 == a2 && GEq.eq b1 b2 && GEq.eq c1 c2--instance (Regular a, GEq.Eq (PF a), GOrd (PF a)) => Ord (MemoKey a) where-  compare (MemoKey a1 b1 c1) (MemoKey a2 b2 c2) =-    compare a1 a2 `mappend` gcompare b1 b2 `mappend` gcompare c1 c2--type Memo a = Map (MemoKey a) (Transformation a)---- | Find a set of edits to transform the first into the second tree-diff :: forall a. (Transform a) => a -> a -> Transformation a-diff a b = evalState (build False a b) Map.empty-  where-    childPaths :: [(a,Path)]-    childPaths = childrenPaths a-    buildmem :: Bool -> a -> a -> State (Memo a) (Transformation a)-    buildmem a b c = do-      mp <- get-      let k = MemoKey a b c-      case Map.lookup k mp of-        Just r  -> return r-        Nothing -> do-          r <- build a b c-          modify (Map.insert k r)-          return r-    build :: Bool -> a -> a -> State (Memo a) (Transformation a)-    build False a' b' | GEq.eq a' b' = return []-    build ins a' b' = case lookupWith GEq.eq b' childPaths of-      Just p  -> return [([], In (Ref p))]-      Nothing -> uses >>= maybe insert return-        where-          -- Construct the edits for the children based on a root-          construct :: Bool -> a -> State (Memo a) (Maybe (Transformation a))-          construct ins' c =-            if shallowEq (from c) (from b')-            then do r <- zipWithM (buildmem ins') (imChildren c) (imChildren b')-                    return $ Just $ concat $ updateChildPaths r-            else return Nothing-          -- Possible edits reusing the existing tree or using a part of-          -- the original tree. The existing tree is only used if we didn't-          -- just insert it, since we want to keep the inserts small-          uses :: State (Memo a) (Maybe (Transformation a))-          uses = reuses >>= \re -> case re of-              Just r | ins -> return re-              _            -> construct ins a' >>= return . best re-          -- Possible edits that include reusing a part of the original tree-          reuses :: State (Memo a) (Maybe (Transformation a))-          reuses = foldM f Nothing childPaths where-            addRef p = fmap (([], In (Ref p)):)-            f c (x,p) = construct False x >>= return . best c . addRef p-          -- Best edit including insertion, only chosen if nothing can be reused-          insert :: State (Memo a) (Transformation a)-          insert = do-            Just r <- construct True b'-            let (r', e') = partialApply (withRefs b') r-            return $ ([], r') : e'---- | Helper function for lookup with provided compare function-lookupWith :: (a -> a -> Bool) -> a -> [(a,b)] -> Maybe b-lookupWith _ _ [] = Nothing-lookupWith f a ((b,r):bs)-  | f a b     = Just r-  | otherwise = lookupWith f a bs---- | Pick the best edit-best :: Maybe (Transformation a) -> Maybe (Transformation a) -> Maybe (Transformation a)-best e1 e2 = case (e1,e2) of-  (Just e1', Just e2') -> Just (pickShortest e1' e2')-  _                    -> e1 <|> e2---- | Pick the shortest of two lists lazily-pickShortest :: [a] -> [a] -> [a]-pickShortest a b = if f a b then a else b-  where f []     _      = True-        f _      []     = False-        f (_:xs) (_:ys) = f xs ys---- | Lift a tree to a tree with references-withRefs :: Transform a => a -> Fix (WithRef a)-withRefs = In . InR . fmap withRefs . from---- | Try to apply as much edits to the edit structure as possible---   to make the final edit smaller-partialApply :: Transform a =>-                Fix (WithRef a) -> Transformation a -> (Fix (WithRef a), Transformation a)-partialApply a [] = (a, [])-partialApply a ((p,r):xs) = case replace p r a of-  Just a' -> partialApply a' xs-  Nothing -> let (a',xs') = partialApply a xs in (a', (p,r) : xs')---- | Replace a subtree in an edit structure-replace :: (Transform a, Monad m)-           => Path -> Fix (WithRef a) -> Fix (WithRef a) -> m (Fix (WithRef a))-replace []     r _ = return r-replace (i:is) r a = case a of-  In (Ref _) -> fail "Replace"-  In (InR a') -> tmapN f a' >>= return . In . InR-    where f j = if i == j then replace is r else return---- | Extend the paths of edits for the children with the child number-updateChildPaths :: [Transformation a] -> [Transformation a]-updateChildPaths = zipWith (\n -> map (\(p,c) -> (n:p,c))) [0..]------------------------------------------------------------------------------------- Shallow equality-----------------------------------------------------------------------------------class SEq f where-  shallowEq :: f a -> f a -> Bool--instance SEq I where-  shallowEq (I _) (I _) = True--instance SEq U where-  shallowEq U U = True--instance Eq a => SEq (K a) where-  shallowEq (K a) (K b) = a == b--instance (SEq f, SEq g) => SEq (f :+: g) where-  shallowEq (L a) (L b) = shallowEq a b-  shallowEq (R a) (R b) = shallowEq a b-  shallowEq _     _     = False--instance (SEq f, SEq g) => SEq (f :*: g) where-  shallowEq (a :*: b) (c :*: d) = shallowEq a c && shallowEq b d--instance SEq f => SEq (C c f) where-  shallowEq (C a) (C b) = shallowEq a b--instance SEq f => SEq (S s f) where-  shallowEq (S a) (S b) = shallowEq a b------------------------------------------------------------------------------------- ExtractN-----------------------------------------------------------------------------------class ExtractN f where-  extractN :: Monad m => Int -> f a -> m a--instance ExtractN I where-  extractN 0 (I r) = return r-  extractN _ (I _) = fail "extractN"--instance ExtractN (K a) where-  extractN _ (K _) = fail "extractN"--instance ExtractN U where-  extractN _ U = fail "extractN"--instance (ExtractN f, ExtractN g) => ExtractN (f :+: g) where-  extractN i (L x) = extractN i x-  extractN i (R x) = extractN i x---- Here we decrement our parameter. Does not require right-nested products-instance (CountI f, ExtractN f, ExtractN g) => ExtractN (f :*: g) where-  extractN i (x :*: y) = let n = countI x-                          in if i < n then extractN i     x-                                      else extractN (i-n) y--instance ExtractN f => ExtractN (C c f) where-  extractN i (C x) = extractN i x--instance ExtractN f => ExtractN (S s f) where-  extractN i (S x) = extractN i x------------------------------------------------------------------------------------- MapN------------------------------------------------------------------------------------- | Map a function with child index at a top-level structure-tmapN :: (Monad m, MapN f) => (Int -> a -> m b) -> f a -> m (f b)-tmapN = mapN 0--class MapN f where-  mapN :: Monad m => Int -> (Int -> a -> m b) -> f a -> m (f b)--instance MapN I where-  mapN i f (I r) = liftM I (f i r)--instance MapN (K a) where-  mapN _ _ (K x)  = liftM K (return x)--instance MapN U where-  mapN _ _ U = return U--instance (MapN f, MapN g) => MapN (f :+: g) where-  mapN i f (L x) = liftM L (mapN i f x)-  mapN i f (R x) = liftM R (mapN i f x)---- Here we increment our parameter. Does not require right-nested products-instance (CountI f, MapN f, MapN g) => MapN (f :*: g) where-  mapN i f (x :*: y) = liftM2 (:*:) (mapN i f x) (mapN (i + countI x) f y)--instance MapN f => MapN (C c f) where-  mapN i f (C x) = liftM C (mapN i f x)--instance MapN f => MapN (S s f) where-  mapN i f (S x) = liftM S (mapN i f x)-------------------------------------------------------------------------------------- CountI-----------------------------------------------------------------------------------class CountI f where-  -- | Count the number of recursive occurrences-  countI :: f a -> Int--instance CountI I where-  countI _ = 1--instance CountI (K a) where-  countI _ = 0--instance CountI U where-  countI _ = 0--instance (CountI f, CountI g) => CountI (f :+: g) where-  countI (L x) = countI x-  countI (R x) = countI x--instance (CountI f, CountI g) => CountI (f :*: g) where-  countI (x :*: y) = countI x + countI y--instance CountI f => CountI (C c f) where-  countI (C x) = countI x--instance CountI f => CountI (S s f) where-  countI (S x) = countI x------------------------------------------------------------------------------------- Children------------------------------------------------------------------------------------- | Get the immediate children-imChildren :: (Regular a, Children (PF a)) => a -> [a]-imChildren = children . from---- | Get all children with their paths-childrenPaths :: (Regular a, Children (PF a)) => a -> [(a,Path)]-childrenPaths a = (a, []) : [ (r, n : p)-                            | (n, c) <- zip [0..] (imChildren a)-                            , (r, p) <- childrenPaths c ]--class Children f where-  children :: f a -> [a]--instance Children I where-  children (I r) = [r]--instance Children (K a) where-  children (K _) = []--instance Children U where-  children U = []--instance (Children f, Children g) => Children (f :+: g) where-  children (L x) = children x-  children (R x) = children x--instance (Children f, Children g) => Children (f :*: g) where-  children (x :*: y) = children x ++ children y--instance Children f => Children (C c f) where-  children (C x) = children x--instance Children f => Children (S s f) where-  children (S x) = children x------------------------------------------------------------------------------------- Nicer interface----------------------------------------------------------------------------------class HasRef a where-  type RefRep a-  -  toRef   :: WithRef a (RefRep a) -> RefRep a-  fromRef :: RefRep a -> WithRef a (RefRep a)--type NiceTransformation a = [ (Path, RefRep a) ] --toNiceTransformation :: (Functor (PF a), HasRef a) -                        => Transformation a -> NiceTransformation a-toNiceTransformation = map (\(p,e) -> (p, tr e)) where-  tr = toRef . fmap tr . out--fromNiceTransformation :: (Functor (PF a), HasRef a) -                          => NiceTransformation a -> Transformation a-fromNiceTransformation = map (\(p,e) -> (p, fr e)) where-  fr = In . fmap fr . fromRef
+ Generics/Regular/Transformations/Main.hs view
@@ -0,0 +1,451 @@+{-# LANGUAGE FlexibleContexts           #-}
+{-# LANGUAGE FlexibleInstances          #-}
+{-# LANGUAGE GADTs                      #-}
+{-# LANGUAGE TypeOperators              #-}
+{-# LANGUAGE ScopedTypeVariables        #-}
+{-# LANGUAGE UndecidableInstances       #-}
+{-# LANGUAGE TypeFamilies               #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE TypeSynonymInstances       #-}
+{-# LANGUAGE OverlappingInstances       #-}
+
+module Generics.Regular.Transformations.Main
+  ( diff, apply
+  , Transformation, WithRef (..), Path (..), Transform, HasRef (..)
+  , NiceTransformation, toNiceTransformation, fromNiceTransformation
+  ) where
+
+import Prelude as P
+import Generics.Regular
+import Generics.Regular.Functions.Show hiding ( show, shows, Show )
+import qualified Generics.Regular.Functions.Show as R
+import Generics.Regular.Zipper
+import Generics.Regular.Functions.GOrd
+import Control.Applicative ( (<|>) )
+import Control.Monad (foldM, liftM, liftM2)
+import Control.Monad.State
+import Data.Monoid (mappend)
+import qualified Data.Map as Map
+import Data.Map (Map)
+import qualified Generics.Regular.Functions.Eq as GEq
+
+--------------------------------------------------------------------------------
+-- Paths, annotations and edits
+--------------------------------------------------------------------------------
+type Path  a = [Dir (PF a)]
+type Dir f = Ctx f ()
+
+data WithRef a b = InR (PF a b)
+                 | Ref (Path a)
+
+instance Functor (PF a) => Functor (WithRef a) where
+  fmap f (InR x) = InR (fmap f x)
+  fmap _ (Ref p) = Ref p -- ?
+
+type Transformation a = [ (Path a, Fix (WithRef a)) ]
+
+class (Regular a, Children (PF a), Functor (PF a), ZipChildren (PF a),
+       SEq (PF a), ExtractP (PF a), MapP (PF a), GMap (PF a), GOrd (PF a),
+       GEq.Eq (PF a)) => Transform a
+
+--------------------------------------------------------------------------------
+-- Showing paths
+--------------------------------------------------------------------------------
+newtype ConIndex = CI Int deriving (Eq, Num)
+
+instance Show ConIndex where
+  show (CI (-1)) = ""
+  show (CI n   ) = "_" ++ show n ++ " "
+
+class ShowPath f where
+  showsPrecPath :: ShowS -> ConIndex -> Int -> Dir f -> ShowS
+
+instance (ShowPath f, ShowPath g) => ShowPath (f :+: g) where
+  showsPrecPath r d n (CL p) = showsPrecPath r d n p
+  showsPrecPath r d n (CR p) = showsPrecPath r d n p
+
+instance (ShowPath f, ShowPath g, CountIs g) => ShowPath (f :*: g) where
+  -- Going left on a product is unproblematic
+  showsPrecPath r d n (C1 p _) = showsPrecPath r d n p
+  -- Going right, however, we have to increase |d| by the number of children to
+  -- our left
+  showsPrecPath r d n (C2 _ (p :: Ctx g ())) =
+    let newd = d + CI (countIs (undefined :: g r))
+    in showsPrecPath r newd n p
+
+instance (ShowPath f, Constructor c) => ShowPath (C c f) where
+  showsPrecPath r d n (CC p) = let name = conName (undefined :: C c f r)
+                               in showParen (n > 10) $ showString name
+                                                     . showsPrecPath r 0 11 p
+
+instance ShowPath (K a) where showsPrecPath _ _ _ _ = id
+instance ShowPath U     where showsPrecPath _ _ _ _ = id
+
+instance ShowPath I where
+  showsPrecPath r d n CId = shows d . r
+
+showsPrecPathC :: (ShowPath f) => ConIndex -> Int -> [Dir f] -> ShowS
+showsPrecPathC d n []     = showString "End"
+showsPrecPathC d n (p:ps) = showsPrecPath (showsPrecPathC d n ps) d n p
+
+instance (ShowPath f) => Show [Dir f] where
+  showsPrec = showsPrecPathC 0
+
+instance (ShowPath (PF a), Functor (PF a), R.Show (PF a))
+    => Show (Fix (WithRef a)) where
+  showsPrec n (In (Ref p)) = showParen (n > 10)
+                           $ showString "Ref " . showsPrec 11 p
+  showsPrec n (In (InR x)) = showParen (n > 10)
+                           $ showString "InR " . R.hshowsPrec showsPrec False 11 x
+
+spaces :: [ShowS] -> ShowS
+spaces = intersperse " "
+
+intersperse :: String -> [ShowS] -> ShowS
+intersperse s []     = id
+intersperse s [x]    = x
+intersperse s (x:xs) = x . (s ++) . spaces xs
+
+class CountIs f where
+  countIs :: f r -> Int
+
+instance CountIs I     where countIs _ = 1
+instance CountIs U     where countIs _ = 0
+instance CountIs (K a) where countIs _ = 0
+
+instance (CountIs f) => CountIs (C c f) where
+  countIs (C x) = countIs x
+
+instance (CountIs f, CountIs g) => CountIs (f :+: g) where
+  countIs (L x) = countIs x
+  countIs (R x) = countIs x
+
+instance (CountIs f, CountIs g) => CountIs (f :*: g) where
+  countIs (x :*: y) = countIs x + countIs y
+
+--------------------------------------------------------------------------------
+-- Patching
+--------------------------------------------------------------------------------
+
+-- | Apply the edits to the given tree
+apply :: Transform a => Transformation a -> a -> Maybe a
+apply e t = foldM apply' t e where
+  apply' a (p, c) = mapP (flip lookupRefs c) p a
+
+-- | Look up the references using the original structure
+lookupRefs :: Transform a => a -> Fix (WithRef a) -> Maybe a
+lookupRefs r (In (InR a)) = fmap to (fmapM (lookupRefs r) a)
+lookupRefs r (In (Ref p)) = extract p r
+
+--------------------------------------------------------------------------------
+-- Diffing
+--------------------------------------------------------------------------------
+data MemoKey a where
+  MemoKey :: Bool -> a -> a -> MemoKey a
+
+instance (Regular a, GEq.Eq (PF a)) => Eq (MemoKey a) where
+  (MemoKey a1 b1 c1) == (MemoKey a2 b2 c2) =
+    a1 == a2 && GEq.eq b1 b2 && GEq.eq c1 c2
+
+instance (Regular a, GEq.Eq (PF a), GOrd (PF a)) => Ord (MemoKey a) where
+  compare (MemoKey a1 b1 c1) (MemoKey a2 b2 c2) =
+    compare a1 a2 `mappend` gcompare b1 b2 `mappend` gcompare c1 c2
+
+type Memo a = Map (MemoKey a) (Transformation a)
+
+-- | Find a set of edits to transform the first into the second tree
+diff :: forall a. (Transform a) => a -> a -> Transformation a
+diff a b = evalState (build False a b) Map.empty
+  where
+    childPaths :: [(a,Path a)]
+    childPaths = childrenPaths a
+    buildmem :: Bool -> a -> a -> State (Memo a) (Transformation a)
+    buildmem a b c = do
+      mp <- get
+      let k = MemoKey a b c
+      case Map.lookup k mp of
+        Just r  -> return r
+        Nothing -> do
+          r <- build a b c
+          modify (Map.insert k r)
+          return r
+    build :: Bool -> a -> a -> State (Memo a) (Transformation a)
+    build False a' b' | GEq.eq a' b' = return []
+    build ins a' b' = case lookupWith GEq.eq b' childPaths of
+      Just p  -> return [([], In (Ref p))]
+      Nothing -> uses >>= maybe insert return
+        where
+          -- Construct the edits for the children based on a root
+          construct :: Bool -> a -> State (Memo a) (Maybe (Transformation a))
+          construct ins' c =
+            if shallowEq (from c) (from b')
+            then do r <- zipChildrenM (\p c1 c2 -> buildmem ins' c1 c2 >>=
+                                                   return . updateChildPaths p) c b'
+                    return $ Just $ concat r
+            else return Nothing
+          -- Possible edits reusing the existing tree or using a part of
+          -- the original tree. The existing tree is only used if we didn't
+          -- just insert it, since we want to keep the inserts small
+          uses :: State (Memo a) (Maybe (Transformation a))
+          uses = reuses >>= \re -> case re of
+              Just r | ins -> return re
+              _            -> construct ins a' >>= return . best re
+          -- Possible edits that include reusing a part of the original tree
+          reuses :: State (Memo a) (Maybe (Transformation a))
+          reuses = foldM f Nothing childPaths where
+            addRef p = fmap (([], In (Ref p)):)
+            f c (x,p) = construct False x >>= return . best c . addRef p
+          -- Best edit including insertion, only chosen if nothing can be reused
+          insert :: State (Memo a) (Transformation a)
+          insert = do
+            Just r <- construct True b'
+            let (r', e') = partialApply (withRefs b') r
+            return $ ([], r') : e'
+
+-- | Helper function for lookup with provided compare function
+lookupWith :: (a -> a -> Bool) -> a -> [(a,b)] -> Maybe b
+lookupWith _ _ [] = Nothing
+lookupWith f a ((b,r):bs)
+  | f a b     = Just r
+  | otherwise = lookupWith f a bs
+
+-- | Pick the best edit
+best :: Maybe (Transformation a) -> Maybe (Transformation a) -> Maybe (Transformation a)
+best e1 e2 = case (e1,e2) of
+  (Just e1', Just e2') -> Just (pickShortest e1' e2')
+  _                    -> e1 <|> e2
+
+-- | Pick the shortest of two lists lazily
+pickShortest :: [a] -> [a] -> [a]
+pickShortest a b = if f a b then a else b
+  where f []     _      = True
+        f _      []     = False
+        f (_:xs) (_:ys) = f xs ys
+
+-- | Lift a tree to a tree with references
+withRefs :: Transform a => a -> Fix (WithRef a)
+withRefs = In . InR . fmap withRefs . from
+
+-- | Try to apply as much edits to the edit structure as possible
+--   to make the final edit smaller
+partialApply :: Transform a =>
+                Fix (WithRef a) -> Transformation a -> (Fix (WithRef a), Transformation a)
+partialApply a [] = (a, [])
+partialApply a ((p,r):xs) = case replace p r a of
+  Just a' -> partialApply a' xs
+  Nothing -> let (a',xs') = partialApply a xs in (a', (p,r) : xs')
+
+-- | Replace a subtree in an edit structure
+replace :: (Transform a, Monad m)
+           => Path a -> Fix (WithRef a) -> Fix (WithRef a) -> m (Fix (WithRef a))
+replace p r a = mapPR (const (return r)) p a
+
+-- | Extend the paths of edits for the children with the child number
+updateChildPaths :: Path a -> Transformation a -> Transformation a
+updateChildPaths p = map (\(p2,c) -> (p ++ p2,c))
+--------------------------------------------------------------------------------
+-- Shallow equality
+--------------------------------------------------------------------------------
+
+class SEq f where
+  shallowEq :: f a -> f a -> Bool
+
+instance SEq I where
+  shallowEq (I _) (I _) = True
+
+instance SEq U where
+  shallowEq U U = True
+
+instance Eq a => SEq (K a) where
+  shallowEq (K a) (K b) = a == b
+
+instance (SEq f, SEq g) => SEq (f :+: g) where
+  shallowEq (L a) (L b) = shallowEq a b
+  shallowEq (R a) (R b) = shallowEq a b
+  shallowEq _     _     = False
+
+instance (SEq f, SEq g) => SEq (f :*: g) where
+  shallowEq (a :*: b) (c :*: d) = shallowEq a c && shallowEq b d
+
+instance SEq f => SEq (C c f) where
+  shallowEq (C a) (C b) = shallowEq a b
+
+instance SEq f => SEq (S s f) where
+  shallowEq (S a) (S b) = shallowEq a b
+
+--------------------------------------------------------------------------------
+-- Extract
+--------------------------------------------------------------------------------
+-- | Extract the subtree at the given path
+extract :: (Transform a, Monad m) => Path a -> a -> m a
+extract []     = return
+extract (p:ps) = extractP (extract ps) p . from
+
+class ExtractP f where
+  extractP :: Monad m => (a -> m a) -> Dir f -> f a -> m a
+
+instance ExtractP I where
+  extractP f CId (I r) = f r
+
+instance ExtractP (K a) where
+  extractP _ _ (K _) = fail "extractP"
+
+instance ExtractP U where
+  extractP _ _ U = fail "extractP"
+
+instance (ExtractP f, ExtractP g) => ExtractP (f :+: g) where
+  extractP f (CL p) (L x) = extractP f p x
+  extractP f (CR p) (R x) = extractP f p x
+  extractP _ _      _     = fail "extractP"
+
+instance (ExtractP f, ExtractP g) => ExtractP (f :*: g) where
+  extractP f (C1 p _) (x :*: _) = extractP f p x
+  extractP f (C2 _ p) (_ :*: y) = extractP f p y
+
+instance ExtractP f => ExtractP (C c f) where
+  extractP f (CC p) (C x) = extractP f p x
+
+instance ExtractP f => ExtractP (S s f) where
+  extractP f (CS p) (S x) = extractP f p x
+
+--------------------------------------------------------------------------------
+-- MapP
+--------------------------------------------------------------------------------
+-- | Map a function over the child in a specific path
+
+mapP :: (MapP (PF a), Monad m, Regular a) => (a -> m a) -> Path a -> a -> m a
+mapP f []     = f
+mapP f (p:ps) = liftM to . mapP' (mapP f ps) p . from
+
+-- | Version of |mapP| for trees with references
+mapPR :: (Transform a, Monad m) =>
+         (Fix (WithRef a) -> m (Fix (WithRef a)))
+         -> Path a -> Fix (WithRef a) -> m (Fix (WithRef a))
+mapPR f p (In (Ref _)) = fail "mapPR"
+mapPR f []     x            = f x
+mapPR f (p:ps) (In (InR r)) = mapP' (mapPR f ps) p r >>= return . In . InR
+
+class MapP f where
+  mapP' :: Monad m => (b -> m b) -> Dir f -> f b -> m (f b)
+
+instance MapP I where
+  mapP' f CId (I r) = liftM I (f r)
+
+instance MapP (K a) where
+  mapP' _ _ (K x)  = liftM K (return x)
+
+instance MapP U where
+  mapP' _ _ U = return U
+
+instance (MapP f, MapP g) => MapP (f :+: g) where
+  mapP' f (CL p) (L x) = liftM L (mapP' f p x)
+  mapP' f (CR p) (R x) = liftM R (mapP' f p x)
+
+instance (MapP f, MapP g) => MapP (f :*: g) where
+  mapP' f (C1 p _) (x :*: y) = liftM2 (:*:) (mapP' f p x) (return y)
+  mapP' f (C2 _ p) (x :*: y) = liftM2 (:*:) (return x) (mapP' f p y)
+
+instance MapP f => MapP (C c f) where
+  mapP' f (CC p) (C x) = liftM C (mapP' f p x)
+
+instance MapP f => MapP (S s f) where
+  mapP' f (CS p) (S x) = liftM S (mapP' f p x)
+
+--------------------------------------------------------------------------------
+-- Children
+--------------------------------------------------------------------------------
+-- | Get the immediate children
+imChildren :: (Regular a, Children (PF a)) => a -> [a]
+imChildren = map fst . children . from
+
+-- | Get all children with their paths
+childrenPaths :: (Regular a, Children (PF a)) => a -> [(a,Path a)]
+childrenPaths a = (a,[]) : [ (r, n : p)
+                           | (c, n) <- children (from a)
+                           , (r, p) <- childrenPaths c ]
+
+class Children f where
+  children :: f a -> [(a, Dir f)]
+
+instance Children I where
+  children (I r) = [(r, CId)]
+
+instance Children (K a) where
+  children (K _) = []
+
+instance Children U where
+  children U = []
+
+instance (Children f, Children g) => Children (f :+: g) where
+  children (L x) = [ (a, CL p) | (a,p) <- children x ]
+  children (R x) = [ (a, CR p) | (a,p) <- children x ]
+
+instance (Children f, Children g) => Children (f :*: g) where
+  children (x :*: y) = [ (a, C1 p nullY) | (a,p) <- children x ]
+                       ++ [ (a, C2 nullX p) | (a,p) <- children y ]
+    where nullX = error "nullX" -- fmap (const ()) x
+          nullY = error "nullY" -- fmap (const ()) y
+-- The errors above should be safe, because we're never inspecting those anyway
+
+instance Children f => Children (C c f) where
+  children (C x) = [ (a, CC p) | (a,p) <- children x ]
+
+instance Children f => Children (S s f) where
+  children (S x) = [ (a, CS p) | (a,p) <- children x ]
+
+--------------------------------------------------------------------------------
+-- ZipChildren
+--------------------------------------------------------------------------------
+zipChildrenM :: (Transform a, Monad m) => (Path a -> a -> a -> m b) -> a -> a -> m [b]
+zipChildrenM f a b = zipChildren f (:[]) (from a) (from b)
+
+class ZipChildren f where
+  zipChildren :: Monad m => (Path a -> a -> a -> m b) -> (Dir f -> Path a) -> f a -> f a -> m [b]
+
+instance ZipChildren I where
+  zipChildren f p (I a) (I b) = f (p CId) a b >>= \x -> return [x]
+
+instance ZipChildren (K a) where
+  zipChildren _ _ _ _ = return []
+
+instance ZipChildren U where
+  zipChildren _ _ _ _ = return []
+
+instance (ZipChildren f, ZipChildren g) => ZipChildren (f :+: g) where
+  zipChildren f p (L x) (L y) = zipChildren f (p . CL) x y
+  zipChildren f p (R x) (R y) = zipChildren f (p . CR) x y
+
+instance (ZipChildren f, ZipChildren g) => ZipChildren (f :*: g) where
+  zipChildren f p (x1 :*: y1) (x2 :*: y2) =
+    liftM2 (++) (zipChildren f (\x -> p $ C1 x nullY) x1 x2)
+                (zipChildren f (\x -> p $ C2 nullX x) y1 y2)
+      where nullX = error "nullX" -- fmap (const ()) x
+            nullY = error "nullY" -- fmap (const ()) y
+-- The errors above should be safe, because we're never inspecting those anyway
+
+instance ZipChildren f => ZipChildren (C c f) where
+  zipChildren f p (C x) (C y) = zipChildren f (p . CC) x y
+
+instance ZipChildren f => ZipChildren (S s f) where
+  zipChildren f p (S x) (S y) = zipChildren f (p . CS) x y
+
+--------------------------------------------------------------------------------
+-- Nicer interface
+--------------------------------------------------------------------------------
+class HasRef a where
+  type RefRep a
+
+  toRef   :: WithRef a (RefRep a) -> RefRep a
+  fromRef :: RefRep a -> WithRef a (RefRep a)
+
+type NiceTransformation a = [ (Path a, RefRep a) ]
+
+toNiceTransformation :: (Functor (PF a), HasRef a)
+                        => Transformation a -> NiceTransformation a
+toNiceTransformation = map (\(p,e) -> (p, tr e)) where
+  tr = toRef . fmap tr . out
+
+fromNiceTransformation :: (Functor (PF a), HasRef a)
+                          => NiceTransformation a -> Transformation a
+fromNiceTransformation = map (\(p,e) -> (p, fr e)) where
+  fr = In . fmap fr . fromRef
− Generics/Regular/Transformations/RewriteRules.hs
@@ -1,31 +0,0 @@-{-# LANGUAGE FlexibleContexts           #-}-{-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE UndecidableInstances       #-}-{-# LANGUAGE ScopedTypeVariables        #-}--module Generics.Regular.Transformations.RewriteRules (-  Transform, Transformation, apply-  ) where--import Generics.Regular-import Generics.Regular.Rewriting-import Generics.Regular.Zipper--import Control.Monad ( foldM )------------------------------------------------------------------------------------- Patch------------------------------------------------------------------------------------ Basically, a class synonym-class (Regular a, Rewrite a, Zipper (PF a)) => Transform a-instance Transform a => Rewrite a---- An edit is a list of:-type Transformation a = [ ( Loc a -> Maybe (Loc a) -- A path to the location to edit-                          , Rule a) ]              -- The rewrite rule to apply there---- Patching is terribly simple: at the given locations, apply all the rules,--- then exit the zipper.-apply :: forall a. (Transform a) => Transformation a -> a -> Maybe a-apply rs = fmap leave . flip (foldM appRule) rs . enter-  where appRule a (l,r) = l a >>= updateM (rewriteM r)
− Generics/Regular/Transformations/TH.hs
@@ -1,191 +0,0 @@-{-# LANGUAGE TemplateHaskell            #-}-{-# LANGUAGE FlexibleContexts           #-}-{-# LANGUAGE TypeFamilies               #-}-{-# LANGUAGE UndecidableInstances       #-}--module Generics.Regular.Transformations.TH ( -  deriveRefRep, prefix, postfix-  ) where--import Generics.Regular-import Language.Haskell.TH-import Generics.Regular.Transformations.Explicit---- Code taken from regular library and adapted to work with transformations---- | Derive data type with references and 'HasRef' instance. For a data type---   N the name of the constructor for a reference is RefN, and the given---   function is used to change the rest of the constructors and the data---   type name itself. For example, for the following definition:------ > data Tree = Leaf Int | Bin Tree Tree--- > $(deriveRefRep ''Tree (postfix "R"))------ The following data type is generated:------ > data TreeR = LeafR Int | BinR TreeR TreeR | RefTree Path--- > instance HasRef Tree-deriveRefRep :: Name -> (Name -> Name) -> Q [Dec]-deriveRefRep t namef = do-  d <- deriveData t namef-  ins <- deriveInst t namef-  return [d,ins]--deriveData :: Name -> (Name -> Name) -> Q Dec-deriveData t namef = do-  let nm = namef t-  i <- reify t-  cons <- case i of-    TyConI (DataD _ _ _ cs _) -> mapM (mkCon t namef nm) cs-  r <- normalC (prefix "Ref" t) [return (NotStrict, ConT ''Path)]-  dataD (cxt []) nm (typeVariables i) (map return $ r : cons) []--mkCon :: Name -> (Name -> Name) -> Name -> Con -> Q Con-mkCon t namef repname (NormalC a b) = normalC (namef a) (map f b) where-  f :: (Strict, Type) -> Q (Strict, Type)-  f (s,t') | t' == ConT t = return (s, ConT repname)-           | otherwise    = return (s, t')--prefix :: String -> Name -> Name-prefix pref n = mkName $ pref ++ nameBase n--postfix :: String -> Name -> Name-postfix post n = mkName $ nameBase n ++ post--deriveInst :: Name -> (Name -> Name) -> Q Dec-deriveInst t namef =-  do-    i <- reify t-    let typ = foldl (\a -> AppT a . VarT . tyVarBndrToName) (ConT t) (typeVariables i)-    let rn = prefix "Ref" t-    fcs <- mkFrom t 1 0 namef rn t-    tcs <- mkTo   t 1 0 namef rn t-    let typ' = return $ foldl (\a -> AppT a . VarT . tyVarBndrToName) (ConT $ namef t) (typeVariables i)      -    instanceD (cxt []) (conT ''HasRef `appT` return typ)-        [tySynInstD ''RefRep [return typ] typ', funD 'toRef tcs, funD 'fromRef fcs]--lrE :: Int -> Int -> (Q Exp -> Q Exp)-lrE 1 0 e = e-lrE m 0 e = conE 'L `appE` e-lrE m i e = conE 'R `appE` lrE (m-1) (i-1) e--tyVarBndrToName :: TyVarBndr -> Name-tyVarBndrToName (PlainTV  name)   = name-tyVarBndrToName (KindedTV name _) = name--typeVariables :: Info -> [TyVarBndr]-typeVariables (TyConI (DataD    _ _ tv _ _)) = tv-typeVariables (TyConI (NewtypeD _ _ tv _ _)) = tv-typeVariables _                           = []--mkFrom :: Name -> Int -> Int -> (Name -> Name) -> Name -> Name -> Q [Q Clause]-mkFrom ns m i namef refname n = do-  let wrapE e = conE 'InR `appE` lrE m i e-  i <- reify n-  let r = clause [conP refname [varP $ field 0]] (normalB $ conE 'Ref `appE` varE (field 0)) []-  let b = case i of-        TyConI (DataD _ dt vs cs _) ->-          zipWith (fromCon wrapE ns namef (dt, map tyVarBndrToName vs) (length cs)) [0..] cs-        TyConI (NewtypeD _ dt vs c _) ->-          [fromCon wrapE ns namef (dt, map tyVarBndrToName vs) 1 0 c]-        TyConI (TySynD t _ _) ->-          [clause [varP (field 0)] (normalB (wrapE $ conE 'K `appE` varE (field 0))) []]-        _ -> error "unknown construct"-  return $ r : b--mkTo :: Name -> Int -> Int -> (Name -> Name) -> Name -> Name -> Q [Q Clause]-mkTo ns m i namef refname n = do-  let wrapP p = conP 'InR [lrP m i p]-  i <- reify n-  let r = clause [conP 'Ref [varP $ field 0]] (normalB $ conE refname `appE` varE (field 0)) []-  let b = case i of-                TyConI (DataD _ dt vs cs _) ->-                  zipWith (toCon wrapP ns namef (dt, map tyVarBndrToName vs) (length cs)) [0..] cs-                TyConI (NewtypeD _ dt vs c _) ->-                  [toCon wrapP ns namef (dt, map tyVarBndrToName vs) 1 0 c]-                TyConI (TySynD t _ _) ->-                  [clause [wrapP $ conP 'K [varP (field 0)]] (normalB $ varE (field 0)) []]-                _ -> error "unknown construct" -  return $ r : b--fromCon :: (Q Exp -> Q Exp) -> Name -> (Name -> Name) -> (Name, [Name]) -> Int -> Int -> Con -> Q Clause-fromCon wrap ns namef (dt, vs) m i (NormalC cn []) =-    clause-      [conP (namef cn) []]-      (normalB $ wrap $ lrE m i $ conE 'C `appE` (conE 'U)) []-fromCon wrap ns namef (dt, vs) m i (NormalC cn fs) =-    clause-      [conP (namef cn) (map (varP . field) [0..length fs - 1])]-      (normalB $ wrap $ lrE m i $ conE 'C `appE` foldr1 prod (zipWith (fromField (dt, vs)) [0..] (map snd fs))) []-  where-    prod x y = conE '(:*:) `appE` x `appE` y-fromCon wrap ns namef (dt, vs) m i r@(RecC cn []) =-    clause-      [conP (namef cn) []]-      (normalB $ wrap $ lrE m i $ conE 'C `appE` (conE 'U)) []-fromCon wrap ns namef (dt, vs) m i r@(RecC cn fs) =-    clause-      [conP (namef cn) (map (varP . field) [0..length fs - 1])]-      (normalB $ wrap $ lrE m i $ conE 'C `appE` foldr1 prod (zipWith (fromField' (dt, vs)) [0..] fs)) []-  where-    prod x y = conE '(:*:) `appE` x `appE` y-fromCon wrap ns namef (dt, vs) m i (InfixC t1 cn t2) =-  fromCon wrap ns namef (dt, vs) m i (NormalC cn [t1,t2])--fromField :: (Name, [Name]) -> Int -> Type -> Q Exp-fromField (dt, vs) nr t | t == dataDeclToType (dt, vs) = -  conE 'I `appE` varE (field nr)-fromField (dt, vs) nr t                                = -  conE 'K `appE` varE (field nr)--fromField' :: (Name, [Name]) -> Int -> (Name, Strict, Type) -> Q Exp-fromField' (dt, vs) nr (_, _, t) | t == dataDeclToType (dt, vs) =-  conE 'S `appE` (conE 'I `appE` varE (field nr))-fromField' (dt, vs) nr (_, _, t)                                =-  conE 'S `appE` (conE 'K `appE` varE (field nr))--toCon :: (Q Pat -> Q Pat) -> Name -> (Name -> Name) -> (Name, [Name]) -> Int -> Int -> Con -> Q Clause-toCon wrap ns namef (dt, vs) m i (NormalC cn []) =-    clause-      [wrap $ lrP m i $ conP 'C [conP 'U []]]-      (normalB $ conE $ namef cn) []-toCon wrap ns namef (dt, vs) m i (NormalC cn fs) =-    -- runIO (putStrLn ("constructor " ++ show ix)) >>-    clause-      [wrap $ lrP m i $ conP 'C [foldr1 prod (zipWith (toField (dt, vs)) [0..] (map snd fs))]]-      (normalB $ foldl appE (conE $ namef cn) (map (varE . field) [0..length fs - 1])) []-  where-    prod x y = conP '(:*:) [x,y]-toCon wrap ns namef (dt, vs) m i r@(RecC cn []) =-    clause-      [wrap $ lrP m i $ conP 'C [conP 'U []]]-      (normalB $ conE $ namef cn) []-toCon wrap ns namef (dt, vs) m i r@(RecC cn fs) =-    clause-      [wrap $ lrP m i $ conP 'C [foldr1 prod (zipWith (toField' (dt, vs)) [0..] fs)]]-      (normalB $ foldl appE (conE $ namef cn) (map (varE . field) [0..length fs - 1])) []-  where-    prod x y = conP '(:*:) [x,y]-toCon wrap ns namef (dt, vs) m i (InfixC t1 cn t2) =-  toCon wrap ns namef (dt, vs) m i (NormalC cn [t1,t2])--toField :: (Name, [Name]) -> Int -> Type -> Q Pat-toField (dt, vs) nr t | t == dataDeclToType (dt, vs) = -  conP 'I [varP (field nr)]-toField (dt, vs) nr t                                = -  conP 'K [varP (field nr)]--toField' :: (Name, [Name]) -> Int -> (Name, Strict, Type) -> Q Pat-toField' (dt, vs) nr (_, _, t) | t == dataDeclToType (dt, vs) = conP 'S [conP 'I [varP (field nr)]]-toField' (dt, vs) nr (_, _, t)                                = conP 'S [conP 'K [varP (field nr)]]--field :: Int -> Name-field n = mkName $ "f" ++ show n--lrP :: Int -> Int -> (Q Pat -> Q Pat)-lrP 1 0 p = p-lrP m 0 p = conP 'L [p]-lrP m i p = conP 'R [lrP (m-1) (i-1) p]--dataDeclToType :: (Name, [Name]) -> Type-dataDeclToType (dt, vs) = foldl (\a b -> AppT a (VarT b)) (ConT dt) vs
− Generics/Regular/Transformations/ZipperState.hs
@@ -1,57 +0,0 @@-{-# LANGUAGE FlexibleContexts           #-}--module Generics.Regular.Transformations.ZipperState (-  ZipperMonad, ZipperState, upMonad, downMonad, leftMonad, rightMonad, -  navigate, saveMonad, loadMonad, topMonad, updateMonad-  ) where--import Control.Monad.State (StateT (..), evalStateT, get, put)--import Generics.Regular.Zipper-import Generics.Regular ( Regular, PF )------------------------------------------------------------------------------------- A zipper with state-----------------------------------------------------------------------------------type ZipperState a = ([a], Loc a)-type ZipperMonad a b = StateT (ZipperState a) Maybe b--moveMonad :: (Loc a -> Maybe (Loc a)) -> ZipperMonad a a-moveMonad m = StateT (\(s,l) -> m l >>= (\l' -> return (on l', (s,l'))))--upMonad, downMonad, leftMonad, rightMonad :: ZipperMonad a a-upMonad    = moveMonad up-downMonad  = moveMonad down-leftMonad  = moveMonad left-rightMonad = moveMonad right--updateMonad :: (a -> a) -> ZipperMonad a a-updateMonad f = do (s,l) <- get-                   let l' = update f l-                   put (s,l')-                   return (on l')--saveMonad :: ZipperMonad a a-saveMonad = do (s,l) <- get-               let a = on l-               put (s++[a],l)-               return a--loadMonad :: ZipperMonad a a-loadMonad = do (s:ss,l) <- get-               let l' = update (const s) l-               put (ss,l')-               return (on l')--topMonad :: ZipperMonad a a-topMonad = do (_, Loc x l) <- get-              case l of-                [] -> return x-                _  -> upMonad >> topMonad--leaveMonad :: Loc a -> ZipperMonad a b -> Maybe a-leaveMonad s m = evalStateT (m >> topMonad) ([],s)--navigate :: (Regular a, Zipper (PF a)) => a -> ZipperMonad a b -> Maybe a-navigate x m = leaveMonad (enter x) m
Generics/Regular/Zipper.hs view
@@ -1,245 +1,244 @@-{-# LANGUAGE FlexibleContexts           #-}-{-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GADTs                      #-}-{-# LANGUAGE KindSignatures             #-}-{-# LANGUAGE MultiParamTypeClasses      #-}-{-# LANGUAGE RankNTypes                 #-}-{-# LANGUAGE TypeFamilies               #-}-{-# LANGUAGE TypeOperators              #-}-{-# LANGUAGE EmptyDataDecls             #-}-{-# LANGUAGE TupleSections              #-}--module Generics.Regular.Zipper-  (-- * Locations-   Loc(..),-   -- * Context frames-   Ctx(),-   -- * Generic zipper class-   Zipper(..),-   -- * Interface-   enter,-   down, down', up, right, left,-   -- dfnext, dfprev,-   leave, on, update, updateM--  )-  where--import Prelude hiding (last)--import Control.Monad-import Control.Monad.State-import Control.Applicative-import Data.Maybe-import Data.Traversable--import Generics.Regular hiding (left, right)---- * Locations and context stacks---- | Abstract type of locations. A location contains the current focus--- and its context. A location is parameterized over the family of--- datatypes and over the type of the complete value.--data Loc :: * -> * where-  Loc :: (Regular a, Zipper (PF a)) => a -> [Ctx (PF a) a] -> Loc a---- * Context frames---- | Abstract type of context frames. Not required for the high-level--- navigation functions.--data family Ctx (f :: * -> *) :: * -> *--data instance Ctx (K a) r-data instance Ctx U r-data instance Ctx (f :+: g) r = CL (Ctx f r) | CR (Ctx g r)-data instance Ctx (f :*: g) r = C1 (Ctx f r) (g r) | C2 (f r) (Ctx g r)-data instance Ctx I r = CId-data instance Ctx (C c f) r = CC (Ctx f r)-data instance Ctx (S s f) r = CS (Ctx f r)---- * Contexts and locations are functors--instance Zipper f => Functor (Ctx f) where-  fmap = cmap---- instance Functor (Loc f) where-  -- fmap f (Loc p x)  = Loc (f p) (map (fmap f) x)---- * Generic navigation functions---- | It is in general not necessary to use the generic navigation--- functions directly. The functions listed in the ``Interface'' section--- below are more user-friendly.-----class Functor f => Zipper f where-  cmap        :: (a -> b) -> Ctx f a -> Ctx f b-  fill        :: Ctx f a -> a -> f a-  first, last :: f a -> Maybe (a, Ctx f a)-  next, prev  :: Ctx f a -> a -> Maybe (a, Ctx f a)--instance Zipper I where-  cmap  f CId = CId-  fill  CId x = I x-  first (I x) = Just (x, CId)-  last  (I x) = Just (x, CId)-  next  CId x = Nothing-  prev  CId x = Nothing--instance Zipper (K a) where-  cmap f void = impossible void-  fill void x = impossible void-  first (K a) = Nothing-  last  (K a) = Nothing-  next  void x = impossible void-  prev  void x = impossible void--instance Zipper U where-  cmap f void = impossible void-  fill void x = impossible void-  first U      = Nothing-  last  U      = Nothing-  next  void x = impossible void-  prev  void x = impossible void--instance (Zipper f, Zipper g) => Zipper (f :+: g) where-  cmap f (CL c)   = CL (cmap f c)-  cmap f (CR c)   = CR (cmap f c)-  fill (CL c) x   = L (fill c x)-  fill (CR c) y   = R (fill c y)-  first (L x)     = first x >>= return . fmap CL-  first (R x)     = first x >>= return . fmap CR-  last  (L x)     = last x >>= return . fmap CL-  last  (R x)     = last x >>= return . fmap CR-  next  (CL c) x = next c x >>= return . fmap CL-  next  (CR c) x = next c x >>= return . fmap CR-  prev  (CL c) x = prev c x >>= return . fmap CL-  prev  (CR c) x = prev c x >>= return . fmap CR--instance (Zipper f, Zipper g) => Zipper (f :*: g) where-  cmap f (C1 c y)   = C1 (cmap f c) (fmap f y)-  cmap f (C2 x c)   = C2 (fmap f x) (cmap f c)-  fill (C1 c y) x = fill c x :*: y-  fill (C2 x c) y = x :*: fill c y-  first (x :*: y) =         fmap (fmap (flip C1 y)) (first x)-                    `mplus` fmap (fmap (C2 x))      (first y)-  last  (x :*: y) =         fmap (fmap (C2 x))      (last  y)-                    `mplus` fmap (fmap (flip C1 y)) (last  x)-  next (C1 c y) z =         (fmap (flip C1 y)     <$> next c z)-                    `mplus` (fmap (C2 (fill c z)) <$> first y)-  next (C2 x c) z =          fmap (C2 x)          <$> next c z-  prev (C1 c y) z =          fmap (flip C1 y)     <$> prev c z-  prev (C2 x c) z =         (fmap (C2 x)               <$> prev c z)-                    `mplus` (fmap (flip C1 (fill c z)) <$> last x)--instance (Zipper f) => Zipper (C c f) where-  cmap f (CC c)   = CC (cmap f c)-  fill   (CC c) x = C (fill c x)-  first  (C x)    = first  x >>= return . fmap CC-  last   (C x)    = last   x >>= return . fmap CC-  next   (CC c) x = next c x >>= return . fmap CC-  prev   (CC c) x = prev c x >>= return . fmap CC--instance (Zipper f) => Zipper (S s f) where-  cmap f (CS c)   = CS (cmap f c)-  fill   (CS c) x = S (fill c x)-  first  (S x)    = first  x >>= return . fmap CS-  last   (S x)    = last   x >>= return . fmap CS-  next   (CS c) x = next c x >>= return . fmap CS-  prev   (CS c) x = prev c x >>= return . fmap CS---- * Interface---- ** Introduction---- | Start navigating a datastructure. Returns a location that--- focuses the entire value and has an empty context.-enter :: (Regular a, Zipper (PF a)) => a -> Loc a-enter x = Loc x []---- ** Navigation---- | Move down to the leftmost child. Returns 'Nothing' if the--- current focus is a leaf.-down :: Loc a -> Maybe (Loc a)-down (Loc x cs) = first (from x) >>= \(a,c) -> return (Loc a (c:cs))---- | Move down to the rightmost child. Returns 'Nothing' if the--- current focus is a leaf.-down' :: Loc a -> Maybe (Loc a)-down' (Loc x cs) = last (from x) >>= \(a,c) -> return (Loc a (c:cs))---- | Move up to the parent. Returns 'Nothing' if the current--- focus is the root.-up :: Loc a -> Maybe (Loc a)-up (Loc x [])     = Nothing-up (Loc x (c:cs)) = return (Loc (to (fill c x)) cs)---- | Move to the right sibling. Returns 'Nothing' if the current--- focus is the rightmost sibling.-right :: Loc a -> Maybe (Loc a)-right (Loc x []    ) = Nothing-right (Loc x (c:cs)) = next c x >>= \(a,c') -> return (Loc a (c':cs))---- | Move to the left sibling. Returns 'Nothing' if the current--- focus is the leftmost sibling.-left :: Loc a -> Maybe (Loc a)-left (Loc x []    ) = Nothing-left (Loc x (c:cs)) = prev c x >>= \(a,c') -> return (Loc a (c':cs))----- ** Derived navigation.-{--df :: (a -> Maybe a) -> (a -> Maybe a) -> (a -> Maybe a) -> a -> Maybe a-df d u lr l =-  case d l of-    Nothing -> df' l-    r       -> r- where-  df' l =-    case lr l of-      Nothing -> case u l of-                   Nothing -> Nothing-                   Just l' -> df' l'-      r       -> r---- | Move through all positions in depth-first left-to-right order.-dfnext :: Loc phi I0 ix -> Maybe (Loc phi I0 ix)-dfnext = df down up right---- | Move through all positions in depth-first right-to-left order.-dfprev :: Loc phi I0 ix -> Maybe (Loc phi I0 ix)-dfprev = df down' up left--}---- | Utility--- navigate :: (Regular a, Zipper (PF a))-         -- => a -> (Loc a -> Maybe (Loc a)) -> Loc a--- navigate a f = fromJust $ f (enter a)---- ** Elimination---- | Return the entire value, independent of the current focus.-leave :: Loc a -> a-leave (Loc x []) = x-leave loc        = leave (fromJust (up loc))---- | Operate on the current focus. This function can be used to--- extract the current point of focus.-on :: Loc a -> a-on (Loc x _) = x---- | Update the current focus without changing its type.-update :: (a -> a) -> Loc a -> Loc a-update f (Loc x cs) = Loc (f x) cs---- | Update the current focus without changing its type.-updateM :: Monad m => (a -> m a) -> Loc a -> m (Loc a)-updateM f (Loc x cs) = f x >>= \y -> return (Loc y cs)---- * Internal functions--impossible :: a -> b-impossible x = x `seq` error "impossible"+{-# LANGUAGE FlexibleContexts           #-}
+{-# LANGUAGE FlexibleInstances          #-}
+{-# LANGUAGE GADTs                      #-}
+{-# LANGUAGE KindSignatures             #-}
+{-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE RankNTypes                 #-}
+{-# LANGUAGE TypeFamilies               #-}
+{-# LANGUAGE TypeOperators              #-}
+{-# LANGUAGE EmptyDataDecls             #-}
+{-# LANGUAGE TupleSections              #-}
+
+module Generics.Regular.Zipper
+  (-- * Locations
+   Loc(..),
+   -- * Context frames
+   Ctx(..),
+   -- * Generic zipper class
+   Zipper(..),
+   -- * Interface
+   enter,
+   down, down', up, right, left,
+   -- dfnext, dfprev,
+   leave, on, update, updateM
+
+  )
+  where
+
+import Prelude hiding (last)
+
+import Control.Monad
+import Control.Monad.State
+import Control.Applicative
+import Data.Maybe
+
+import Generics.Regular hiding (left, right)
+
+-- * Locations and context stacks
+
+-- | Abstract type of locations. A location contains the current focus
+-- and its context. A location is parameterized over the family of
+-- datatypes and over the type of the complete value.
+
+data Loc :: * -> * where
+  Loc :: (Regular a, Zipper (PF a)) => a -> [Ctx (PF a) a] -> Loc a
+
+-- * Context frames
+
+-- | Abstract type of context frames. Not required for the high-level
+-- navigation functions.
+
+data family Ctx (f :: * -> *) :: * -> *
+
+data instance Ctx (K a) r
+data instance Ctx U r
+data instance Ctx (f :+: g) r = CL (Ctx f r) | CR (Ctx g r)
+data instance Ctx (f :*: g) r = C1 (Ctx f r) (g r) | C2 (f r) (Ctx g r)
+data instance Ctx I r = CId
+data instance Ctx (C c f) r = CC (Ctx f r)
+data instance Ctx (S s f) r = CS (Ctx f r)
+
+-- * Contexts and locations are functors
+
+instance Zipper f => Functor (Ctx f) where
+  fmap = cmap
+
+-- instance Functor (Loc f) where
+  -- fmap f (Loc p x)  = Loc (f p) (map (fmap f) x)
+
+-- * Generic navigation functions
+
+-- | It is in general not necessary to use the generic navigation
+-- functions directly. The functions listed in the ``Interface'' section
+-- below are more user-friendly.
+--
+
+class Functor f => Zipper f where
+  cmap        :: (a -> b) -> Ctx f a -> Ctx f b
+  fill        :: Ctx f a -> a -> f a
+  first, last :: f a -> Maybe (a, Ctx f a)
+  next, prev  :: Ctx f a -> a -> Maybe (a, Ctx f a)
+
+instance Zipper I where
+  cmap  f CId = CId
+  fill  CId x = I x
+  first (I x) = Just (x, CId)
+  last  (I x) = Just (x, CId)
+  next  CId x = Nothing
+  prev  CId x = Nothing
+
+instance Zipper (K a) where
+  cmap f void = impossible void
+  fill void x = impossible void
+  first (K a) = Nothing
+  last  (K a) = Nothing
+  next  void x = impossible void
+  prev  void x = impossible void
+
+instance Zipper U where
+  cmap f void = impossible void
+  fill void x = impossible void
+  first U      = Nothing
+  last  U      = Nothing
+  next  void x = impossible void
+  prev  void x = impossible void
+
+instance (Zipper f, Zipper g) => Zipper (f :+: g) where
+  cmap f (CL c)   = CL (cmap f c)
+  cmap f (CR c)   = CR (cmap f c)
+  fill (CL c) x   = L (fill c x)
+  fill (CR c) y   = R (fill c y)
+  first (L x)     = first x >>= return . fmap CL
+  first (R x)     = first x >>= return . fmap CR
+  last  (L x)     = last x >>= return . fmap CL
+  last  (R x)     = last x >>= return . fmap CR
+  next  (CL c) x = next c x >>= return . fmap CL
+  next  (CR c) x = next c x >>= return . fmap CR
+  prev  (CL c) x = prev c x >>= return . fmap CL
+  prev  (CR c) x = prev c x >>= return . fmap CR
+
+instance (Zipper f, Zipper g) => Zipper (f :*: g) where
+  cmap f (C1 c y)   = C1 (cmap f c) (fmap f y)
+  cmap f (C2 x c)   = C2 (fmap f x) (cmap f c)
+  fill (C1 c y) x = fill c x :*: y
+  fill (C2 x c) y = x :*: fill c y
+  first (x :*: y) =         fmap (fmap (flip C1 y)) (first x)
+                    `mplus` fmap (fmap (C2 x))      (first y)
+  last  (x :*: y) =         fmap (fmap (C2 x))      (last  y)
+                    `mplus` fmap (fmap (flip C1 y)) (last  x)
+  next (C1 c y) z =         (fmap (flip C1 y)     <$> next c z)
+                    `mplus` (fmap (C2 (fill c z)) <$> first y)
+  next (C2 x c) z =          fmap (C2 x)          <$> next c z
+  prev (C1 c y) z =          fmap (flip C1 y)     <$> prev c z
+  prev (C2 x c) z =         (fmap (C2 x)               <$> prev c z)
+                    `mplus` (fmap (flip C1 (fill c z)) <$> last x)
+
+instance (Zipper f) => Zipper (C c f) where
+  cmap f (CC c)   = CC (cmap f c)
+  fill   (CC c) x = C (fill c x)
+  first  (C x)    = first  x >>= return . fmap CC
+  last   (C x)    = last   x >>= return . fmap CC
+  next   (CC c) x = next c x >>= return . fmap CC
+  prev   (CC c) x = prev c x >>= return . fmap CC
+
+instance (Zipper f) => Zipper (S s f) where
+  cmap f (CS c)   = CS (cmap f c)
+  fill   (CS c) x = S (fill c x)
+  first  (S x)    = first  x >>= return . fmap CS
+  last   (S x)    = last   x >>= return . fmap CS
+  next   (CS c) x = next c x >>= return . fmap CS
+  prev   (CS c) x = prev c x >>= return . fmap CS
+
+-- * Interface
+
+-- ** Introduction
+
+-- | Start navigating a datastructure. Returns a location that
+-- focuses the entire value and has an empty context.
+enter :: (Regular a, Zipper (PF a)) => a -> Loc a
+enter x = Loc x []
+
+-- ** Navigation
+
+-- | Move down to the leftmost child. Returns 'Nothing' if the
+-- current focus is a leaf.
+down :: Loc a -> Maybe (Loc a)
+down (Loc x cs) = first (from x) >>= \(a,c) -> return (Loc a (c:cs))
+
+-- | Move down to the rightmost child. Returns 'Nothing' if the
+-- current focus is a leaf.
+down' :: Loc a -> Maybe (Loc a)
+down' (Loc x cs) = last (from x) >>= \(a,c) -> return (Loc a (c:cs))
+
+-- | Move up to the parent. Returns 'Nothing' if the current
+-- focus is the root.
+up :: Loc a -> Maybe (Loc a)
+up (Loc x [])     = Nothing
+up (Loc x (c:cs)) = return (Loc (to (fill c x)) cs)
+
+-- | Move to the right sibling. Returns 'Nothing' if the current
+-- focus is the rightmost sibling.
+right :: Loc a -> Maybe (Loc a)
+right (Loc x []    ) = Nothing
+right (Loc x (c:cs)) = next c x >>= \(a,c') -> return (Loc a (c':cs))
+
+-- | Move to the left sibling. Returns 'Nothing' if the current
+-- focus is the leftmost sibling.
+left :: Loc a -> Maybe (Loc a)
+left (Loc x []    ) = Nothing
+left (Loc x (c:cs)) = prev c x >>= \(a,c') -> return (Loc a (c':cs))
+
+
+-- ** Derived navigation.
+{-
+df :: (a -> Maybe a) -> (a -> Maybe a) -> (a -> Maybe a) -> a -> Maybe a
+df d u lr l =
+  case d l of
+    Nothing -> df' l
+    r       -> r
+ where
+  df' l =
+    case lr l of
+      Nothing -> case u l of
+                   Nothing -> Nothing
+                   Just l' -> df' l'
+      r       -> r
+
+-- | Move through all positions in depth-first left-to-right order.
+dfnext :: Loc phi I0 ix -> Maybe (Loc phi I0 ix)
+dfnext = df down up right
+
+-- | Move through all positions in depth-first right-to-left order.
+dfprev :: Loc phi I0 ix -> Maybe (Loc phi I0 ix)
+dfprev = df down' up left
+-}
+
+-- | Utility
+-- navigate :: (Regular a, Zipper (PF a))
+         -- => a -> (Loc a -> Maybe (Loc a)) -> Loc a
+-- navigate a f = fromJust $ f (enter a)
+
+-- ** Elimination
+
+-- | Return the entire value, independent of the current focus.
+leave :: Loc a -> a
+leave (Loc x []) = x
+leave loc        = leave (fromJust (up loc))
+
+-- | Operate on the current focus. This function can be used to
+-- extract the current point of focus.
+on :: Loc a -> a
+on (Loc x _) = x
+
+-- | Update the current focus without changing its type.
+update :: (a -> a) -> Loc a -> Loc a
+update f (Loc x cs) = Loc (f x) cs
+
+-- | Update the current focus without changing its type.
+updateM :: Monad m => (a -> m a) -> Loc a -> m (Loc a)
+updateM f (Loc x cs) = f x >>= \y -> return (Loc y cs)
+
+-- * Internal functions
+
+impossible :: a -> b
+impossible x = x `seq` error "impossible"
LICENSE view
@@ -1,675 +1,675 @@-              GNU GENERAL PUBLIC LICENSE-                Version 3, 29 June 2007-- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>- Everyone is permitted to copy and distribute verbatim copies- of this license document, but changing it is not allowed.--                     Preamble--  The GNU General Public License is a free, copyleft license for-software and other kinds of works.--  The licenses for most software and other practical works are designed-to take away your freedom to share and change the works.  By contrast,-the GNU General Public License is intended to guarantee your freedom to-share and change all versions of a program--to make sure it remains free-software for all its users.  We, the Free Software Foundation, use the-GNU General Public License for most of our software; it applies also to-any other work released this way by its authors.  You can apply it to-your programs, too.--  When we speak of free software, we are referring to freedom, not-price.  Our General Public Licenses are designed to make sure that you-have the freedom to distribute copies of free software (and charge for-them if you wish), that you receive source code or can get it if you-want it, that you can change the software or use pieces of it in new-free programs, and that you know you can do these things.--  To protect your rights, we need to prevent others from denying you-these rights or asking you to surrender the rights.  Therefore, you have-certain responsibilities if you distribute copies of the software, or if-you modify it: responsibilities to respect the freedom of others.--  For example, if you distribute copies of such a program, whether-gratis or for a fee, you must pass on to the recipients the same-freedoms that you received.  You must make sure that they, too, receive-or can get the source code.  And you must show them these terms so they-know their rights.--  Developers that use the GNU GPL protect your rights with two steps:-(1) assert copyright on the software, and (2) offer you this License-giving you legal permission to copy, distribute and/or modify it.--  For the developers' and authors' protection, the GPL clearly explains-that there is no warranty for this free software.  For both users' and-authors' sake, the GPL requires that modified versions be marked as-changed, so that their problems will not be attributed erroneously to-authors of previous versions.--  Some devices are designed to deny users access to install or run-modified versions of the software inside them, although the manufacturer-can do so.  This is fundamentally incompatible with the aim of-protecting users' freedom to change the software.  The systematic-pattern of such abuse occurs in the area of products for individuals to-use, which is precisely where it is most unacceptable.  Therefore, we-have designed this version of the GPL to prohibit the practice for those-products.  If such problems arise substantially in other domains, we-stand ready to extend this provision to those domains in future versions-of the GPL, as needed to protect the freedom of users.--  Finally, every program is threatened constantly by software patents.-States should not allow patents to restrict development and use of-software on general-purpose computers, but in those that do, we wish to-avoid the special danger that patents applied to a free program could-make it effectively proprietary.  To prevent this, the GPL assures that-patents cannot be used to render the program non-free.--  The precise terms and conditions for copying, distribution and-modification follow.--                TERMS AND CONDITIONS--  0. Definitions.--  "This License" refers to version 3 of the GNU General Public License.--  "Copyright" also means copyright-like laws that apply to other kinds of-works, such as semiconductor masks.- -  "The Program" refers to any copyrightable work licensed under this-License.  Each licensee is addressed as "you".  "Licensees" and-"recipients" may be individuals or organizations.--  To "modify" a work means to copy from or adapt all or part of the work-in a fashion requiring copyright permission, other than the making of an-exact copy.  The resulting work is called a "modified version" of the-earlier work or a work "based on" the earlier work.--  A "covered work" means either the unmodified Program or a work based-on the Program.--  To "propagate" a work means to do anything with it that, without-permission, would make you directly or secondarily liable for-infringement under applicable copyright law, except executing it on a-computer or modifying a private copy.  Propagation includes copying,-distribution (with or without modification), making available to the-public, and in some countries other activities as well.--  To "convey" a work means any kind of propagation that enables other-parties to make or receive copies.  Mere interaction with a user through-a computer network, with no transfer of a copy, is not conveying.--  An interactive user interface displays "Appropriate Legal Notices"-to the extent that it includes a convenient and prominently visible-feature that (1) displays an appropriate copyright notice, and (2)-tells the user that there is no warranty for the work (except to the-extent that warranties are provided), that licensees may convey the-work under this License, and how to view a copy of this License.  If-the interface presents a list of user commands or options, such as a-menu, a prominent item in the list meets this criterion.--  1. Source Code.--  The "source code" for a work means the preferred form of the work-for making modifications to it.  "Object code" means any non-source-form of a work.--  A "Standard Interface" means an interface that either is an official-standard defined by a recognized standards body, or, in the case of-interfaces specified for a particular programming language, one that-is widely used among developers working in that language.--  The "System Libraries" of an executable work include anything, other-than the work as a whole, that (a) is included in the normal form of-packaging a Major Component, but which is not part of that Major-Component, and (b) serves only to enable use of the work with that-Major Component, or to implement a Standard Interface for which an-implementation is available to the public in source code form.  A-"Major Component", in this context, means a major essential component-(kernel, window system, and so on) of the specific operating system-(if any) on which the executable work runs, or a compiler used to-produce the work, or an object code interpreter used to run it.--  The "Corresponding Source" for a work in object code form means all-the source code needed to generate, install, and (for an executable-work) run the object code and to modify the work, including scripts to-control those activities.  However, it does not include the work's-System Libraries, or general-purpose tools or generally available free-programs which are used unmodified in performing those activities but-which are not part of the work.  For example, Corresponding Source-includes interface definition files associated with source files for-the work, and the source code for shared libraries and dynamically-linked subprograms that the work is specifically designed to require,-such as by intimate data communication or control flow between those-subprograms and other parts of the work.--  The Corresponding Source need not include anything that users-can regenerate automatically from other parts of the Corresponding-Source.--  The Corresponding Source for a work in source code form is that-same work.--  2. Basic Permissions.--  All rights granted under this License are granted for the term of-copyright on the Program, and are irrevocable provided the stated-conditions are met.  This License explicitly affirms your unlimited-permission to run the unmodified Program.  The output from running a-covered work is covered by this License only if the output, given its-content, constitutes a covered work.  This License acknowledges your-rights of fair use or other equivalent, as provided by copyright law.--  You may make, run and propagate covered works that you do not-convey, without conditions so long as your license otherwise remains-in force.  You may convey covered works to others for the sole purpose-of having them make modifications exclusively for you, or provide you-with facilities for running those works, provided that you comply with-the terms of this License in conveying all material for which you do-not control copyright.  Those thus making or running the covered works-for you must do so exclusively on your behalf, under your direction-and control, on terms that prohibit them from making any copies of-your copyrighted material outside their relationship with you.--  Conveying under any other circumstances is permitted solely under-the conditions stated below.  Sublicensing is not allowed; section 10-makes it unnecessary.--  3. Protecting Users' Legal Rights From Anti-Circumvention Law.--  No covered work shall be deemed part of an effective technological-measure under any applicable law fulfilling obligations under article-11 of the WIPO copyright treaty adopted on 20 December 1996, or-similar laws prohibiting or restricting circumvention of such-measures.--  When you convey a covered work, you waive any legal power to forbid-circumvention of technological measures to the extent such circumvention-is effected by exercising rights under this License with respect to-the covered work, and you disclaim any intention to limit operation or-modification of the work as a means of enforcing, against the work's-users, your or third parties' legal rights to forbid circumvention of-technological measures.--  4. Conveying Verbatim Copies.--  You may convey verbatim copies of the Program's source code as you-receive it, in any medium, provided that you conspicuously and-appropriately publish on each copy an appropriate copyright notice;-keep intact all notices stating that this License and any-non-permissive terms added in accord with section 7 apply to the code;-keep intact all notices of the absence of any warranty; and give all-recipients a copy of this License along with the Program.--  You may charge any price or no price for each copy that you convey,-and you may offer support or warranty protection for a fee.--  5. Conveying Modified Source Versions.--  You may convey a work based on the Program, or the modifications to-produce it from the Program, in the form of source code under the-terms of section 4, provided that you also meet all of these conditions:--    a) The work must carry prominent notices stating that you modified-    it, and giving a relevant date.--    b) The work must carry prominent notices stating that it is-    released under this License and any conditions added under section-    7.  This requirement modifies the requirement in section 4 to-    "keep intact all notices".--    c) You must license the entire work, as a whole, under this-    License to anyone who comes into possession of a copy.  This-    License will therefore apply, along with any applicable section 7-    additional terms, to the whole of the work, and all its parts,-    regardless of how they are packaged.  This License gives no-    permission to license the work in any other way, but it does not-    invalidate such permission if you have separately received it.--    d) If the work has interactive user interfaces, each must display-    Appropriate Legal Notices; however, if the Program has interactive-    interfaces that do not display Appropriate Legal Notices, your-    work need not make them do so.--  A compilation of a covered work with other separate and independent-works, which are not by their nature extensions of the covered work,-and which are not combined with it such as to form a larger program,-in or on a volume of a storage or distribution medium, is called an-"aggregate" if the compilation and its resulting copyright are not-used to limit the access or legal rights of the compilation's users-beyond what the individual works permit.  Inclusion of a covered work-in an aggregate does not cause this License to apply to the other-parts of the aggregate.--  6. Conveying Non-Source Forms.--  You may convey a covered work in object code form under the terms-of sections 4 and 5, provided that you also convey the-machine-readable Corresponding Source under the terms of this License,-in one of these ways:--    a) Convey the object code in, or embodied in, a physical product-    (including a physical distribution medium), accompanied by the-    Corresponding Source fixed on a durable physical medium-    customarily used for software interchange.--    b) Convey the object code in, or embodied in, a physical product-    (including a physical distribution medium), accompanied by a-    written offer, valid for at least three years and valid for as-    long as you offer spare parts or customer support for that product-    model, to give anyone who possesses the object code either (1) a-    copy of the Corresponding Source for all the software in the-    product that is covered by this License, on a durable physical-    medium customarily used for software interchange, for a price no-    more than your reasonable cost of physically performing this-    conveying of source, or (2) access to copy the-    Corresponding Source from a network server at no charge.--    c) Convey individual copies of the object code with a copy of the-    written offer to provide the Corresponding Source.  This-    alternative is allowed only occasionally and noncommercially, and-    only if you received the object code with such an offer, in accord-    with subsection 6b.--    d) Convey the object code by offering access from a designated-    place (gratis or for a charge), and offer equivalent access to the-    Corresponding Source in the same way through the same place at no-    further charge.  You need not require recipients to copy the-    Corresponding Source along with the object code.  If the place to-    copy the object code is a network server, the Corresponding Source-    may be on a different server (operated by you or a third party)-    that supports equivalent copying facilities, provided you maintain-    clear directions next to the object code saying where to find the-    Corresponding Source.  Regardless of what server hosts the-    Corresponding Source, you remain obligated to ensure that it is-    available for as long as needed to satisfy these requirements.--    e) Convey the object code using peer-to-peer transmission, provided-    you inform other peers where the object code and Corresponding-    Source of the work are being offered to the general public at no-    charge under subsection 6d.--  A separable portion of the object code, whose source code is excluded-from the Corresponding Source as a System Library, need not be-included in conveying the object code work.--  A "User Product" is either (1) a "consumer product", which means any-tangible personal property which is normally used for personal, family,-or household purposes, or (2) anything designed or sold for incorporation-into a dwelling.  In determining whether a product is a consumer product,-doubtful cases shall be resolved in favor of coverage.  For a particular-product received by a particular user, "normally used" refers to a-typical or common use of that class of product, regardless of the status-of the particular user or of the way in which the particular user-actually uses, or expects or is expected to use, the product.  A product-is a consumer product regardless of whether the product has substantial-commercial, industrial or non-consumer uses, unless such uses represent-the only significant mode of use of the product.--  "Installation Information" for a User Product means any methods,-procedures, authorization keys, or other information required to install-and execute modified versions of a covered work in that User Product from-a modified version of its Corresponding Source.  The information must-suffice to ensure that the continued functioning of the modified object-code is in no case prevented or interfered with solely because-modification has been made.--  If you convey an object code work under this section in, or with, or-specifically for use in, a User Product, and the conveying occurs as-part of a transaction in which the right of possession and use of the-User Product is transferred to the recipient in perpetuity or for a-fixed term (regardless of how the transaction is characterized), the-Corresponding Source conveyed under this section must be accompanied-by the Installation Information.  But this requirement does not apply-if neither you nor any third party retains the ability to install-modified object code on the User Product (for example, the work has-been installed in ROM).--  The requirement to provide Installation Information does not include a-requirement to continue to provide support service, warranty, or updates-for a work that has been modified or installed by the recipient, or for-the User Product in which it has been modified or installed.  Access to a-network may be denied when the modification itself materially and-adversely affects the operation of the network or violates the rules and-protocols for communication across the network.--  Corresponding Source conveyed, and Installation Information provided,-in accord with this section must be in a format that is publicly-documented (and with an implementation available to the public in-source code form), and must require no special password or key for-unpacking, reading or copying.--  7. Additional Terms.--  "Additional permissions" are terms that supplement the terms of this-License by making exceptions from one or more of its conditions.-Additional permissions that are applicable to the entire Program shall-be treated as though they were included in this License, to the extent-that they are valid under applicable law.  If additional permissions-apply only to part of the Program, that part may be used separately-under those permissions, but the entire Program remains governed by-this License without regard to the additional permissions.--  When you convey a copy of a covered work, you may at your option-remove any additional permissions from that copy, or from any part of-it.  (Additional permissions may be written to require their own-removal in certain cases when you modify the work.)  You may place-additional permissions on material, added by you to a covered work,-for which you have or can give appropriate copyright permission.--  Notwithstanding any other provision of this License, for material you-add to a covered work, you may (if authorized by the copyright holders of-that material) supplement the terms of this License with terms:--    a) Disclaiming warranty or limiting liability differently from the-    terms of sections 15 and 16 of this License; or--    b) Requiring preservation of specified reasonable legal notices or-    author attributions in that material or in the Appropriate Legal-    Notices displayed by works containing it; or--    c) Prohibiting misrepresentation of the origin of that material, or-    requiring that modified versions of such material be marked in-    reasonable ways as different from the original version; or--    d) Limiting the use for publicity purposes of names of licensors or-    authors of the material; or--    e) Declining to grant rights under trademark law for use of some-    trade names, trademarks, or service marks; or--    f) Requiring indemnification of licensors and authors of that-    material by anyone who conveys the material (or modified versions of-    it) with contractual assumptions of liability to the recipient, for-    any liability that these contractual assumptions directly impose on-    those licensors and authors.--  All other non-permissive additional terms are considered "further-restrictions" within the meaning of section 10.  If the Program as you-received it, or any part of it, contains a notice stating that it is-governed by this License along with a term that is a further-restriction, you may remove that term.  If a license document contains-a further restriction but permits relicensing or conveying under this-License, you may add to a covered work material governed by the terms-of that license document, provided that the further restriction does-not survive such relicensing or conveying.--  If you add terms to a covered work in accord with this section, you-must place, in the relevant source files, a statement of the-additional terms that apply to those files, or a notice indicating-where to find the applicable terms.--  Additional terms, permissive or non-permissive, may be stated in the-form of a separately written license, or stated as exceptions;-the above requirements apply either way.--  8. Termination.--  You may not propagate or modify a covered work except as expressly-provided under this License.  Any attempt otherwise to propagate or-modify it is void, and will automatically terminate your rights under-this License (including any patent licenses granted under the third-paragraph of section 11).--  However, if you cease all violation of this License, then your-license from a particular copyright holder is reinstated (a)-provisionally, unless and until the copyright holder explicitly and-finally terminates your license, and (b) permanently, if the copyright-holder fails to notify you of the violation by some reasonable means-prior to 60 days after the cessation.--  Moreover, your license from a particular copyright holder is-reinstated permanently if the copyright holder notifies you of the-violation by some reasonable means, this is the first time you have-received notice of violation of this License (for any work) from that-copyright holder, and you cure the violation prior to 30 days after-your receipt of the notice.--  Termination of your rights under this section does not terminate the-licenses of parties who have received copies or rights from you under-this License.  If your rights have been terminated and not permanently-reinstated, you do not qualify to receive new licenses for the same-material under section 10.--  9. Acceptance Not Required for Having Copies.--  You are not required to accept this License in order to receive or-run a copy of the Program.  Ancillary propagation of a covered work-occurring solely as a consequence of using peer-to-peer transmission-to receive a copy likewise does not require acceptance.  However,-nothing other than this License grants you permission to propagate or-modify any covered work.  These actions infringe copyright if you do-not accept this License.  Therefore, by modifying or propagating a-covered work, you indicate your acceptance of this License to do so.--  10. Automatic Licensing of Downstream Recipients.--  Each time you convey a covered work, the recipient automatically-receives a license from the original licensors, to run, modify and-propagate that work, subject to this License.  You are not responsible-for enforcing compliance by third parties with this License.--  An "entity transaction" is a transaction transferring control of an-organization, or substantially all assets of one, or subdividing an-organization, or merging organizations.  If propagation of a covered-work results from an entity transaction, each party to that-transaction who receives a copy of the work also receives whatever-licenses to the work the party's predecessor in interest had or could-give under the previous paragraph, plus a right to possession of the-Corresponding Source of the work from the predecessor in interest, if-the predecessor has it or can get it with reasonable efforts.--  You may not impose any further restrictions on the exercise of the-rights granted or affirmed under this License.  For example, you may-not impose a license fee, royalty, or other charge for exercise of-rights granted under this License, and you may not initiate litigation-(including a cross-claim or counterclaim in a lawsuit) alleging that-any patent claim is infringed by making, using, selling, offering for-sale, or importing the Program or any portion of it.--  11. Patents.--  A "contributor" is a copyright holder who authorizes use under this-License of the Program or a work on which the Program is based.  The-work thus licensed is called the contributor's "contributor version".--  A contributor's "essential patent claims" are all patent claims-owned or controlled by the contributor, whether already acquired or-hereafter acquired, that would be infringed by some manner, permitted-by this License, of making, using, or selling its contributor version,-but do not include claims that would be infringed only as a-consequence of further modification of the contributor version.  For-purposes of this definition, "control" includes the right to grant-patent sublicenses in a manner consistent with the requirements of-this License.--  Each contributor grants you a non-exclusive, worldwide, royalty-free-patent license under the contributor's essential patent claims, to-make, use, sell, offer for sale, import and otherwise run, modify and-propagate the contents of its contributor version.--  In the following three paragraphs, a "patent license" is any express-agreement or commitment, however denominated, not to enforce a patent-(such as an express permission to practice a patent or covenant not to-sue for patent infringement).  To "grant" such a patent license to a-party means to make such an agreement or commitment not to enforce a-patent against the party.--  If you convey a covered work, knowingly relying on a patent license,-and the Corresponding Source of the work is not available for anyone-to copy, free of charge and under the terms of this License, through a-publicly available network server or other readily accessible means,-then you must either (1) cause the Corresponding Source to be so-available, or (2) arrange to deprive yourself of the benefit of the-patent license for this particular work, or (3) arrange, in a manner-consistent with the requirements of this License, to extend the patent-license to downstream recipients.  "Knowingly relying" means you have-actual knowledge that, but for the patent license, your conveying the-covered work in a country, or your recipient's use of the covered work-in a country, would infringe one or more identifiable patents in that-country that you have reason to believe are valid.-  -  If, pursuant to or in connection with a single transaction or-arrangement, you convey, or propagate by procuring conveyance of, a-covered work, and grant a patent license to some of the parties-receiving the covered work authorizing them to use, propagate, modify-or convey a specific copy of the covered work, then the patent license-you grant is automatically extended to all recipients of the covered-work and works based on it.--  A patent license is "discriminatory" if it does not include within-the scope of its coverage, prohibits the exercise of, or is-conditioned on the non-exercise of one or more of the rights that are-specifically granted under this License.  You may not convey a covered-work if you are a party to an arrangement with a third party that is-in the business of distributing software, under which you make payment-to the third party based on the extent of your activity of conveying-the work, and under which the third party grants, to any of the-parties who would receive the covered work from you, a discriminatory-patent license (a) in connection with copies of the covered work-conveyed by you (or copies made from those copies), or (b) primarily-for and in connection with specific products or compilations that-contain the covered work, unless you entered into that arrangement,-or that patent license was granted, prior to 28 March 2007.--  Nothing in this License shall be construed as excluding or limiting-any implied license or other defenses to infringement that may-otherwise be available to you under applicable patent law.--  12. No Surrender of Others' Freedom.--  If conditions are imposed on you (whether by court order, agreement or-otherwise) that contradict the conditions of this License, they do not-excuse you from the conditions of this License.  If you cannot convey a-covered work so as to satisfy simultaneously your obligations under this-License and any other pertinent obligations, then as a consequence you may-not convey it at all.  For example, if you agree to terms that obligate you-to collect a royalty for further conveying from those to whom you convey-the Program, the only way you could satisfy both those terms and this-License would be to refrain entirely from conveying the Program.--  13. Use with the GNU Affero General Public License.--  Notwithstanding any other provision of this License, you have-permission to link or combine any covered work with a work licensed-under version 3 of the GNU Affero General Public License into a single-combined work, and to convey the resulting work.  The terms of this-License will continue to apply to the part which is the covered work,-but the special requirements of the GNU Affero General Public License,-section 13, concerning interaction through a network will apply to the-combination as such.--  14. Revised Versions of this License.--  The Free Software Foundation may publish revised and/or new versions of-the GNU General Public License from time to time.  Such new versions will-be similar in spirit to the present version, but may differ in detail to-address new problems or concerns.--  Each version is given a distinguishing version number.  If the-Program specifies that a certain numbered version of the GNU General-Public License "or any later version" applies to it, you have the-option of following the terms and conditions either of that numbered-version or of any later version published by the Free Software-Foundation.  If the Program does not specify a version number of the-GNU General Public License, you may choose any version ever published-by the Free Software Foundation.--  If the Program specifies that a proxy can decide which future-versions of the GNU General Public License can be used, that proxy's-public statement of acceptance of a version permanently authorizes you-to choose that version for the Program.--  Later license versions may give you additional or different-permissions.  However, no additional obligations are imposed on any-author or copyright holder as a result of your choosing to follow a-later version.--  15. Disclaimer of Warranty.--  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY-APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM-IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.--  16. Limitation of Liability.--  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF-SUCH DAMAGES.--  17. Interpretation of Sections 15 and 16.--  If the disclaimer of warranty and limitation of liability provided-above cannot be given local legal effect according to their terms,-reviewing courts shall apply local law that most closely approximates-an absolute waiver of all civil liability in connection with the-Program, unless a warranty or assumption of liability accompanies a-copy of the Program in return for a fee.--              END OF TERMS AND CONDITIONS--     How to Apply These Terms to Your New Programs--  If you develop a new program, and you want it to be of the greatest-possible use to the public, the best way to achieve this is to make it-free software which everyone can redistribute and change under these terms.--  To do so, attach the following notices to the program.  It is safest-to attach them to the start of each source file to most effectively-state the exclusion of warranty; and each file should have at least-the "copyright" line and a pointer to where the full notice is found.--    <one line to give the program's name and a brief idea of what it does.>-    Copyright (C) <year>  <name of author>--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program is distributed in the hope that it will be useful,-    but WITHOUT ANY WARRANTY; without even the implied warranty of-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-    GNU General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.--Also add information on how to contact you by electronic and paper mail.--  If the program does terminal interaction, make it output a short-notice like this when it starts in an interactive mode:--    <program>  Copyright (C) <year>  <name of author>-    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.-    This is free software, and you are welcome to redistribute it-    under certain conditions; type `show c' for details.--The hypothetical commands `show w' and `show c' should show the appropriate-parts of the General Public License.  Of course, your program's commands-might be different; for a GUI interface, you would use an "about box".--  You should also get your employer (if you work as a programmer) or school,-if any, to sign a "copyright disclaimer" for the program, if necessary.-For more information on this, and how to apply and follow the GNU GPL, see-<http://www.gnu.org/licenses/>.--  The GNU General Public License does not permit incorporating your program-into proprietary programs.  If your program is a subroutine library, you-may consider it more useful to permit linking proprietary applications with-the library.  If this is what you want to do, use the GNU Lesser General-Public License instead of this License.  But first, please read-<http://www.gnu.org/philosophy/why-not-lgpl.html>.-+              GNU GENERAL PUBLIC LICENSE
+                Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                     Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+ 
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+  
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+              END OF TERMS AND CONDITIONS
+
+     How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
+ QuickCheck.hs view
@@ -0,0 +1,105 @@+{-# LANGUAGE GADTs                 #-}
+{-# LANGUAGE KindSignatures        #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TypeFamilies          #-}
+{-# LANGUAGE TypeOperators         #-}
+{-# LANGUAGE TypeSynonymInstances  #-}
+{-# LANGUAGE EmptyDataDecls        #-}
+{-# LANGUAGE TemplateHaskell       #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE OverlappingInstances  #-}
+{-# LANGUAGE DataKinds             #-}
+{-# LANGUAGE PolyKinds             #-}
+{-# LANGUAGE TypeFamilies          #-}
+{-# LANGUAGE StandaloneDeriving    #-}
+
+module Main where
+
+import Generics.MultiRec hiding (show)
+import Generics.MultiRec.TH
+import Test.QuickCheck
+import Test.QuickCheck.Random
+import Control.Monad
+import Generics.MultiRec.Transformations.Main
+import Generics.MultiRec.Transformations.MemoTable
+import Criterion.Main
+
+data Tree = Leaf Int | Branch1 Tree Tree | Branch2 Tree Tree | Test [(Tree,Int)]
+          deriving (Show, Ord, Eq)
+
+data TreeAST :: * -> * where
+  Tree :: TreeAST Tree
+
+type instance Ixs TreeAST = '[ Tree ]
+
+data Leaf
+data Branch1
+data Branch2
+data Test
+
+instance Constructor Leaf where
+  conName _ = "Leaf"
+instance Constructor Branch1 where
+  conName _ = "Branch1"
+instance Constructor Branch2 where
+  conName _ = "Branch2"
+instance Constructor Test where
+  conName _ = "Test"
+
+type instance PF TreeAST = (C Leaf (K Int) :+: C Branch1 (I Tree :*: I Tree) :+: C Branch2 (I Tree :*: I Tree) :+: (C Test ([] :.: (I Tree :*: K Int)))) :>: Tree
+
+instance El TreeAST Tree where
+  proof = Tree
+
+instance Fam TreeAST where
+  from Tree (Leaf f0)
+    = Tag (L (C (K f0)))
+  from Tree (Branch1 f0 f1)
+    = Tag (R (L (C ((:*:) ((I . I0) f0) ((I . I0) f1)))))
+  from Tree (Branch2 f0 f1)
+    = Tag (R (R (L (C ((:*:) ((I . I0) f0) ((I . I0) f1))))))
+  from Tree (Test f0)
+    = Tag (R (R (R (C (D [(I . I0) i :*: K t | (i,t) <- f0])))))
+  to Tree (Tag (L (C f0)))
+    = Leaf (unK f0)
+  to Tree (Tag (R (L (C ((:*:) f0 f1)))))
+    = Branch1 ((unI0 . unI) f0) ((unI0 . unI) f1)
+  to Tree (Tag (R (R (L (C ((:*:) f0 f1))))))
+    = Branch2 ((unI0 . unI) f0) ((unI0 . unI) f1)
+  to Tree (Tag (R (R (R (C (D f0))))))
+    = Test [((unI0 . unI) t, i) | t :*: K i <- f0]
+
+-- $(deriveAll ''TreeAST)
+
+-- Straight from the QC manual
+instance Arbitrary Tree where
+  shrink (Leaf v) = map Leaf (shrink v)
+  shrink (Branch1 l r) = [l,r]
+  shrink (Branch2 l r) = [l,r]
+  shrink (Test lst) = concatMap (map fst) (shrink lst)
+  arbitrary = sized tree'
+    where tree' 0 = liftM Leaf arbitrary
+          tree' n | n>0 =
+                oneof [liftM Leaf arbitrary,
+                       liftM2 Branch1 subtree subtree,
+                       liftM2 Branch2 subtree subtree,
+                       liftM Test genLst]
+            where subtree = tree' (n `div` 2)
+                  genLst  = do m <- choose (1,n)
+                               replicateM m (resize (n `div` m) arbitrary)
+
+-- Our diff property
+diffCorrect :: Tree -> Tree -> Bool
+diffCorrect a b = apply Tree a (diff Tree a b) == Just b
+
+main :: IO ()
+main = do
+  putStrLn "Generating testcases"
+  cases <- replicateM 40 (liftM2 (,) (generate arbitrary) (generate arbitrary))
+  putStrLn "Checking correctness"
+  forM_ cases $ \(a,b) -> do
+    when (not $ diffCorrect a b) $ putStrLn "Failed diffCorrect"
+  putStrLn "Benchmarking"
+  defaultMain [
+    bench "MultiRec" $ nf (map (uncurry diffCorrect)) cases
+    ]
+ README view
@@ -0,0 +1,24 @@+A quick guide to the example code
+---------------------------------
+
+All the code compiles with GHC 7.10 RC2. The package itself compiles with 7.8,
+but the pattern synonyms used in the examples require 7.10.
+
+QuickCheck.hs contains an artificial benchmark for the MultiRec code.
+
+In the examples folder there are many other examples. The best way to test these
+is to load each of them in GHCi as follows:
+
+> ghci -iexamples examples\Expr.hs
+
+examples/Expr.hs
+  The Expr example shown in the paper
+
+examples/MultiRec.hs
+  The AST example shown in the paper
+
+examples/Regular.hs
+  Examples using the Regular library
+
+examples/LUA.hs
+  The Lua code example shown in the paper.
Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple-main = defaultMain+import Distribution.Simple
+main = defaultMain
examples/Datatypes.hs view
@@ -1,81 +1,81 @@--module Datatypes (-  Tree (..), exTree1, exTree2, exTree3, exTree4, exTree5,-  List (..), toL, fromL, exLst1, exLst2,-  X (..), exX1, exX2,-  Zig (..), Zag (..), zigzag, zigzag2,-  Expr, prog1, prog2, prog3, prog4, prog5, prog6,-  module Lang-  ) where--import Lang-import Data.List ( unfoldr )------------------------------------------------------------------------------------- Example datatypes-------------------------------------------------------------------------------------Trees-data Tree = Leaf Int | Bin Tree Tree deriving (Show, Eq)--exTree1, exTree2, exTree3, exTree4, exTree5 :: Tree-exTree1 = Bin (Leaf 0) (Leaf 1)-exTree2 = Bin (Leaf 1) (Leaf 0)-exTree3 = Bin (Leaf 2) (Leaf 3)-exTree4 = Bin exTree2 exTree3-exTree5 = Bin exTree3 exTree4---- Lists-data List a = Nil | Cons a (List a) deriving (Eq, Show)--toL :: [a] -> List a-toL = foldr Cons Nil--fromL :: List a -> [a]-fromL = unfoldr f where-  f Nil        = Nothing-  f (Cons h t) = Just (h,t)--exLst1, exLst2 :: List Int-exLst1 = Cons 1 $ Cons 2 $ Cons 3 $ Cons 4 Nil-exLst2 = Cons 4 $ Cons 2 $ Cons 3 $ Cons 1 Nil---- Something more exotic-data X = XA X | XB X | XC X X | XD Int deriving (Show, Eq)--exX1, exX2 :: X-exX1 = XA (XA (XA (XC (XD 1) (XD 2))))-exX2 = XA (XB (XA (XA (XB (XC (XD 2) (XD 1))))))---- Mutually recursive-data Zig = Zig1 Zig | Zig2 Zag | Zig3 deriving (Show, Eq)-data Zag = Zag1 Zag | Zag2 Zig | Zag3 deriving (Show, Eq)--zigzag :: Zig-zigzag = Zig1 (Zig2 (Zag2 Zig3))--zigzag2 :: Zig-zigzag2 = Zig2 (Zag1 (Zag2 Zig3))---- Example from paper (imported from Lang)-type Expr = AExpr--progFragment1, progFragment2 :: String-progFragment1 =     "a := 1;"-                 ++ "b := a + 2;"-                 ++ "if b > 3"-                 ++ "then a := 2"-                 ++ "else b := 1;"-progFragment2 =     "a := 1;"-                 ++ "b := a + 2;"-                 ++ "if not b > 3"-                 ++ "then b := 1"-                 ++ "else a := 2;"--prog1, prog2, prog3, prog4, prog5, prog6 :: Stmt-prog1 = parseString . init . concat . replicate 1 $ progFragment1-prog2 = parseString . init . concat . replicate 1 $ progFragment2-prog3 = parseString . init . concat . replicate 4 $ progFragment1-prog4 = parseString . init . concat . replicate 4 $ progFragment2-prog5 = parseString . init . concat . replicate 5 $ progFragment1-prog6 = parseString . init . concat . replicate 5 $ progFragment2+
+module Datatypes (
+  Tree (..), exTree1, exTree2, exTree3, exTree4, exTree5,
+  List (..), toL, fromL, exLst1, exLst2,
+  X (..), exX1, exX2,
+  Zig (..), Zag (..), zigzag, zigzag2,
+  Expr, prog1, prog2, prog3, prog4, prog5, prog6,
+  module Lang
+  ) where
+
+import Lang
+import Data.List ( unfoldr )
+
+--------------------------------------------------------------------------------
+-- Example datatypes
+--------------------------------------------------------------------------------
+
+--Trees
+data Tree = Leaf Int | Bin Tree Tree deriving (Show, Eq)
+
+exTree1, exTree2, exTree3, exTree4, exTree5 :: Tree
+exTree1 = Bin (Leaf 0) (Leaf 1)
+exTree2 = Bin (Leaf 1) (Leaf 0)
+exTree3 = Bin (Leaf 2) (Leaf 3)
+exTree4 = Bin exTree2 exTree3
+exTree5 = Bin exTree3 exTree4
+
+-- Lists
+data List a = Nil | Cons a (List a) deriving (Eq, Show)
+
+toL :: [a] -> List a
+toL = foldr Cons Nil
+
+fromL :: List a -> [a]
+fromL = unfoldr f where
+  f Nil        = Nothing
+  f (Cons h t) = Just (h,t)
+
+exLst1, exLst2 :: List Int
+exLst1 = Cons 1 $ Cons 2 $ Cons 3 $ Cons 4 Nil
+exLst2 = Cons 4 $ Cons 2 $ Cons 3 $ Cons 1 Nil
+
+-- Something more exotic
+data X = XA X | XB X | XC X X | XD Int deriving (Show, Eq)
+
+exX1, exX2 :: X
+exX1 = XA (XA (XA (XC (XD 1) (XD 2))))
+exX2 = XA (XB (XA (XA (XB (XC (XD 2) (XD 1))))))
+
+-- Mutually recursive
+data Zig = Zig1 Zig | Zig2 Zag | Zig3 deriving (Show, Eq)
+data Zag = Zag1 Zag | Zag2 Zig | Zag3 deriving (Show, Eq)
+
+zigzag :: Zig
+zigzag = Zig1 (Zig2 (Zag2 Zig3))
+
+zigzag2 :: Zig
+zigzag2 = Zig2 (Zag1 (Zag2 Zig3))
+
+-- Example from paper (imported from Lang)
+type Expr = AExpr
+
+progFragment1, progFragment2 :: String
+progFragment1 =     "a := 1;"
+                 ++ "b := a + 2;"
+                 ++ "if b > 3"
+                 ++ "then a := 2"
+                 ++ "else b := 1;"
+progFragment2 =     "a := 1;"
+                 ++ "b := a + 2;"
+                 ++ "if not b > 3"
+                 ++ "then b := 1"
+                 ++ "else a := 2;"
+
+prog1, prog2, prog3, prog4, prog5, prog6 :: Stmt
+prog1 = parseString . init . concat . replicate 1 $ progFragment1
+prog2 = parseString . init . concat . replicate 1 $ progFragment2
+prog3 = parseString . init . concat . replicate 4 $ progFragment1
+prog4 = parseString . init . concat . replicate 4 $ progFragment2
+prog5 = parseString . init . concat . replicate 5 $ progFragment1
+prog6 = parseString . init . concat . replicate 5 $ progFragment2
+ examples/Expr.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE GADTs                 #-}
+{-# LANGUAGE KindSignatures        #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TypeFamilies          #-}
+{-# LANGUAGE TypeOperators         #-}
+{-# LANGUAGE TypeSynonymInstances  #-}
+{-# LANGUAGE EmptyDataDecls        #-}
+{-# LANGUAGE TemplateHaskell       #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE OverlappingInstances  #-}
+{-# LANGUAGE DataKinds             #-}
+{-# LANGUAGE PolyKinds             #-}
+{-# LANGUAGE TypeFamilies          #-}
+{-# LANGUAGE StandaloneDeriving    #-}
+{-# LANGUAGE PatternSynonyms       #-}
+
+module Main where
+
+import Generics.MultiRec hiding (show)
+import Generics.MultiRec.TH
+import Test.QuickCheck
+import Test.QuickCheck.Random
+import Control.Monad
+import Generics.MultiRec.Transformations.Main
+import Generics.MultiRec.Transformations.MemoTable
+
+data Expr  =  Var    String
+           |  Const  Int
+           |  Neg    Expr
+           |  Add    Expr Expr
+          deriving (Show, Ord, Eq)
+
+data ExprAST :: * -> * where
+  Expr :: ExprAST Expr
+
+type instance Ixs ExprAST = '[ Expr ]
+
+$(deriveAll ''ExprAST)
+
+instance Arbitrary Expr where
+  shrink (Var v)   = map Var   (shrink v)
+  shrink (Const v) = map Const (shrink v)
+  shrink (Neg l)   = [l]
+  shrink (Add l r) = [l,r]
+
+  arbitrary = sized expr'
+    where expr' 0 = oneof [liftM Var arbitrary, liftM Const arbitrary]
+          expr' n | n>0 =
+                oneof [ liftM Var arbitrary
+                      , liftM Const arbitrary
+                      , liftM Neg (expr' (pred n))
+                      , liftM2 Add subtree subtree ]
+            where subtree = expr' (n `div` 2)
+
+expr1, expr2, expr3 :: Expr
+expr1  =  Add (Const 1) (Var "a")
+expr2  =  Add (Const 1) (Neg (Var "a"))
+expr3  =  Add (Var "a") (Const 1)
+ examples/LUA.hs view
@@ -0,0 +1,715 @@+{-# LANGUAGE GADTs                 #-}
+{-# LANGUAGE KindSignatures        #-}
+{-# LANGUAGE TypeFamilies          #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE DataKinds             #-}
+{-# LANGUAGE StandaloneDeriving    #-}
+{-# LANGUAGE CPP                   #-}
+
+import Control.Monad
+import qualified Language.Lua as LUA -- from language-lua
+import Generics.MultiRec hiding (show)
+import Generics.MultiRec.Transformations.Main
+
+-- From https://github.com/leafo/lapis/commits/629c559094df41694bea0a33a2be75115c544dad/lapis/application.lua
+files :: [FilePath]
+files = ["examples/LUA_project/" ++ f
+        | f <- [ "application.lua." ++ show n | n <- [1..90] ] ]
+
+--------------------------------------------------------------------------------
+-- Main
+--------------------------------------------------------------------------------
+main :: IO ()
+main = do
+  putStrLn "Parsing"
+  psr <- forM files $ LUA.parseFile
+  let ps = [ (p,f) | (Right p,f) <- zip psr files ]
+  putStrLn "Diffing"
+  forM_ (zip ps (tail ps)) $ \((p1,f1),(p2,f2)) -> do
+    putStrLn $ f1 ++ " --> " ++ f2
+    let df = diff Block p1 p2
+    print $ length df
+    print $ apply Block p1 df == Just p2
+
+--------------------------------------------------------------------------------
+-- Multirec instance
+--------------------------------------------------------------------------------
+data AST :: * -> * where
+  Stat :: AST LUA.Stat
+  Exp :: AST LUA.Exp
+  Var :: AST LUA.Var
+  Binop :: AST LUA.Binop
+  Unop :: AST LUA.Unop
+  PrefixExp :: AST LUA.PrefixExp
+  TableField :: AST LUA.TableField
+  Block :: AST LUA.Block
+  FunName :: AST LUA.FunName
+  FunBody :: AST LUA.FunBody
+  FunCall :: AST LUA.FunCall
+  FunArg :: AST LUA.FunArg
+
+deriving instance Ord LUA.Stat
+deriving instance Ord LUA.Exp
+deriving instance Ord LUA.Var
+deriving instance Ord LUA.Binop
+deriving instance Ord LUA.Unop
+deriving instance Ord LUA.PrefixExp
+deriving instance Ord LUA.TableField
+deriving instance Ord LUA.Block
+deriving instance Ord LUA.FunName
+deriving instance Ord LUA.FunBody
+deriving instance Ord LUA.FunCall
+deriving instance Ord LUA.FunArg
+
+
+#if OLD_APPROACH
+instance Transform AST
+
+instance OrdI AST where
+  indexI Stat = 0
+  indexI Exp = 1
+  indexI Var = 2
+  indexI Binop = 3
+  indexI Unop = 4
+  indexI PrefixExp = 5
+  indexI TableField = 6
+  indexI Block = 7
+  indexI FunName = 8
+  indexI FunBody = 9
+  indexI FunCall = 10
+  indexI FunArg = 11
+#else
+type instance Ixs AST = '[ LUA.Stat, LUA.Exp, LUA.Var, LUA.Binop, LUA.Unop, LUA.PrefixExp, LUA.TableField, LUA.Block, LUA.FunName, LUA.FunBody, LUA.FunCall, LUA.FunArg ]
+#endif
+
+-- Automatically generated and manually edited
+data Assign
+data FunCall
+data Label
+data Break
+data Goto
+data Do
+data While
+data Repeat
+data If
+data ForRange
+data ForIn
+data FunAssign
+data LocalFunAssign
+data LocalAssign
+data EmptyStat
+instance Constructor Assign where
+  conName _ = "Assign"
+instance Constructor FunCall where
+  conName _ = "FunCall"
+instance Constructor Label where
+  conName _ = "Label"
+instance Constructor Break where
+  conName _ = "Break"
+instance Constructor Goto where
+  conName _ = "Goto"
+instance Constructor Do where
+  conName _ = "Do"
+instance Constructor While where
+  conName _ = "While"
+instance Constructor Repeat where
+  conName _ = "Repeat"
+instance Constructor If where
+  conName _ = "If"
+instance Constructor ForRange where
+  conName _ = "ForRange"
+instance Constructor ForIn where
+  conName _ = "ForIn"
+instance Constructor FunAssign where
+  conName _ = "FunAssign"
+instance Constructor LocalFunAssign where
+  conName _ = "LocalFunAssign"
+instance Constructor LocalAssign where
+  conName _ = "LocalAssign"
+instance Constructor EmptyStat where
+  conName _ = "EmptyStat"
+data Nil
+data LBool
+data Number
+data LString
+data Vararg
+data EFunDef
+data PrefixExp
+data TableConst
+data Binop
+data Unop
+instance Constructor Nil where
+  conName _ = "Nil"
+instance Constructor LBool where
+  conName _ = "Bool"
+instance Constructor Number where
+  conName _ = "Number"
+instance Constructor LString where
+  conName _ = "String"
+instance Constructor Vararg where
+  conName _ = "Vararg"
+instance Constructor EFunDef where
+  conName _ = "EFunDef"
+instance Constructor PrefixExp where
+  conName _ = "PrefixExp"
+instance Constructor TableConst where
+  conName _ = "TableConst"
+instance Constructor Binop where
+  conName _ = "Binop"
+instance Constructor Unop where
+  conName _ = "Unop"
+data VarName
+data Select
+data SelectName
+instance Constructor VarName where
+  conName _ = "VarName"
+instance Constructor Select where
+  conName _ = "Select"
+instance Constructor SelectName where
+  conName _ = "SelectName"
+data Add
+data Sub
+data Mul
+data Div
+data Exp
+data Mod
+data Concat
+data LT
+data LTE
+data GT
+data GTE
+data EQ
+data NEQ
+data And
+data Or
+instance Constructor Add where
+  conName _ = "Add"
+instance Constructor Sub where
+  conName _ = "Sub"
+instance Constructor Mul where
+  conName _ = "Mul"
+instance Constructor Div where
+  conName _ = "Div"
+instance Constructor Exp where
+  conName _ = "Exp"
+instance Constructor Mod where
+  conName _ = "Mod"
+instance Constructor Concat where
+  conName _ = "Concat"
+instance Constructor LT where
+  conName _ = "LT"
+instance Constructor LTE where
+  conName _ = "LTE"
+instance Constructor GT where
+  conName _ = "GT"
+instance Constructor GTE where
+  conName _ = "GTE"
+instance Constructor EQ where
+  conName _ = "EQ"
+instance Constructor NEQ where
+  conName _ = "NEQ"
+instance Constructor And where
+  conName _ = "And"
+instance Constructor Or where
+  conName _ = "Or"
+data Neg
+data Not
+data Len
+instance Constructor Neg where
+  conName _ = "Neg"
+instance Constructor Not where
+  conName _ = "Not"
+instance Constructor Len where
+  conName _ = "Len"
+data PEVar
+data PEFunCall
+data Paren
+instance Constructor PEVar where
+  conName _ = "PEVar"
+instance Constructor PEFunCall where
+  conName _ = "PEFunCall"
+instance Constructor Paren where
+  conName _ = "Paren"
+data ExpField
+data NamedField
+data Field
+instance Constructor ExpField where
+  conName _ = "ExpField"
+instance Constructor NamedField where
+  conName _ = "NamedField"
+instance Constructor Field where
+  conName _ = "Field"
+data Block
+instance Constructor Block where
+  conName _ = "Block"
+data FunName
+instance Constructor FunName where
+  conName _ = "FunName"
+data FunBody
+instance Constructor FunBody where
+  conName _ = "FunBody"
+data NormalFunCall
+data MethodCall
+instance Constructor NormalFunCall where
+  conName _ = "NormalFunCall"
+instance Constructor MethodCall where
+  conName _ = "MethodCall"
+data Args
+data TableArg
+data StringArg
+instance Constructor Args where
+  conName _ = "Args"
+instance Constructor TableArg where
+  conName _ = "TableArg"
+instance Constructor StringArg where
+  conName _ = "StringArg"
+
+type instance PF AST = (:+:) ((:>:)
+  ((:+:) (C Assign ((:*:) ((:.:) [] (I LUA.Var)) ((:.:) [] (I LUA.Exp))))
+  ((:+:) (C FunCall (I LUA.FunCall))
+  ((:+:) (C Label (K LUA.Name))
+  ((:+:) (C Break U)
+  ((:+:) (C Goto (K LUA.Name))
+  ((:+:) (C Do (I LUA.Block))
+  ((:+:) (C While ((:*:) (I LUA.Exp) (I LUA.Block)))
+  ((:+:) (C Repeat ((:*:) (I LUA.Block) (I LUA.Exp)))
+  ((:+:) (C If ((:*:) ((:.:) [] ((:*:) (I LUA.Exp) (I LUA.Block))) ((:.:) Maybe (I LUA.Block)))) -- Manually changed, is it correct?
+  ((:+:) (C ForRange ((:*:) (K LUA.Name) ((:*:) (I LUA.Exp) ((:*:) (I LUA.Exp) ((:*:) ((:.:) Maybe (I LUA.Exp)) (I LUA.Block))))))
+  ((:+:) (C ForIn ((:*:) ((:.:) [] (K LUA.Name)) ((:*:) ((:.:) [] (I LUA.Exp)) (I LUA.Block))))
+  ((:+:) (C FunAssign ((:*:) (I LUA.FunName) (I LUA.FunBody)))
+  ((:+:) (C LocalFunAssign ((:*:) (K LUA.Name) (I LUA.FunBody)))
+  ((:+:) (C LocalAssign ((:*:) ((:.:) [] (K LUA.Name)) ((:.:) Maybe ((:.:) [] (I LUA.Exp)))))
+   (C EmptyStat U))))))))))))))) LUA.Stat) ((:+:) ((:>:)
+  ((:+:) (C Nil U)
+  ((:+:) (C LBool (K Bool))
+  ((:+:) (C Number (K String))
+  ((:+:) (C LString (K String))
+  ((:+:) (C Vararg U)
+  ((:+:) (C EFunDef (I LUA.FunBody))
+  ((:+:) (C PrefixExp (I LUA.PrefixExp))
+  ((:+:) (C TableConst ((:.:) [] (I LUA.TableField)))
+  ((:+:) (C Binop ((:*:) (I LUA.Binop) ((:*:) (I LUA.Exp) (I LUA.Exp))))
+   (C Unop ((:*:) (I LUA.Unop) (I LUA.Exp)))))))))))) LUA.Exp) ((:+:) ((:>:)
+  ((:+:) (C VarName (K LUA.Name))
+  ((:+:) (C Select ((:*:) (I LUA.PrefixExp) (I LUA.Exp))) (C SelectName ((:*:) (I LUA.PrefixExp) (K LUA.Name))))) LUA.Var) ((:+:) ((:>:)
+  ((:+:) (C Add U)
+  ((:+:) (C Sub U)
+  ((:+:) (C Mul U)
+  ((:+:) (C Div U)
+  ((:+:) (C Exp U)
+  ((:+:) (C Mod U)
+  ((:+:) (C Concat U)
+  ((:+:) (C LT U)
+  ((:+:) (C LTE U)
+  ((:+:) (C GT U)
+  ((:+:) (C GTE U)
+  ((:+:) (C EQ U)
+  ((:+:) (C NEQ U)
+  ((:+:) (C And U) (C Or U))))))))))))))) LUA.Binop)
+  ((:+:) ((:>:)
+  ((:+:) (C Neg U)
+  ((:+:) (C Not U) (C Len U))) LUA.Unop)
+  ((:+:) ((:>:)
+  ((:+:) (C PEVar (I LUA.Var))
+  ((:+:) (C PEFunCall (I LUA.FunCall)) (C Paren (I LUA.Exp)))) LUA.PrefixExp)
+  ((:+:) ((:>:)
+  ((:+:) (C ExpField ((:*:) (I LUA.Exp) (I LUA.Exp)))
+  ((:+:) (C NamedField ((:*:) (K LUA.Name) (I LUA.Exp))) (C Field (I LUA.Exp)))) LUA.TableField)
+  ((:+:) ((:>:) (C Block ((:*:) ((:.:) [] (I LUA.Stat)) ((:.:) Maybe ((:.:) [] (I LUA.Exp))))) LUA.Block)
+  ((:+:) ((:>:) (C FunName ((:*:) (K LUA.Name) ((:*:) ((:.:) [] (K LUA.Name)) ((:.:) Maybe (K LUA.Name))))) LUA.FunName)
+  ((:+:) ((:>:) (C FunBody ((:*:) ((:.:) [] (K LUA.Name)) ((:*:) (K Bool) (I LUA.Block)))) LUA.FunBody)
+  ((:+:) ((:>:)
+  ((:+:) (C NormalFunCall ((:*:) (I LUA.PrefixExp) (I LUA.FunArg))) (C MethodCall ((:*:) (I LUA.PrefixExp) ((:*:) (K LUA.Name) (I LUA.FunArg))))) LUA.FunCall) ((:>:)
+  ((:+:) (C Args ((:.:) [] (I LUA.Exp)))
+  ((:+:) (C TableArg ((:.:) [] (I LUA.TableField))) (C StringArg (K String)))) LUA.FunArg)))))))))))
+
+instance El AST LUA.Stat where
+  proof = Stat
+instance El AST LUA.Exp where
+  proof = Exp
+instance El AST LUA.Var where
+  proof = Var
+instance El AST LUA.Binop where
+  proof = Binop
+instance El AST LUA.Unop where
+  proof = Unop
+instance El AST LUA.PrefixExp where
+  proof = PrefixExp
+instance El AST LUA.TableField where
+  proof = TableField
+instance El AST LUA.Block where
+  proof = Block
+instance El AST LUA.FunName where
+  proof = FunName
+instance El AST LUA.FunBody where
+  proof = FunBody
+instance El AST LUA.FunCall where
+  proof = FunCall
+instance El AST LUA.FunArg where
+  proof = FunArg
+instance Fam AST where
+  from Stat (LUA.Assign f0 f1)
+    = L (Tag
+           (L (C ((:*:)
+                    ((D . (fmap (I . I0))) f0) ((D . (fmap (I . I0))) f1)))))
+  from Stat (LUA.FunCall f0)
+    = L (Tag (R (L (C ((I . I0) f0)))))
+  from Stat (LUA.Label f0) = L (Tag (R (R (L (C (K f0))))))
+  from Stat LUA.Break = L (Tag (R (R (R (L (C U))))))
+  from Stat (LUA.Goto f0) = L (Tag (R (R (R (R (L (C (K f0))))))))
+  from Stat (LUA.Do f0)
+    = L (Tag (R (R (R (R (R (L (C ((I . I0) f0)))))))))
+  from Stat (LUA.While f0 f1)
+    = L (Tag
+           (R (R (R (R (R (R (L (C ((:*:)
+                                      ((I . I0) f0) ((I . I0) f1)))))))))))
+  from Stat (LUA.Repeat f0 f1)
+    = L (Tag
+           (R (R (R (R (R (R (R (L (C ((:*:)
+                                         ((I . I0) f0) ((I . I0) f1))))))))))))
+  from Stat (LUA.If f0 f1)
+    = L (Tag
+           (R (R (R (R (R (R (R (R (L (C ((:*:)
+                                            (D [((I . I0) e :*: (I . I0) b) | (e, b) <- f0])
+                                            ((D . (fmap (I . I0))) f1)))))))))))))
+  from Stat (LUA.ForRange f0 f1 f2 f3 f4)
+    = L (Tag
+           (R (R (R (R (R (R (R (R (R (L (C ((:*:)
+                                               (K f0)
+                                               ((:*:)
+                                                  ((I . I0) f1)
+                                                  ((:*:)
+                                                     ((I . I0) f2)
+                                                     ((:*:)
+                                                        ((D . (fmap (I . I0))) f3)
+                                                        ((I . I0) f4)))))))))))))))))
+  from Stat (LUA.ForIn f0 f1 f2)
+    = L (Tag
+           (R (R (R (R (R (R (R (R (R (R (L (C ((:*:)
+                                                  ((D . (fmap K)) f0)
+                                                  ((:*:)
+                                                     ((D . (fmap (I . I0))) f1)
+                                                     ((I . I0) f2))))))))))))))))
+  from Stat (LUA.FunAssign f0 f1)
+    = L (Tag
+           (R (R (R (R (R (R (R (R (R (R (R (L (C ((:*:)
+                                                     ((I . I0) f0) ((I . I0) f1))))))))))))))))
+  from Stat (LUA.LocalFunAssign f0 f1)
+    = L (Tag
+           (R (R (R (R (R (R (R (R (R (R (R (R (L (C ((:*:)
+                                                        (K f0) ((I . I0) f1)))))))))))))))))
+  from Stat (LUA.LocalAssign f0 f1)
+    = L (Tag
+           (R (R (R (R (R (R (R (R (R (R (R (R (R (L (C ((:*:)
+                                                           ((D . (fmap K)) f0)
+                                                           ((D . (fmap (D . (fmap (I . I0)))))
+                                                              f1))))))))))))))))))
+  from Stat LUA.EmptyStat
+    = L (Tag
+           (R (R (R (R (R (R (R (R (R (R (R (R (R (R (C U))))))))))))))))
+  from Exp LUA.Nil = R (L (Tag (L (C U))))
+  from Exp (LUA.Bool f0) = R (L (Tag (R (L (C (K f0))))))
+  from Exp (LUA.Number f0) = R (L (Tag (R (R (L (C (K f0)))))))
+  from Exp (LUA.String f0) = R (L (Tag (R (R (R (L (C (K f0))))))))
+  from Exp LUA.Vararg = R (L (Tag (R (R (R (R (L (C U))))))))
+  from Exp (LUA.EFunDef f0)
+    = R (L (Tag (R (R (R (R (R (L (C ((I . I0) f0))))))))))
+  from Exp (LUA.PrefixExp f0)
+    = R (L (Tag (R (R (R (R (R (R (L (C ((I . I0) f0)))))))))))
+  from Exp (LUA.TableConst f0)
+    = R (L (Tag
+              (R (R (R (R (R (R (R (L (C ((D . (fmap (I . I0))) f0))))))))))))
+  from Exp (LUA.Binop f0 f1 f2)
+    = R (L (Tag
+              (R (R (R (R (R (R (R (R (L (C ((:*:)
+                                               ((I . I0) f0)
+                                               ((:*:) ((I . I0) f1) ((I . I0) f2)))))))))))))))
+  from Exp (LUA.Unop f0 f1)
+    = R (L (Tag
+              (R (R (R (R (R (R (R (R (R (C ((:*:)
+                                               ((I . I0) f0) ((I . I0) f1))))))))))))))
+  from Var (LUA.VarName f0) = R (R (L (Tag (L (C (K f0))))))
+  from Var (LUA.Select f0 f1)
+    = R (R (L (Tag (R (L (C ((:*:) ((I . I0) f0) ((I . I0) f1))))))))
+  from Var (LUA.SelectName f0 f1)
+    = R (R (L (Tag (R (R (C ((:*:) ((I . I0) f0) (K f1))))))))
+  from Binop LUA.Add = R (R (R (L (Tag (L (C U))))))
+  from Binop LUA.Sub = R (R (R (L (Tag (R (L (C U)))))))
+  from Binop LUA.Mul = R (R (R (L (Tag (R (R (L (C U))))))))
+  from Binop LUA.Div = R (R (R (L (Tag (R (R (R (L (C U)))))))))
+  from Binop LUA.Exp
+    = R (R (R (L (Tag (R (R (R (R (L (C U))))))))))
+  from Binop LUA.Mod = R (R (R (L (Tag (R (R (R (R (R (L (C U)))))))))))
+  from Binop LUA.Concat
+    = R (R (R (L (Tag (R (R (R (R (R (R (L (C U))))))))))))
+  from Binop LUA.LT
+    = R (R (R (L (Tag (R (R (R (R (R (R (R (L (C U)))))))))))))
+  from Binop LUA.LTE
+    = R (R (R (L (Tag (R (R (R (R (R (R (R (R (L (C U))))))))))))))
+  from Binop LUA.GT
+    = R (R (R (L (Tag (R (R (R (R (R (R (R (R (R (L (C U)))))))))))))))
+  from Binop LUA.GTE
+    = R (R (R (L (Tag
+                    (R (R (R (R (R (R (R (R (R (R (L (C U))))))))))))))))
+  from Binop LUA.EQ
+    = R (R (R (L (Tag
+                    (R (R (R (R (R (R (R (R (R (R (R (L (C U)))))))))))))))))
+  from Binop LUA.NEQ
+    = R (R (R (L (Tag
+                    (R (R (R (R (R (R (R (R (R (R (R (R (L (C U))))))))))))))))))
+  from Binop LUA.And
+    = R (R (R (L (Tag
+                    (R (R (R (R (R (R (R (R (R (R (R (R (R (L (C U)))))))))))))))))))
+  from Binop LUA.Or
+    = R (R (R (L (Tag
+                    (R (R (R (R (R (R (R (R (R (R (R (R (R (R (C U)))))))))))))))))))
+  from Unop LUA.Neg = R (R (R (R (L (Tag (L (C U)))))))
+  from Unop LUA.Not = R (R (R (R (L (Tag (R (L (C U))))))))
+  from Unop LUA.Len = R (R (R (R (L (Tag (R (R (C U))))))))
+  from PrefixExp (LUA.PEVar f0)
+    = R (R (R (R (R (L (Tag (L (C ((I . I0) f0)))))))))
+  from PrefixExp (LUA.PEFunCall f0)
+    = R (R (R (R (R (L (Tag (R (L (C ((I . I0) f0))))))))))
+  from PrefixExp (LUA.Paren f0)
+    = R (R (R (R (R (L (Tag (R (R (C ((I . I0) f0))))))))))
+  from TableField (LUA.ExpField f0 f1)
+    = R (R (R (R (R (R (L (Tag
+                             (L (C ((:*:) ((I . I0) f0) ((I . I0) f1)))))))))))
+  from TableField (LUA.NamedField f0 f1)
+    = R (R (R (R (R (R (L (Tag
+                             (R (L (C ((:*:) (K f0) ((I . I0) f1))))))))))))
+  from TableField (LUA.Field f0)
+    = R (R (R (R (R (R (L (Tag (R (R (C ((I . I0) f0)))))))))))
+  from Block (LUA.Block f0 f1)
+    = R (R (R (R (R (R (R (L (Tag
+                                (C ((:*:)
+                                      ((D . (fmap (I . I0))) f0)
+                                      ((D . (fmap (D . (fmap (I . I0))))) f1)))))))))))
+  from FunName (LUA.FunName f0 f1 f2)
+    = R (R (R (R (R (R (R (R (L (Tag
+                                   (C ((:*:)
+                                         (K f0)
+                                         ((:*:)
+                                            ((D . (fmap K)) f1) ((D . (fmap K)) f2)))))))))))))
+  from FunBody (LUA.FunBody f0 f1 f2)
+    = R (R (R (R (R (R (R (R (R (L (Tag
+                                      (C ((:*:)
+                                            ((D . (fmap K)) f0)
+                                            ((:*:) (K f1) ((I . I0) f2))))))))))))))
+  from FunCall (LUA.NormalFunCall f0 f1)
+    = R (R (R (R (R (R (R (R (R (R (L (Tag
+                                         (L (C ((:*:) ((I . I0) f0) ((I . I0) f1)))))))))))))))
+  from FunCall (LUA.MethodCall f0 f1 f2)
+    = R (R (R (R (R (R (R (R (R (R (L (Tag
+                                         (R (C ((:*:)
+                                                  ((I . I0) f0)
+                                                  ((:*:) (K f1) ((I . I0) f2))))))))))))))))
+  from FunArg (LUA.Args f0)
+    = R (R (R (R (R (R (R (R (R (R (R (Tag
+                                         (L (C ((D . (fmap (I . I0))) f0))))))))))))))
+  from FunArg (LUA.TableArg f0)
+    = R (R (R (R (R (R (R (R (R (R (R (Tag
+                                         (R (L (C ((D . (fmap (I . I0))) f0)))))))))))))))
+  from FunArg (LUA.StringArg f0)
+    = R (R (R (R (R (R (R (R (R (R (R (Tag
+                                         (R (R (C (K f0)))))))))))))))
+  to Stat (L (Tag (L (C ((:*:) f0 f1)))))
+    = LUA.Assign
+        (((fmap (unI0 . unI)) . unD) f0) (((fmap (unI0 . unI)) . unD) f1)
+  to Stat (L (Tag (R (L (C f0)))))
+    = LUA.FunCall ((unI0 . unI) f0)
+  to Stat (L (Tag (R (R (L (C f0)))))) = LUA.Label (unK f0)
+  to Stat (L (Tag (R (R (R (L (C U))))))) = LUA.Break
+  to Stat (L (Tag (R (R (R (R (L (C f0)))))))) = LUA.Goto (unK f0)
+  to Stat (L (Tag (R (R (R (R (R (L (C f0)))))))))
+    = LUA.Do ((unI0 . unI) f0)
+  to Stat (L (Tag (R (R (R (R (R (R (L (C ((:*:) f0 f1)))))))))))
+    = LUA.While ((unI0 . unI) f0) ((unI0 . unI) f1)
+  to Stat (L (Tag (R (R (R (R (R (R (R (L (C ((:*:) f0 f1))))))))))))
+    = LUA.Repeat ((unI0 . unI) f0) ((unI0 . unI) f1)
+  to
+    Stat
+    (L (Tag (R (R (R (R (R (R (R (R (L (C ((:*:) f0 f1)))))))))))))
+    = LUA.If
+        [((unI0 . unI) e, (unI0 . unI) b)  | (e :*: b) <- unD f0]
+        (((fmap (unI0 . unI)) . unD) f1)
+  to
+    Stat
+    (L (Tag (R (R (R (R (R (R (R (R (R (L (C ((:*:) f0
+                                                    ((:*:) f1
+                                                           ((:*:) f2
+                                                                  ((:*:) f3 f4)))))))))))))))))
+    = LUA.ForRange
+        (unK f0)
+        ((unI0 . unI) f1)
+        ((unI0 . unI) f2)
+        (((fmap (unI0 . unI)) . unD) f3)
+        ((unI0 . unI) f4)
+  to
+    Stat
+    (L (Tag (R (R (R (R (R (R (R (R (R (R (L (C ((:*:) f0
+                                                       ((:*:) f1 f2))))))))))))))))
+    = LUA.ForIn
+        (((fmap unK) . unD) f0)
+        (((fmap (unI0 . unI)) . unD) f1)
+        ((unI0 . unI) f2)
+  to
+    Stat
+    (L (Tag (R (R (R (R (R (R (R (R (R (R (R (L (C ((:*:) f0
+                                                          f1))))))))))))))))
+    = LUA.FunAssign ((unI0 . unI) f0) ((unI0 . unI) f1)
+  to
+    Stat
+    (L (Tag (R (R (R (R (R (R (R (R (R (R (R (R (L (C ((:*:) f0
+                                                             f1)))))))))))))))))
+    = LUA.LocalFunAssign (unK f0) ((unI0 . unI) f1)
+  to
+    Stat
+    (L (Tag (R (R (R (R (R (R (R (R (R (R (R (R (R (L (C ((:*:) f0
+                                                                f1))))))))))))))))))
+    = LUA.LocalAssign
+        (((fmap unK) . unD) f0)
+        (((fmap ((fmap (unI0 . unI)) . unD)) . unD) f1)
+  to
+    Stat
+    (L (Tag (R (R (R (R (R (R (R (R (R (R (R (R (R (R (C U)))))))))))))))))
+    = LUA.EmptyStat
+  to Exp (R (L (Tag (L (C U))))) = LUA.Nil
+  to Exp (R (L (Tag (R (L (C f0)))))) = LUA.Bool (unK f0)
+  to Exp (R (L (Tag (R (R (L (C f0))))))) = LUA.Number (unK f0)
+  to Exp (R (L (Tag (R (R (R (L (C f0)))))))) = LUA.String (unK f0)
+  to Exp (R (L (Tag (R (R (R (R (L (C U))))))))) = LUA.Vararg
+  to Exp (R (L (Tag (R (R (R (R (R (L (C f0))))))))))
+    = LUA.EFunDef ((unI0 . unI) f0)
+  to Exp (R (L (Tag (R (R (R (R (R (R (L (C f0)))))))))))
+    = LUA.PrefixExp ((unI0 . unI) f0)
+  to Exp (R (L (Tag (R (R (R (R (R (R (R (L (C f0))))))))))))
+    = LUA.TableConst (((fmap (unI0 . unI)) . unD) f0)
+  to
+    Exp
+    (R (L (Tag (R (R (R (R (R (R (R (R (L (C ((:*:) f0
+                                                    ((:*:) f1 f2)))))))))))))))
+    = LUA.Binop
+        ((unI0 . unI) f0) ((unI0 . unI) f1) ((unI0 . unI) f2)
+  to
+    Exp
+    (R (L (Tag (R (R (R (R (R (R (R (R (R (C ((:*:) f0 f1))))))))))))))
+    = LUA.Unop ((unI0 . unI) f0) ((unI0 . unI) f1)
+  to Var (R (R (L (Tag (L (C f0)))))) = LUA.VarName (unK f0)
+  to Var (R (R (L (Tag (R (L (C ((:*:) f0 f1))))))))
+    = LUA.Select ((unI0 . unI) f0) ((unI0 . unI) f1)
+  to Var (R (R (L (Tag (R (R (C ((:*:) f0 f1))))))))
+    = LUA.SelectName ((unI0 . unI) f0) (unK f1)
+  to Binop (R (R (R (L (Tag (L (C U))))))) = LUA.Add
+  to Binop (R (R (R (L (Tag (R (L (C U)))))))) = LUA.Sub
+  to Binop (R (R (R (L (Tag (R (R (L (C U))))))))) = LUA.Mul
+  to Binop (R (R (R (L (Tag (R (R (R (L (C U)))))))))) = LUA.Div
+  to Binop (R (R (R (L (Tag (R (R (R (R (L (C U)))))))))))
+    = LUA.Exp
+  to Binop (R (R (R (L (Tag (R (R (R (R (R (L (C U)))))))))))) = LUA.Mod
+  to Binop (R (R (R (L (Tag (R (R (R (R (R (R (L (C U)))))))))))))
+    = LUA.Concat
+  to
+    Binop
+    (R (R (R (L (Tag (R (R (R (R (R (R (R (L (C U))))))))))))))
+    = LUA.LT
+  to
+    Binop
+    (R (R (R (L (Tag (R (R (R (R (R (R (R (R (L (C U)))))))))))))))
+    = LUA.LTE
+  to
+    Binop
+    (R (R (R (L (Tag (R (R (R (R (R (R (R (R (R (L (C U))))))))))))))))
+    = LUA.GT
+  to
+    Binop
+    (R (R (R (L (Tag (R (R (R (R (R (R (R (R (R (R (L (C U)))))))))))))))))
+    = LUA.GTE
+  to
+    Binop
+    (R (R (R (L (Tag (R (R (R (R (R (R (R (R (R (R (R (L (C U))))))))))))))))))
+    = LUA.EQ
+  to
+    Binop
+    (R (R (R (L (Tag (R (R (R (R (R (R (R (R (R (R (R (R (L (C U)))))))))))))))))))
+    = LUA.NEQ
+  to
+    Binop
+    (R (R (R (L (Tag (R (R (R (R (R (R (R (R (R (R (R (R (R (L (C U))))))))))))))))))))
+    = LUA.And
+  to
+    Binop
+    (R (R (R (L (Tag (R (R (R (R (R (R (R (R (R (R (R (R (R (R (C U))))))))))))))))))))
+    = LUA.Or
+  to Unop (R (R (R (R (L (Tag (L (C U)))))))) = LUA.Neg
+  to Unop (R (R (R (R (L (Tag (R (L (C U))))))))) = LUA.Not
+  to Unop (R (R (R (R (L (Tag (R (R (C U))))))))) = LUA.Len
+  to PrefixExp (R (R (R (R (R (L (Tag (L (C f0)))))))))
+    = LUA.PEVar ((unI0 . unI) f0)
+  to PrefixExp (R (R (R (R (R (L (Tag (R (L (C f0))))))))))
+    = LUA.PEFunCall ((unI0 . unI) f0)
+  to PrefixExp (R (R (R (R (R (L (Tag (R (R (C f0))))))))))
+    = LUA.Paren ((unI0 . unI) f0)
+  to
+    TableField
+    (R (R (R (R (R (R (L (Tag (L (C ((:*:) f0 f1)))))))))))
+    = LUA.ExpField ((unI0 . unI) f0) ((unI0 . unI) f1)
+  to
+    TableField
+    (R (R (R (R (R (R (L (Tag (R (L (C ((:*:) f0 f1))))))))))))
+    = LUA.NamedField (unK f0) ((unI0 . unI) f1)
+  to TableField (R (R (R (R (R (R (L (Tag (R (R (C f0)))))))))))
+    = LUA.Field ((unI0 . unI) f0)
+  to Block (R (R (R (R (R (R (R (L (Tag (C ((:*:) f0 f1)))))))))))
+    = LUA.Block
+        (((fmap (unI0 . unI)) . unD) f0)
+        (((fmap ((fmap (unI0 . unI)) . unD)) . unD) f1)
+  to
+    FunName
+    (R (R (R (R (R (R (R (R (L (Tag (C ((:*:) f0
+                                              ((:*:) f1 f2)))))))))))))
+    = LUA.FunName
+        (unK f0) (((fmap unK) . unD) f1) (((fmap unK) . unD) f2)
+  to
+    FunBody
+    (R (R (R (R (R (R (R (R (R (L (Tag (C ((:*:) f0
+                                                 ((:*:) f1 f2))))))))))))))
+    = LUA.FunBody
+        (((fmap unK) . unD) f0) (unK f1) ((unI0 . unI) f2)
+  to
+    FunCall
+    (R (R (R (R (R (R (R (R (R (R (L (Tag (L (C ((:*:) f0
+                                                       f1)))))))))))))))
+    = LUA.NormalFunCall ((unI0 . unI) f0) ((unI0 . unI) f1)
+  to
+    FunCall
+    (R (R (R (R (R (R (R (R (R (R (L (Tag (R (C ((:*:) f0
+                                                       ((:*:) f1 f2))))))))))))))))
+    = LUA.MethodCall ((unI0 . unI) f0) (unK f1) ((unI0 . unI) f2)
+  to
+    FunArg
+    (R (R (R (R (R (R (R (R (R (R (R (Tag (L (C f0))))))))))))))
+    = LUA.Args (((fmap (unI0 . unI)) . unD) f0)
+  to
+    FunArg
+    (R (R (R (R (R (R (R (R (R (R (R (Tag (R (L (C f0)))))))))))))))
+    = LUA.TableArg (((fmap (unI0 . unI)) . unD) f0)
+  to
+    FunArg
+    (R (R (R (R (R (R (R (R (R (R (R (Tag (R (R (C f0)))))))))))))))
+    = LUA.StringArg (unK f0)
+
+instance EqS AST where
+  eqS Stat Stat = Just Refl
+  eqS Exp Exp = Just Refl
+  eqS Var Var = Just Refl
+  eqS Binop Binop = Just Refl
+  eqS Unop Unop = Just Refl
+  eqS PrefixExp PrefixExp = Just Refl
+  eqS TableField TableField = Just Refl
+  eqS Block Block = Just Refl
+  eqS FunName FunName = Just Refl
+  eqS FunBody FunBody = Just Refl
+  eqS FunCall FunCall = Just Refl
+  eqS FunArg FunArg = Just Refl
+  eqS _ _ = Nothing
+ examples/LUA_project/application.lua.1 view
@@ -0,0 +1,733 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local lapis_config = require("lapis.config")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local increment_perf
+do
+  local _obj_0 = require("lapis.nginx.context")
+  increment_perf = _obj_0.increment_perf
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local insert
+do
+  local _obj_0 = table
+  insert = _obj_0.insert
+end
+local json = require("cjson")
+local capture_errors, capture_errors_json, respond_to
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-Type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-Type"] = ct
+        end
+      end
+      if not self.res.headers["Content-Type"] then
+        self.res.headers["Content-Type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+          return ""
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      local widget = self.options.render
+      if widget == true then
+        widget = self.route_name
+      end
+      local config = lapis_config.get()
+      if widget then
+        if type(widget) == "string" then
+          widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+        end
+        local start_time
+        if config.measure_performance then
+          ngx.update_time()
+          start_time = ngx.now()
+        end
+        local view = widget(self.options.locals)
+        if self.layout_opts then
+          self.layout_opts.view_widget = view
+        end
+        view:include_helper(self)
+        self:write(view)
+        if start_time then
+          ngx.update_time()
+          increment_perf("view_time", ngx.now() - start_time)
+        end
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local start_time
+        if config.measure_performance then
+          ngx.update_time()
+          start_time = ngx.now()
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+        if start_time then
+          ngx.update_time()
+          increment_perf("layout_time", ngx.now() - start_time)
+        end
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+        do
+          local extra = self.app.cookie_attributes(self, k, v)
+          if extra then
+            cookie = cookie .. ("; " .. extra)
+          end
+        end
+        self.res:add_header("Set-Cookie", cookie)
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    enable = function(self, feature)
+      local fn = require("lapis.features." .. tostring(feature))
+      if type(fn) == "function" then
+        return fn(self)
+      end
+    end,
+    match = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.ordered_routes = self.ordered_routes or { }
+      local key
+      if route_name then
+        local tuple = self.ordered_routes[route_name]
+        do
+          local old_path = tuple and tuple[next(tuple)]
+          if old_path then
+            if old_path ~= path then
+              error("named route mismatch (" .. tostring(old_path) .. " != " .. tostring(path) .. ")")
+            end
+          end
+        end
+        if tuple then
+          key = tuple
+        else
+          tuple = {
+            [route_name] = path
+          }
+          self.ordered_routes[route_name] = tuple
+          key = tuple
+        end
+      else
+        key = path
+      end
+      if not (self[key]) then
+        insert(self.ordered_routes, key)
+      end
+      self[key] = handler
+      self.router = nil
+      return handler
+    end,
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_route
+      add_route = function(path, handler)
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          return self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          add_route(path, handler)
+        end
+        do
+          local ordered = self.ordered_routes
+          if ordered then
+            for _index_0 = 1, #ordered do
+              local path = ordered[_index_0]
+              add_route(path, self[path])
+            end
+          else
+            for path, handler in pairs(self) do
+              add_route(path, handler)
+            end
+          end
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          handler({ }, nil, "default_route", r)
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace, error_page)
+      if error_page == nil then
+        error_page = self.app.error_page
+      end
+      local r = self.app.Request(self, self.req, self.res)
+      local config = lapis_config.get()
+      if config._name == "test" then
+        local param_dump = logger.flatten_params(self.url_params)
+        r.res:add_header("X-Lapis-Error", "true")
+        r:write({
+          status = 500,
+          json = {
+            status = "[" .. tostring(r.req.cmd_mth) .. "] " .. tostring(r.req.cmd_url) .. " " .. tostring(param_dump),
+            err = err,
+            trace = trace
+          }
+        })
+      else
+        r:write({
+          status = 500,
+          layout = false,
+          content_type = "text/html",
+          error_page({
+            status = 500,
+            err = err,
+            trace = trace
+          })
+        })
+      end
+      r:render()
+      logger.request(r)
+      return r
+    end,
+    cookie_attributes = function(self, name, value)
+      return "Path=/; HttpOnly"
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  local _list_0 = {
+    "get",
+    "post",
+    "delete",
+    "put"
+  }
+  for _index_0 = 1, #_list_0 do
+    local meth = _list_0[_index_0]
+    local upper_meth = meth:upper()
+    self.__base[meth] = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.responders = self.responders or { }
+      local existing = self.responders[route_name or path]
+      local tbl = {
+        [upper_meth] = handler
+      }
+      if existing then
+        setmetatable(tbl, {
+          __index = function(self, key)
+            if key:match("%u") then
+              return existing
+            end
+          end
+        })
+      end
+      local responder = respond_to(tbl)
+      self.responders[route_name or path] = responder
+      return self:match(route_name, path, responder)
+    end
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out, tbl
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error or error_response
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg, ...)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing, msg, ...
+end
+local json_params
+json_params = function(fn)
+  return function(self, ...)
+    do
+      local content_type = self.req.headers["content-type"]
+      if content_type then
+        if string.find(content_type:lower(), "application/json", nil, true) then
+          local obj
+          pcall(function()
+            local err
+            obj, err = json.decode(ngx.req.get_body_data())
+          end)
+          if obj then
+            self:add_params(obj, "json")
+          end
+        end
+      end
+    end
+    return fn(self, ...)
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  json_params = json_params,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.10 view
@@ -0,0 +1,655 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local json = require("cjson")
+local capture_errors, capture_errors_json, respond_to
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      local widget = self.options.render
+      if widget == true then
+        widget = self.route_name
+      end
+      if widget then
+        if type(widget) == "string" then
+          widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+        end
+        local view = widget(self.options.locals)
+        if self.layout_opts then
+          self.layout_opts.view_widget = view
+        end
+        view:include_helper(self)
+        self:write(view)
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-cookie", cookie)
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    enable = function(self, feature)
+      local fn = require("lapis.features." .. tostring(feature))
+      return fn(self)
+    end,
+    match = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      local key
+      if route_name then
+        key = {
+          [route_name] = path
+        }
+      else
+        key = path
+      end
+      self[key] = handler
+      self.router = nil
+      return handler
+    end,
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_route
+      add_route = function(path, handler)
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          return self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          add_route(path, handler)
+        end
+        for path, handler in pairs(self) do
+          add_route(path, handler)
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          handler({ }, nil, "default_route", r)
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace, error_page)
+      if error_page == nil then
+        error_page = self.app.error_page
+      end
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  local _list_0 = {
+    "get",
+    "post",
+    "delete",
+    "put"
+  }
+  for _index_0 = 1, #_list_0 do
+    local meth = _list_0[_index_0]
+    local upper_meth = meth:upper()
+    self.__base[meth] = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.responders = self.responders or { }
+      local existing = self.responders[route_name or path]
+      local tbl = {
+        [upper_meth] = handler
+      }
+      if existing then
+        setmetatable(tbl, {
+          __index = function(self, key)
+            if key:match("%u") then
+              return existing
+            end
+          end
+        })
+      end
+      local responder = respond_to(tbl)
+      self.responders[route_name or path] = responder
+      return self:match(route_name, path, responder)
+    end
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out, tbl
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error or error_response
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg, ...)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing, msg, ...
+end
+local json_params
+json_params = function(fn)
+  return function(self, ...)
+    do
+      local content_type = self.req.headers["content-type"]
+      if content_type then
+        if string.find(content_type:lower(), "application/json", nil, true) then
+          local obj
+          pcall(function()
+            local err
+            obj, err = json.decode(ngx.req.get_body_data())
+          end)
+          if obj then
+            self:add_params(obj, "json")
+          end
+        end
+      end
+    end
+    return fn(self, ...)
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  json_params = json_params,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.11 view
@@ -0,0 +1,655 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local json = require("cjson")
+local capture_errors, capture_errors_json, respond_to
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      local widget = self.options.render
+      if widget == true then
+        widget = self.route_name
+      end
+      if widget then
+        if type(widget) == "string" then
+          widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+        end
+        local view = widget(self.options.locals)
+        if self.layout_opts then
+          self.layout_opts.view_widget = view
+        end
+        view:include_helper(self)
+        self:write(view)
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-cookie", cookie)
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    enable = function(self, feature)
+      local fn = require("lapis.features." .. tostring(feature))
+      return fn(self)
+    end,
+    match = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      local key
+      if route_name then
+        key = {
+          [route_name] = path
+        }
+      else
+        key = path
+      end
+      self[key] = handler
+      self.router = nil
+      return handler
+    end,
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_route
+      add_route = function(path, handler)
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          return self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          add_route(path, handler)
+        end
+        for path, handler in pairs(self) do
+          add_route(path, handler)
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          handler({ }, nil, "default_route", r)
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace, error_page)
+      if error_page == nil then
+        error_page = self.app.error_page
+      end
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  local _list_0 = {
+    "get",
+    "post",
+    "delete",
+    "put"
+  }
+  for _index_0 = 1, #_list_0 do
+    local meth = _list_0[_index_0]
+    local upper_meth = meth:upper()
+    self.__base[meth] = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.responders = self.responders or { }
+      local existing = self.responders[route_name or path]
+      local tbl = {
+        [upper_meth] = handler
+      }
+      if existing then
+        setmetatable(tbl, {
+          __index = function(self, key)
+            if key:match("%u") then
+              return existing
+            end
+          end
+        })
+      end
+      local responder = respond_to(tbl)
+      self.responders[route_name or path] = responder
+      return self:match(route_name, path, responder)
+    end
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out, tbl
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error or error_response
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+local json_params
+json_params = function(fn)
+  return function(self, ...)
+    do
+      local content_type = self.req.headers["content-type"]
+      if content_type then
+        if string.find(content_type:lower(), "application/json", nil, true) then
+          local obj
+          pcall(function()
+            local err
+            obj, err = json.decode(ngx.req.get_body_data())
+          end)
+          if obj then
+            self:add_params(obj, "json")
+          end
+        end
+      end
+    end
+    return fn(self, ...)
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  json_params = json_params,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.12 view
@@ -0,0 +1,652 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local json = require("cjson")
+local capture_errors, capture_errors_json, respond_to
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      local widget = self.options.render
+      if widget == true then
+        widget = self.route_name
+      end
+      if widget then
+        if type(widget) == "string" then
+          widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+        end
+        local view = widget(self.options.locals)
+        view:include_helper(self)
+        self:write(view)
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-cookie", cookie)
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    enable = function(self, feature)
+      local fn = require("lapis.features." .. tostring(feature))
+      return fn(self)
+    end,
+    match = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      local key
+      if route_name then
+        key = {
+          [route_name] = path
+        }
+      else
+        key = path
+      end
+      self[key] = handler
+      self.router = nil
+      return handler
+    end,
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_route
+      add_route = function(path, handler)
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          return self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          add_route(path, handler)
+        end
+        for path, handler in pairs(self) do
+          add_route(path, handler)
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          handler({ }, nil, "default_route", r)
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace, error_page)
+      if error_page == nil then
+        error_page = self.app.error_page
+      end
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  local _list_0 = {
+    "get",
+    "post",
+    "delete",
+    "put"
+  }
+  for _index_0 = 1, #_list_0 do
+    local meth = _list_0[_index_0]
+    local upper_meth = meth:upper()
+    self.__base[meth] = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.responders = self.responders or { }
+      local existing = self.responders[route_name or path]
+      local tbl = {
+        [upper_meth] = handler
+      }
+      if existing then
+        setmetatable(tbl, {
+          __index = function(self, key)
+            if key:match("%u") then
+              return existing
+            end
+          end
+        })
+      end
+      local responder = respond_to(tbl)
+      self.responders[route_name or path] = responder
+      return self:match(route_name, path, responder)
+    end
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out, tbl
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error or error_response
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+local json_params
+json_params = function(fn)
+  return function(self, ...)
+    do
+      local content_type = self.req.headers["content-type"]
+      if content_type then
+        if string.find(content_type:lower(), "application/json", nil, true) then
+          local obj
+          pcall(function()
+            local err
+            obj, err = json.decode(ngx.req.get_body_data())
+          end)
+          if obj then
+            self:add_params(obj, "json")
+          end
+        end
+      end
+    end
+    return fn(self, ...)
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  json_params = json_params,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.13 view
@@ -0,0 +1,649 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local json = require("cjson")
+local capture_errors, capture_errors_json, respond_to
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      local widget = self.options.render
+      if widget == true then
+        widget = self.route_name
+      end
+      if widget then
+        if type(widget) == "string" then
+          widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+        end
+        local view = widget(self.options.locals)
+        view:include_helper(self)
+        self:write(view)
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-cookie", cookie)
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    enable = function(self, feature)
+      local fn = require("lapis.features." .. tostring(feature))
+      return fn(self)
+    end,
+    match = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      local key
+      if route_name then
+        key = {
+          [route_name] = path
+        }
+      else
+        key = path
+      end
+      self[key] = handler
+      self.router = nil
+      return handler
+    end,
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_route
+      add_route = function(path, handler)
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          return self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          add_route(path, handler)
+        end
+        for path, handler in pairs(self) do
+          add_route(path, handler)
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          handler({ }, nil, "default_route", r)
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  local _list_0 = {
+    "get",
+    "post",
+    "delete",
+    "put"
+  }
+  for _index_0 = 1, #_list_0 do
+    local meth = _list_0[_index_0]
+    local upper_meth = meth:upper()
+    self.__base[meth] = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.responders = self.responders or { }
+      local existing = self.responders[route_name or path]
+      local tbl = {
+        [upper_meth] = handler
+      }
+      if existing then
+        setmetatable(tbl, {
+          __index = function(self, key)
+            if key:match("%u") then
+              return existing
+            end
+          end
+        })
+      end
+      local responder = respond_to(tbl)
+      self.responders[route_name or path] = responder
+      return self:match(route_name, path, responder)
+    end
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out, tbl
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error or error_response
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+local json_params
+json_params = function(fn)
+  return function(self, ...)
+    do
+      local content_type = self.req.headers["content-type"]
+      if content_type then
+        if string.find(content_type:lower(), "application/json", nil, true) then
+          local obj
+          pcall(function()
+            local err
+            obj, err = json.decode(ngx.req.get_body_data())
+          end)
+          if obj then
+            self:add_params(obj, "json")
+          end
+        end
+      end
+    end
+    return fn(self, ...)
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  json_params = json_params,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.14 view
@@ -0,0 +1,645 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local json = require("cjson")
+local capture_errors, capture_errors_json, respond_to
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      local widget = self.options.render
+      if widget == true then
+        widget = self.route_name
+      end
+      if widget then
+        if type(widget) == "string" then
+          widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+        end
+        local view = widget(self.options.locals)
+        view:include_helper(self)
+        self:write(view)
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-cookie", cookie)
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    match = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      local key
+      if route_name then
+        key = {
+          [route_name] = path
+        }
+      else
+        key = path
+      end
+      self[key] = handler
+      self.router = nil
+      return handler
+    end,
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_route
+      add_route = function(path, handler)
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          return self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          add_route(path, handler)
+        end
+        for path, handler in pairs(self) do
+          add_route(path, handler)
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          handler({ }, nil, "default_route", r)
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  local _list_0 = {
+    "get",
+    "post",
+    "delete",
+    "put"
+  }
+  for _index_0 = 1, #_list_0 do
+    local meth = _list_0[_index_0]
+    local upper_meth = meth:upper()
+    self.__base[meth] = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.responders = self.responders or { }
+      local existing = self.responders[route_name or path]
+      local tbl = {
+        [upper_meth] = handler
+      }
+      if existing then
+        setmetatable(tbl, {
+          __index = function(self, key)
+            if key:match("%u") then
+              return existing
+            end
+          end
+        })
+      end
+      local responder = respond_to(tbl)
+      self.responders[route_name or path] = responder
+      return self:match(route_name, path, responder)
+    end
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out, tbl
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error or error_response
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+local json_params
+json_params = function(fn)
+  return function(self, ...)
+    do
+      local content_type = self.req.headers["content-type"]
+      if content_type then
+        if string.find(content_type:lower(), "application/json", nil, true) then
+          local obj
+          pcall(function()
+            local err
+            obj, err = json.decode(ngx.req.get_body_data())
+          end)
+          if obj then
+            self:add_params(obj, "json")
+          end
+        end
+      end
+    end
+    return fn(self, ...)
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  json_params = json_params,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.15 view
@@ -0,0 +1,659 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local json = require("cjson")
+local capture_errors, capture_errors_json, respond_to
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      local widget = self.options.render
+      if widget == true then
+        widget = self.route_name
+      end
+      if widget then
+        if type(widget) == "string" then
+          widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+        end
+        local view = widget(self.options.locals)
+        view:include_helper(self)
+        self:write(view)
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-cookie", cookie)
+      end
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    match = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      local key
+      if route_name then
+        key = {
+          [route_name] = path
+        }
+      else
+        key = path
+      end
+      self[key] = handler
+      self.router = nil
+      return handler
+    end,
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_route
+      add_route = function(path, handler)
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          return self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          add_route(path, handler)
+        end
+        for path, handler in pairs(self) do
+          add_route(path, handler)
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          handler({ }, nil, "default_route", r)
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  local _list_0 = {
+    "get",
+    "post",
+    "delete",
+    "put"
+  }
+  for _index_0 = 1, #_list_0 do
+    local meth = _list_0[_index_0]
+    local upper_meth = meth:upper()
+    self.__base[meth] = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.responders = self.responders or { }
+      local existing = self.responders[route_name or path]
+      local tbl = {
+        [upper_meth] = handler
+      }
+      if existing then
+        setmetatable(tbl, {
+          __index = function(self, key)
+            if key:match("%u") then
+              return existing
+            end
+          end
+        })
+      end
+      local responder = respond_to(tbl)
+      self.responders[route_name or path] = responder
+      return self:match(route_name, path, responder)
+    end
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out, tbl
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error or error_response
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+local json_params
+json_params = function(fn)
+  return function(self, ...)
+    do
+      local content_type = self.req.headers["content-type"]
+      if content_type then
+        if string.find(content_type:lower(), "application/json", nil, true) then
+          local obj
+          pcall(function()
+            local err
+            obj, err = json.decode(ngx.req.get_body_data())
+          end)
+          if obj then
+            self:add_params(obj, "json")
+          end
+        end
+      end
+    end
+    return fn(self, ...)
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  json_params = json_params,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.16 view
@@ -0,0 +1,659 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local json = require("cjson")
+local capture_errors, capture_errors_json, respond_to
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      local widget = self.options.render
+      if widget == true then
+        widget = self.route_name
+      end
+      if widget then
+        if type(widget) == "string" then
+          widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+        end
+        local view = widget(self.options.locals)
+        view:include_helper(self)
+        self:write(view)
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-cookie", cookie)
+      end
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    match = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      local key
+      if route_name then
+        key = {
+          [route_name] = path
+        }
+      else
+        key = path
+      end
+      self[key] = handler
+      self.router = nil
+      return handler
+    end,
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_route
+      add_route = function(path, handler)
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          return self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          add_route(path, handler)
+        end
+        for path, handler in pairs(self) do
+          add_route(path, handler)
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  local _list_0 = {
+    "get",
+    "post",
+    "delete",
+    "put"
+  }
+  for _index_0 = 1, #_list_0 do
+    local meth = _list_0[_index_0]
+    local upper_meth = meth:upper()
+    self.__base[meth] = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.responders = self.responders or { }
+      local existing = self.responders[route_name or path]
+      local tbl = {
+        [upper_meth] = handler
+      }
+      if existing then
+        setmetatable(tbl, {
+          __index = function(self, key)
+            if key:match("%u") then
+              return existing
+            end
+          end
+        })
+      end
+      local responder = respond_to(tbl)
+      self.responders[route_name or path] = responder
+      return self:match(route_name, path, responder)
+    end
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out, tbl
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error or error_response
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+local json_params
+json_params = function(fn)
+  return function(self, ...)
+    do
+      local content_type = self.req.headers["content-type"]
+      if content_type then
+        if string.find(content_type:lower(), "application/json", nil, true) then
+          local obj
+          pcall(function()
+            local err
+            obj, err = json.decode(ngx.req.get_body_data())
+          end)
+          if obj then
+            self:add_params(obj, "json")
+          end
+        end
+      end
+    end
+    return fn(self, ...)
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  json_params = json_params,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.17 view
@@ -0,0 +1,659 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local json = require("cjson")
+local capture_errors, capture_errors_json, respond_to
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      local widget = self.options.render
+      if widget == true then
+        widget = self.route_name
+      end
+      if widget then
+        if type(widget) == "string" then
+          widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+        end
+        local view = widget(self.options.locals)
+        view:include_helper(self)
+        self:write(view)
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-cookie", cookie)
+      end
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    match = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      local key
+      if route_name then
+        key = {
+          [route_name] = path
+        }
+      else
+        key = path
+      end
+      self[key] = handler
+      self.router = nil
+      return handler
+    end,
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_route
+      add_route = function(path, handler)
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          return self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          add_route(path, handler)
+        end
+        for path, handler in pairs(self) do
+          add_route(path, handler)
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  local _list_0 = {
+    "get",
+    "post",
+    "delete",
+    "put"
+  }
+  for _index_0 = 1, #_list_0 do
+    local meth = _list_0[_index_0]
+    local upper_meth = meth:upper()
+    self.__base[meth] = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.responders = self.responders or { }
+      local existing = self.responders[route_name or path]
+      local tbl = {
+        [upper_meth] = handler
+      }
+      if existing then
+        setmetatable(tbl, {
+          __index = function(self, key)
+            if key:match("%u") then
+              return existing
+            end
+          end
+        })
+      end
+      local responder = respond_to(tbl)
+      self.responders[route_name or path] = responder
+      return self:match(route_name, path, responder)
+    end
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out, tbl
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error or error_response
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+local json_params
+json_params = function(fn)
+  return function(self, ...)
+    do
+      local content_type = self.req.headers["content-type"]
+      if content_type then
+        if string.find(content_type:lower(), "application/json") then
+          local obj
+          pcall(function()
+            local err
+            obj, err = json.decode(ngx.req.get_body_data())
+          end)
+          if obj then
+            self:add_params(obj, "json")
+          end
+        end
+      end
+    end
+    return fn(self, ...)
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  json_params = json_params,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.18 view
@@ -0,0 +1,659 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local json = require("cjson")
+local capture_errors, capture_errors_json, respond_to
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      local widget = self.options.render
+      if widget == true then
+        widget = self.route_name
+      end
+      if widget then
+        if type(widget) == "string" then
+          widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+        end
+        local view = widget(self.options.locals)
+        view:include_helper(self)
+        self:write(view)
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-cookie", cookie)
+      end
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    match = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      local key
+      if route_name then
+        key = {
+          [route_name] = path
+        }
+      else
+        key = path
+      end
+      self[key] = handler
+      self.router = nil
+      return handler
+    end,
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_route
+      add_route = function(path, handler)
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          return self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          add_route(path, handler)
+        end
+        for path, handler in pairs(self) do
+          add_route(path, handler)
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  local _list_0 = {
+    "get",
+    "post",
+    "delete",
+    "put"
+  }
+  for _index_0 = 1, #_list_0 do
+    local meth = _list_0[_index_0]
+    local upper_meth = meth:upper()
+    self.__base[meth] = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.responders = self.responders or { }
+      local existing = self.responders[route_name or path]
+      local tbl = {
+        [upper_meth] = handler
+      }
+      if existing then
+        setmetatable(tbl, {
+          __index = function(self, key)
+            if key:match("%u") then
+              return existing
+            end
+          end
+        })
+      end
+      local responder = respond_to(tbl)
+      self.responders[route_name or path] = responder
+      return self:match(route_name, path, responder)
+    end
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out, tbl
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error or error_response
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+local json_params
+json_params = function(fn)
+  return function(self, ...)
+    do
+      local content_type = self.req.headers["content-type"]
+      if content_type then
+        if content_type:lower() == "application/json" then
+          local obj
+          pcall(function()
+            local err
+            obj, err = json.decode(ngx.req.get_body_data())
+          end)
+          if obj then
+            self:add_params(obj, "json")
+          end
+        end
+      end
+    end
+    return fn(self, ...)
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  json_params = json_params,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.19 view
@@ -0,0 +1,655 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local json_safe = require("cjson.safe")
+local capture_errors, capture_errors_json, respond_to
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      local widget = self.options.render
+      if widget == true then
+        widget = self.route_name
+      end
+      if widget then
+        if type(widget) == "string" then
+          widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+        end
+        local view = widget(self.options.locals)
+        view:include_helper(self)
+        self:write(view)
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-cookie", cookie)
+      end
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    match = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      local key
+      if route_name then
+        key = {
+          [route_name] = path
+        }
+      else
+        key = path
+      end
+      self[key] = handler
+      self.router = nil
+      return handler
+    end,
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_route
+      add_route = function(path, handler)
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          return self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          add_route(path, handler)
+        end
+        for path, handler in pairs(self) do
+          add_route(path, handler)
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  local _list_0 = {
+    "get",
+    "post",
+    "delete",
+    "put"
+  }
+  for _index_0 = 1, #_list_0 do
+    local meth = _list_0[_index_0]
+    local upper_meth = meth:upper()
+    self.__base[meth] = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.responders = self.responders or { }
+      local existing = self.responders[route_name or path]
+      local tbl = {
+        [upper_meth] = handler
+      }
+      if existing then
+        setmetatable(tbl, {
+          __index = function(self, key)
+            if key:match("%u") then
+              return existing
+            end
+          end
+        })
+      end
+      local responder = respond_to(tbl)
+      self.responders[route_name or path] = responder
+      return self:match(route_name, path, responder)
+    end
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out, tbl
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error or error_response
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+local json_params
+json_params = function(fn)
+  return function(self, ...)
+    do
+      local content_type = self.req.headers["content-type"]
+      if content_type then
+        if content_type:lower() == "application/json" then
+          local obj, err = json_safe.decode(ngx.req.get_body_data())
+          if obj then
+            self:add_params(obj, "json")
+          end
+        end
+      end
+    end
+    return fn(self, ...)
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  json_params = json_params,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.2 view
@@ -0,0 +1,724 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local lapis_config = require("lapis.config")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local increment_perf
+do
+  local _obj_0 = require("lapis.nginx.context")
+  increment_perf = _obj_0.increment_perf
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local insert
+do
+  local _obj_0 = table
+  insert = _obj_0.insert
+end
+local json = require("cjson")
+local capture_errors, capture_errors_json, respond_to
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-Type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-Type"] = ct
+        end
+      end
+      if not self.res.headers["Content-Type"] then
+        self.res.headers["Content-Type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+          return ""
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      local widget = self.options.render
+      if widget == true then
+        widget = self.route_name
+      end
+      if widget then
+        local config = lapis_config.get()
+        if type(widget) == "string" then
+          widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+        end
+        local start_time
+        if config.measure_performance then
+          ngx.update_time()
+          start_time = ngx.now()
+        end
+        local view = widget(self.options.locals)
+        if self.layout_opts then
+          self.layout_opts.view_widget = view
+        end
+        view:include_helper(self)
+        self:write(view)
+        if start_time then
+          ngx.update_time()
+          increment_perf("view_time", ngx.now() - start_time)
+        end
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+        do
+          local extra = self.app.cookie_attributes(self, k, v)
+          if extra then
+            cookie = cookie .. ("; " .. extra)
+          end
+        end
+        self.res:add_header("Set-Cookie", cookie)
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    enable = function(self, feature)
+      local fn = require("lapis.features." .. tostring(feature))
+      if type(fn) == "function" then
+        return fn(self)
+      end
+    end,
+    match = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.ordered_routes = self.ordered_routes or { }
+      local key
+      if route_name then
+        local tuple = self.ordered_routes[route_name]
+        do
+          local old_path = tuple and tuple[next(tuple)]
+          if old_path then
+            if old_path ~= path then
+              error("named route mismatch (" .. tostring(old_path) .. " != " .. tostring(path) .. ")")
+            end
+          end
+        end
+        if tuple then
+          key = tuple
+        else
+          tuple = {
+            [route_name] = path
+          }
+          self.ordered_routes[route_name] = tuple
+          key = tuple
+        end
+      else
+        key = path
+      end
+      if not (self[key]) then
+        insert(self.ordered_routes, key)
+      end
+      self[key] = handler
+      self.router = nil
+      return handler
+    end,
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_route
+      add_route = function(path, handler)
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          return self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          add_route(path, handler)
+        end
+        do
+          local ordered = self.ordered_routes
+          if ordered then
+            for _index_0 = 1, #ordered do
+              local path = ordered[_index_0]
+              add_route(path, self[path])
+            end
+          else
+            for path, handler in pairs(self) do
+              add_route(path, handler)
+            end
+          end
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          handler({ }, nil, "default_route", r)
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace, error_page)
+      if error_page == nil then
+        error_page = self.app.error_page
+      end
+      local r = self.app.Request(self, self.req, self.res)
+      local config = lapis_config.get()
+      if config._name == "test" then
+        local param_dump = logger.flatten_params(self.url_params)
+        r.res:add_header("X-Lapis-Error", "true")
+        r:write({
+          status = 500,
+          json = {
+            status = "[" .. tostring(r.req.cmd_mth) .. "] " .. tostring(r.req.cmd_url) .. " " .. tostring(param_dump),
+            err = err,
+            trace = trace
+          }
+        })
+      else
+        r:write({
+          status = 500,
+          layout = false,
+          content_type = "text/html",
+          error_page({
+            status = 500,
+            err = err,
+            trace = trace
+          })
+        })
+      end
+      r:render()
+      logger.request(r)
+      return r
+    end,
+    cookie_attributes = function(self, name, value)
+      return "Path=/; HttpOnly"
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  local _list_0 = {
+    "get",
+    "post",
+    "delete",
+    "put"
+  }
+  for _index_0 = 1, #_list_0 do
+    local meth = _list_0[_index_0]
+    local upper_meth = meth:upper()
+    self.__base[meth] = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.responders = self.responders or { }
+      local existing = self.responders[route_name or path]
+      local tbl = {
+        [upper_meth] = handler
+      }
+      if existing then
+        setmetatable(tbl, {
+          __index = function(self, key)
+            if key:match("%u") then
+              return existing
+            end
+          end
+        })
+      end
+      local responder = respond_to(tbl)
+      self.responders[route_name or path] = responder
+      return self:match(route_name, path, responder)
+    end
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out, tbl
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error or error_response
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg, ...)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing, msg, ...
+end
+local json_params
+json_params = function(fn)
+  return function(self, ...)
+    do
+      local content_type = self.req.headers["content-type"]
+      if content_type then
+        if string.find(content_type:lower(), "application/json", nil, true) then
+          local obj
+          pcall(function()
+            local err
+            obj, err = json.decode(ngx.req.get_body_data())
+          end)
+          if obj then
+            self:add_params(obj, "json")
+          end
+        end
+      end
+    end
+    return fn(self, ...)
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  json_params = json_params,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.20 view
@@ -0,0 +1,597 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local json_safe = require("cjson.safe")
+local capture_errors, capture_errors_json
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      local widget = self.options.render
+      if widget == true then
+        widget = self.route_name
+      end
+      if widget then
+        if type(widget) == "string" then
+          widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+        end
+        local view = widget(self.options.locals)
+        view:include_helper(self)
+        self:write(view)
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-cookie", cookie)
+      end
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          local t = type(path)
+          if t == "table" or t == "string" and path:match("^/") then
+            self.router:add_route(path, self:wrap_handler(handler))
+          end
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+local respond_to
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error or error_response
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+local json_params
+json_params = function(fn)
+  return function(self, ...)
+    do
+      local content_type = self.req.headers["content-type"]
+      if content_type then
+        if content_type:lower() == "application/json" then
+          local obj, err = json_safe.decode(ngx.req.get_body_data())
+          if obj then
+            self:add_params(obj, "json")
+          end
+        end
+      end
+    end
+    return fn(self, ...)
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  json_params = json_params,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.21 view
@@ -0,0 +1,578 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local capture_errors, capture_errors_json
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      local widget = self.options.render
+      if widget == true then
+        widget = self.route_name
+      end
+      if widget then
+        if type(widget) == "string" then
+          widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+        end
+        local view = widget(self.options.locals)
+        view:include_helper(self)
+        self:write(view)
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-cookie", cookie)
+      end
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          local t = type(path)
+          if t == "table" or t == "string" and path:match("^/") then
+            self.router:add_route(path, self:wrap_handler(handler))
+          end
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+local respond_to
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error or error_response
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.22 view
@@ -0,0 +1,580 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local capture_errors, capture_errors_json
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-cookie", cookie)
+      end
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          local t = type(path)
+          if t == "table" or t == "string" and path:match("^/") then
+            self.router:add_route(path, self:wrap_handler(handler))
+          end
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+local respond_to
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = error_response or fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.23 view
@@ -0,0 +1,580 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local capture_errors, capture_errors_json
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-cookie", cookie)
+      end
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          local t = type(path)
+          if t == "table" or t == "string" and path:match("^/") then
+            self.router:add_route(path, self:wrap_handler(handler))
+          end
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+local respond_to
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.24 view
@@ -0,0 +1,575 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local capture_errors, capture_errors_json
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-cookie", cookie)
+      end
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          local t = type(path)
+          if t == "table" or t == "string" and path:match("^/") then
+            self.router:add_route(path, self:wrap_handler(handler))
+          end
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+local respond_to
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.25 view
@@ -0,0 +1,564 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local capture_errors, capture_errors_json
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-cookie", cookie)
+      end
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          local t = type(path)
+          if t == "table" or t == "string" and path:match("^/") then
+            self.router:add_route(path, self:wrap_handler(handler))
+          end
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  local out
+  out = function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          if run_before_filter(before, self) then
+            return 
+          end
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+  do
+    local error_response = tbl.on_error
+    if error_response then
+      out = capture_errors(out, error_response)
+    end
+  end
+  return out
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.26 view
@@ -0,0 +1,561 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local capture_errors, capture_errors_json
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-cookie", cookie)
+      end
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          local t = type(path)
+          if t == "table" or t == "string" and path:match("^/") then
+            self.router:add_route(path, self:wrap_handler(handler))
+          end
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  local out
+  out = function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          if run_before_filter(before, self) then
+            return 
+          end
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+  do
+    local error_response = tbl.on_error
+    if error_response then
+      out = capture_errors(out, error_response)
+    end
+  end
+  return out
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.27 view
@@ -0,0 +1,545 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local capture_errors, capture_errors_json
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-cookie", cookie)
+      end
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          local t = type(path)
+          if t == "table" or t == "string" and path:match("^/") then
+            self.router:add_route(path, self:wrap_handler(handler))
+          end
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  local out
+  out = function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          if run_before_filter(before, self) then
+            return 
+          end
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+  do
+    local error_response = tbl.on_error
+    if error_response then
+      out = capture_errors(out, error_response)
+    end
+  end
+  return out
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.28 view
@@ -0,0 +1,545 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local capture_errors, capture_errors_json
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-cookie", cookie)
+      end
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_routes
+      add_routes = function(cls)
+        do
+          local parent = cls.__parent
+          if parent then
+            add_routes(parent)
+          end
+        end
+        for path, handler in pairs(cls.__base) do
+          local t = type(path)
+          if t == "table" or t == "string" and path:match("^/") then
+            self.router:add_route(path, self:wrap_handler(handler))
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  local out
+  out = function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          if run_before_filter(before, self) then
+            return 
+          end
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+  do
+    local error_response = tbl.on_error
+    if error_response then
+      out = capture_errors(out, error_response)
+    end
+  end
+  return out
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.29 view
@@ -0,0 +1,559 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local capture_errors, capture_errors_json
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts
+      do
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        parts = _accum_0
+      end
+      if not (#parts > 0) then
+        return 
+      end
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          for _index_0 = 1, #extra do
+            local p = extra[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_routes
+      add_routes = function(cls)
+        do
+          local parent = cls.__parent
+          if parent then
+            add_routes(parent)
+          end
+        end
+        for path, handler in pairs(cls.__base) do
+          local t = type(path)
+          if t == "table" or t == "string" and path:match("^/") then
+            self.router:add_route(path, self:wrap_handler(handler))
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  local out
+  out = function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          if run_before_filter(before, self) then
+            return 
+          end
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+  do
+    local error_response = tbl.on_error
+    if error_response then
+      out = capture_errors(out, error_response)
+    end
+  end
+  return out
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.3 view
@@ -0,0 +1,708 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local insert
+do
+  local _obj_0 = table
+  insert = _obj_0.insert
+end
+local json = require("cjson")
+local capture_errors, capture_errors_json, respond_to
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-Type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-Type"] = ct
+        end
+      end
+      if not self.res.headers["Content-Type"] then
+        self.res.headers["Content-Type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+          return ""
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      local widget = self.options.render
+      if widget == true then
+        widget = self.route_name
+      end
+      if widget then
+        if type(widget) == "string" then
+          widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+        end
+        local view = widget(self.options.locals)
+        if self.layout_opts then
+          self.layout_opts.view_widget = view
+        end
+        view:include_helper(self)
+        self:write(view)
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+        do
+          local extra = self.app.cookie_attributes(self, k, v)
+          if extra then
+            cookie = cookie .. ("; " .. extra)
+          end
+        end
+        self.res:add_header("Set-Cookie", cookie)
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    enable = function(self, feature)
+      local fn = require("lapis.features." .. tostring(feature))
+      if type(fn) == "function" then
+        return fn(self)
+      end
+    end,
+    match = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.ordered_routes = self.ordered_routes or { }
+      local key
+      if route_name then
+        local tuple = self.ordered_routes[route_name]
+        do
+          local old_path = tuple and tuple[next(tuple)]
+          if old_path then
+            if old_path ~= path then
+              error("named route mismatch (" .. tostring(old_path) .. " != " .. tostring(path) .. ")")
+            end
+          end
+        end
+        if tuple then
+          key = tuple
+        else
+          tuple = {
+            [route_name] = path
+          }
+          self.ordered_routes[route_name] = tuple
+          key = tuple
+        end
+      else
+        key = path
+      end
+      if not (self[key]) then
+        insert(self.ordered_routes, key)
+      end
+      self[key] = handler
+      self.router = nil
+      return handler
+    end,
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_route
+      add_route = function(path, handler)
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          return self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          add_route(path, handler)
+        end
+        do
+          local ordered = self.ordered_routes
+          if ordered then
+            for _index_0 = 1, #ordered do
+              local path = ordered[_index_0]
+              add_route(path, self[path])
+            end
+          else
+            for path, handler in pairs(self) do
+              add_route(path, handler)
+            end
+          end
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          handler({ }, nil, "default_route", r)
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace, error_page)
+      if error_page == nil then
+        error_page = self.app.error_page
+      end
+      local r = self.app.Request(self, self.req, self.res)
+      local config = require("lapis.config").get()
+      if config._name == "test" then
+        local param_dump = logger.flatten_params(self.url_params)
+        r.res:add_header("X-Lapis-Error", "true")
+        r:write({
+          status = 500,
+          json = {
+            status = "[" .. tostring(r.req.cmd_mth) .. "] " .. tostring(r.req.cmd_url) .. " " .. tostring(param_dump),
+            err = err,
+            trace = trace
+          }
+        })
+      else
+        r:write({
+          status = 500,
+          layout = false,
+          content_type = "text/html",
+          error_page({
+            status = 500,
+            err = err,
+            trace = trace
+          })
+        })
+      end
+      r:render()
+      logger.request(r)
+      return r
+    end,
+    cookie_attributes = function(self, name, value)
+      return "Path=/; HttpOnly"
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  local _list_0 = {
+    "get",
+    "post",
+    "delete",
+    "put"
+  }
+  for _index_0 = 1, #_list_0 do
+    local meth = _list_0[_index_0]
+    local upper_meth = meth:upper()
+    self.__base[meth] = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.responders = self.responders or { }
+      local existing = self.responders[route_name or path]
+      local tbl = {
+        [upper_meth] = handler
+      }
+      if existing then
+        setmetatable(tbl, {
+          __index = function(self, key)
+            if key:match("%u") then
+              return existing
+            end
+          end
+        })
+      end
+      local responder = respond_to(tbl)
+      self.responders[route_name or path] = responder
+      return self:match(route_name, path, responder)
+    end
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out, tbl
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error or error_response
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg, ...)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing, msg, ...
+end
+local json_params
+json_params = function(fn)
+  return function(self, ...)
+    do
+      local content_type = self.req.headers["content-type"]
+      if content_type then
+        if string.find(content_type:lower(), "application/json", nil, true) then
+          local obj
+          pcall(function()
+            local err
+            obj, err = json.decode(ngx.req.get_body_data())
+          end)
+          if obj then
+            self:add_params(obj, "json")
+          end
+        end
+      end
+    end
+    return fn(self, ...)
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  json_params = json_params,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.30 view
@@ -0,0 +1,546 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local capture_errors, capture_errors_json
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts
+      do
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        parts = _accum_0
+      end
+      if not (#parts > 0) then
+        return 
+      end
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          for _index_0 = 1, #extra do
+            local p = extra[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  local out
+  out = function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          if run_before_filter(before, self) then
+            return 
+          end
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+  do
+    local error_response = tbl.on_error
+    if error_response then
+      out = capture_errors(out, error_response)
+    end
+  end
+  return out
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.31 view
@@ -0,0 +1,546 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local capture_errors, capture_errors_json
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts
+      do
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        parts = _accum_0
+      end
+      if not (#parts > 0) then
+        return 
+      end
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          for _index_0 = 1, #extra do
+            local p = extra[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  local out
+  out = function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          if run_before_filter(before, self) then
+            return 
+          end
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+  do
+    local error_response = tbl.on_error
+    if error_response then
+      out = capture_errors(out, error_response)
+    end
+  end
+  return out
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.32 view
@@ -0,0 +1,556 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local capture_errors, capture_errors_json
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          do
+            local old_query = parsed.query
+            if old_query then
+              parsed.query = old_query .. "&" .. query
+            else
+              parsed.query = query
+            end
+          end
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts
+      do
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        parts = _accum_0
+      end
+      if not (#parts > 0) then
+        return 
+      end
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          for _index_0 = 1, #extra do
+            local p = extra[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  local out
+  out = function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          if run_before_filter(before, self) then
+            return 
+          end
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+  do
+    local error_response = tbl.on_error
+    if error_response then
+      out = capture_errors(out, error_response)
+    end
+  end
+  return out
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.33 view
@@ -0,0 +1,558 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local capture_errors, capture_errors_json
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          do
+            local old_query = parsed.query
+            if old_query then
+              parsed.query = old_query .. "&" .. query
+            else
+              parsed.query = query
+            end
+          end
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts
+      do
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        parts = _accum_0
+      end
+      if not (#parts > 0) then
+        return 
+      end
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          for _index_0 = 1, #extra do
+            local p = extra[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  local out
+  out = function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          if run_before_filter(before, self) then
+            return 
+          end
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+  do
+    local error_response = tbl.on_error
+    if error_response then
+      out = capture_errors(out, error_response)
+    end
+  end
+  return out
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.34 view
@@ -0,0 +1,570 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url
+end
+local capture_errors, capture_errors_json
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          do
+            local old_query = parsed.query
+            if old_query then
+              parsed.query = old_query .. "&" .. query
+            else
+              parsed.query = query
+            end
+          end
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts
+      do
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        parts = _accum_0
+      end
+      if not (#parts > 0) then
+        return 
+      end
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          for _index_0 = 1, #extra do
+            local p = extra[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  local out
+  out = function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          if run_before_filter(before, self) then
+            return 
+          end
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+  do
+    local error_response = tbl.on_error
+    if error_response then
+      out = capture_errors(out, error_response)
+    end
+  end
+  return out
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.35 view
@@ -0,0 +1,600 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json, build_url
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url = _table_0.parse_cookie_string, _table_0.to_json, _table_0.build_url
+end
+local capture_errors, capture_errors_json
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+        end
+      end
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          do
+            local old_query = parsed.query
+            if old_query then
+              parsed.query = old_query .. "&" .. query
+            else
+              parsed.query = query
+            end
+          end
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      if not (#parts > 0) then
+        return 
+      end
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          local _list_0 = extra
+          for _index_0 = 1, #_list_0 do
+            local p = _list_0[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              local _list_0 = before_filters
+              for _index_0 = 1, #_list_0 do
+                local filter = _list_0[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  local out
+  out = function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          if run_before_filter(before, self) then
+            return 
+          end
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+  do
+    local error_response = tbl.on_error
+    if error_response then
+      out = capture_errors(out, error_response)
+    end
+  end
+  return out
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.36 view
@@ -0,0 +1,600 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json, build_url
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url = _table_0.parse_cookie_string, _table_0.to_json, _table_0.build_url
+end
+local capture_errors, capture_errors_json
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          do
+            local old_query = parsed.query
+            if old_query then
+              parsed.query = old_query .. "&" .. query
+            else
+              parsed.query = query
+            end
+          end
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      if not (#parts > 0) then
+        return 
+      end
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          local _list_0 = extra
+          for _index_0 = 1, #_list_0 do
+            local p = _list_0[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              local _list_0 = before_filters
+              for _index_0 = 1, #_list_0 do
+                local filter = _list_0[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  local out
+  out = function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          if run_before_filter(before, self) then
+            return 
+          end
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+  do
+    local error_response = tbl.on_error
+    if error_response then
+      out = capture_errors(out, error_response)
+    end
+  end
+  return out
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.37 view
@@ -0,0 +1,600 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json, build_url
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url = _table_0.parse_cookie_string, _table_0.to_json, _table_0.build_url
+end
+local capture_errors, capture_errors_json
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          do
+            local old_query = parsed.query
+            if old_query then
+              parsed.query = old_query .. "&" .. query
+            else
+              parsed.query = query
+            end
+          end
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      if not (#parts > 0) then
+        return 
+      end
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          local _list_0 = extra
+          for _index_0 = 1, #_list_0 do
+            local p = _list_0[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              local _list_0 = before_filters
+              for _index_0 = 1, #_list_0 do
+                local filter = _list_0[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  local out
+  out = function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          if run_before_filter(before, self) then
+            return 
+          end
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+  do
+    local error_response = tbl.on_error
+    if error_response then
+      out = capture_errors(out, error_response)
+    end
+  end
+  return out
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.38 view
@@ -0,0 +1,589 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json, build_url
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url = _table_0.parse_cookie_string, _table_0.to_json, _table_0.build_url
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          do
+            local old_query = parsed.query
+            if old_query then
+              parsed.query = old_query .. "&" .. query
+            else
+              parsed.query = query
+            end
+          end
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      if not (#parts > 0) then
+        return 
+      end
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          local _list_0 = extra
+          for _index_0 = 1, #_list_0 do
+            local p = _list_0[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              filter(r)
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              local _list_0 = before_filters
+              for _index_0 = 1, #_list_0 do
+                local filter = _list_0[_index_0]
+                filter(r)
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          if run_before_filter(before, self) then
+            return 
+          end
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.39 view
@@ -0,0 +1,575 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json, build_url
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url = _table_0.parse_cookie_string, _table_0.to_json, _table_0.build_url
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          do
+            local old_query = parsed.query
+            if old_query then
+              parsed.query = old_query .. "&" .. query
+            else
+              parsed.query = query
+            end
+          end
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      if not (#parts > 0) then
+        return 
+      end
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          local _list_0 = extra
+          for _index_0 = 1, #_list_0 do
+            local p = _list_0[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              filter(r)
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              local _list_0 = before_filters
+              for _index_0 = 1, #_list_0 do
+                local filter = _list_0[_index_0]
+                filter(r)
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.4 view
@@ -0,0 +1,706 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local insert
+do
+  local _obj_0 = table
+  insert = _obj_0.insert
+end
+local json = require("cjson")
+local capture_errors, capture_errors_json, respond_to
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-Type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-Type"] = ct
+        end
+      end
+      if not self.res.headers["Content-Type"] then
+        self.res.headers["Content-Type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+          return ""
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      local widget = self.options.render
+      if widget == true then
+        widget = self.route_name
+      end
+      if widget then
+        if type(widget) == "string" then
+          widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+        end
+        local view = widget(self.options.locals)
+        if self.layout_opts then
+          self.layout_opts.view_widget = view
+        end
+        view:include_helper(self)
+        self:write(view)
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+        do
+          local extra = self.app.cookie_attributes(self, k, v)
+          if extra then
+            cookie = cookie .. ("; " .. extra)
+          end
+        end
+        self.res:add_header("Set-Cookie", cookie)
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    enable = function(self, feature)
+      local fn = require("lapis.features." .. tostring(feature))
+      if type(fn) == "function" then
+        return fn(self)
+      end
+    end,
+    match = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.ordered_routes = self.ordered_routes or { }
+      local key
+      if route_name then
+        local tuple = self.ordered_routes[route_name]
+        do
+          local old_path = tuple and tuple[next(tuple)]
+          if old_path then
+            if old_path ~= path then
+              error("named route mismatch (" .. tostring(old_path) .. " != " .. tostring(path) .. ")")
+            end
+          end
+        end
+        if tuple then
+          key = tuple
+        else
+          tuple = {
+            [route_name] = path
+          }
+          self.ordered_routes[route_name] = tuple
+          key = tuple
+        end
+      else
+        key = path
+      end
+      if not (self[key]) then
+        insert(self.ordered_routes, key)
+      end
+      self[key] = handler
+      self.router = nil
+      return handler
+    end,
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_route
+      add_route = function(path, handler)
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          return self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          add_route(path, handler)
+        end
+        do
+          local ordered = self.ordered_routes
+          if ordered then
+            for _index_0 = 1, #ordered do
+              local path = ordered[_index_0]
+              add_route(path, self[path])
+            end
+          else
+            for path, handler in pairs(self) do
+              add_route(path, handler)
+            end
+          end
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          handler({ }, nil, "default_route", r)
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace, error_page)
+      if error_page == nil then
+        error_page = self.app.error_page
+      end
+      local r = self.app.Request(self, self.req, self.res)
+      local config = require("lapis.config").get()
+      if config._name == "test" then
+        r.res:add_header("X-Lapis-Error", "true")
+        r:write({
+          status = 500,
+          json = {
+            err = err,
+            trace = trace
+          }
+        })
+      else
+        r:write({
+          status = 500,
+          layout = false,
+          content_type = "text/html",
+          error_page({
+            status = 500,
+            err = err,
+            trace = trace
+          })
+        })
+      end
+      r:render()
+      logger.request(r)
+      return r
+    end,
+    cookie_attributes = function(self, name, value)
+      return "Path=/; HttpOnly"
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  local _list_0 = {
+    "get",
+    "post",
+    "delete",
+    "put"
+  }
+  for _index_0 = 1, #_list_0 do
+    local meth = _list_0[_index_0]
+    local upper_meth = meth:upper()
+    self.__base[meth] = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.responders = self.responders or { }
+      local existing = self.responders[route_name or path]
+      local tbl = {
+        [upper_meth] = handler
+      }
+      if existing then
+        setmetatable(tbl, {
+          __index = function(self, key)
+            if key:match("%u") then
+              return existing
+            end
+          end
+        })
+      end
+      local responder = respond_to(tbl)
+      self.responders[route_name or path] = responder
+      return self:match(route_name, path, responder)
+    end
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out, tbl
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error or error_response
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg, ...)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing, msg, ...
+end
+local json_params
+json_params = function(fn)
+  return function(self, ...)
+    do
+      local content_type = self.req.headers["content-type"]
+      if content_type then
+        if string.find(content_type:lower(), "application/json", nil, true) then
+          local obj
+          pcall(function()
+            local err
+            obj, err = json.decode(ngx.req.get_body_data())
+          end)
+          if obj then
+            self:add_params(obj, "json")
+          end
+        end
+      end
+    end
+    return fn(self, ...)
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  json_params = json_params,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.40 view
@@ -0,0 +1,575 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json, build_url
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url = _table_0.parse_cookie_string, _table_0.to_json, _table_0.build_url
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          do
+            local old_query = parsed.query
+            if old_query then
+              parsed.query = old_query .. "&" .. query
+            else
+              parsed.query = query
+            end
+          end
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      if not (#parts > 0) then
+        return 
+      end
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          local _list_0 = extra
+          for _index_0 = 1, #_list_0 do
+            local p = _list_0[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              filter(r)
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              local _list_0 = before_filters
+              for _index_0 = 1, #_list_0 do
+                local filter = _list_0[_index_0]
+                filter(r)
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.41 view
@@ -0,0 +1,572 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json, build_url
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url = _table_0.parse_cookie_string, _table_0.to_json, _table_0.build_url
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          do
+            local old_query = parsed.query
+            if old_query then
+              parsed.query = old_query .. "&" .. query
+            else
+              parsed.query = query
+            end
+          end
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          local _list_0 = extra
+          for _index_0 = 1, #_list_0 do
+            local p = _list_0[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              filter(r)
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              local _list_0 = before_filters
+              for _index_0 = 1, #_list_0 do
+                local filter = _list_0[_index_0]
+                filter(r)
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.42 view
@@ -0,0 +1,577 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json, build_url
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url = _table_0.parse_cookie_string, _table_0.to_json, _table_0.build_url
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          do
+            local old_query = parsed.query
+            if old_query then
+              parsed.query = old_query .. "&" .. query
+            else
+              parsed.query = query
+            end
+          end
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          local _list_0 = extra
+          for _index_0 = 1, #_list_0 do
+            local p = _list_0[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              filter(r)
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              local _list_0 = before_filters
+              for _index_0 = 1, #_list_0 do
+                local filter = _list_0[_index_0]
+                filter(r)
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.43 view
@@ -0,0 +1,554 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json, build_url
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url = _table_0.parse_cookie_string, _table_0.to_json, _table_0.build_url
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          do
+            local old_query = parsed.query
+            if old_query then
+              parsed.query = old_query .. "&" .. query
+            else
+              parsed.query = query
+            end
+          end
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          local _list_0 = extra
+          for _index_0 = 1, #_list_0 do
+            local p = _list_0[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              filter(r)
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local t = type(path)
+      if t == "table" then
+        if path_prefix then
+          local name = next(path)
+          path[name] = path_prefix .. path[name]
+        end
+        if name_prefix then
+          local name = next(path)
+          path[name_prefix .. name] = path[name]
+          path[name] = nil
+        end
+        into[path] = action
+      elseif t == "string" and path:match("^/") then
+        if path_prefix then
+          path = path_prefix .. path
+        end
+        into[path] = action
+      end
+    end
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.44 view
@@ -0,0 +1,527 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json, build_url
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url = _table_0.parse_cookie_string, _table_0.to_json, _table_0.build_url
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          do
+            local old_query = parsed.query
+            if old_query then
+              parsed.query = old_query .. "&" .. query
+            else
+              parsed.query = query
+            end
+          end
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          local _list_0 = extra
+          for _index_0 = 1, #_list_0 do
+            local p = _list_0[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              filter(r)
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.45 view
@@ -0,0 +1,527 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json, build_url
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url = _table_0.parse_cookie_string, _table_0.to_json, _table_0.build_url
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local widget = self.options.render
+        if widget then
+          if widget == true then
+            widget = self.route_name
+          end
+          if type(widget) == "string" then
+            widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          end
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          do
+            local old_query = parsed.query
+            if old_query then
+              parsed.query = old_query .. "&" .. query
+            else
+              parsed.query = query
+            end
+          end
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          local _list_0 = extra
+          for _index_0 = 1, #_list_0 do
+            local p = _list_0[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              filter(r)
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.46 view
@@ -0,0 +1,525 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json, build_url
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url = _table_0.parse_cookie_string, _table_0.to_json, _table_0.build_url
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          do
+            local old_query = parsed.query
+            if old_query then
+              parsed.query = old_query .. "&" .. query
+            else
+              parsed.query = query
+            end
+          end
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          local _list_0 = extra
+          for _index_0 = 1, #_list_0 do
+            local p = _list_0[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              filter(r)
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.47 view
@@ -0,0 +1,525 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json, build_url
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url = _table_0.parse_cookie_string, _table_0.to_json, _table_0.build_url
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path:match("^%a+:") then
+        return path
+      end
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          do
+            local old_query = parsed.query
+            if old_query then
+              parsed.query = old_query .. "&" .. query
+            else
+              parsed.query = query
+            end
+          end
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          local _list_0 = extra
+          for _index_0 = 1, #_list_0 do
+            local p = _list_0[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              filter(r)
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.48 view
@@ -0,0 +1,522 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json, build_url
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url = _table_0.parse_cookie_string, _table_0.to_json, _table_0.build_url
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          do
+            local old_query = parsed.query
+            if old_query then
+              parsed.query = old_query .. "&" .. query
+            else
+              parsed.query = query
+            end
+          end
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          local _list_0 = extra
+          for _index_0 = 1, #_list_0 do
+            local p = _list_0[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              filter(r)
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.49 view
@@ -0,0 +1,520 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json, build_url
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url = _table_0.parse_cookie_string, _table_0.to_json, _table_0.build_url
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          do
+            local old_query = parsed.query
+            if old_query then
+              parsed.query = old_query .. "&" .. query
+            else
+              parsed.query = query
+            end
+          end
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          local _list_0 = extra
+          for _index_0 = 1, #_list_0 do
+            local p = _list_0[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              filter(r)
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.5 view
@@ -0,0 +1,694 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local insert
+do
+  local _obj_0 = table
+  insert = _obj_0.insert
+end
+local json = require("cjson")
+local capture_errors, capture_errors_json, respond_to
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-Type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-Type"] = ct
+        end
+      end
+      if not self.res.headers["Content-Type"] then
+        self.res.headers["Content-Type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+          return ""
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      local widget = self.options.render
+      if widget == true then
+        widget = self.route_name
+      end
+      if widget then
+        if type(widget) == "string" then
+          widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+        end
+        local view = widget(self.options.locals)
+        if self.layout_opts then
+          self.layout_opts.view_widget = view
+        end
+        view:include_helper(self)
+        self:write(view)
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+        do
+          local extra = self.app.cookie_attributes(self, k, v)
+          if extra then
+            cookie = cookie .. ("; " .. extra)
+          end
+        end
+        self.res:add_header("Set-Cookie", cookie)
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    enable = function(self, feature)
+      local fn = require("lapis.features." .. tostring(feature))
+      if type(fn) == "function" then
+        return fn(self)
+      end
+    end,
+    match = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.ordered_routes = self.ordered_routes or { }
+      local key
+      if route_name then
+        local tuple = self.ordered_routes[route_name]
+        do
+          local old_path = tuple and tuple[next(tuple)]
+          if old_path then
+            if old_path ~= path then
+              error("named route mismatch (" .. tostring(old_path) .. " != " .. tostring(path) .. ")")
+            end
+          end
+        end
+        if tuple then
+          key = tuple
+        else
+          tuple = {
+            [route_name] = path
+          }
+          self.ordered_routes[route_name] = tuple
+          key = tuple
+        end
+      else
+        key = path
+      end
+      if not (self[key]) then
+        insert(self.ordered_routes, key)
+      end
+      self[key] = handler
+      self.router = nil
+      return handler
+    end,
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_route
+      add_route = function(path, handler)
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          return self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          add_route(path, handler)
+        end
+        do
+          local ordered = self.ordered_routes
+          if ordered then
+            for _index_0 = 1, #ordered do
+              local path = ordered[_index_0]
+              add_route(path, self[path])
+            end
+          else
+            for path, handler in pairs(self) do
+              add_route(path, handler)
+            end
+          end
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          handler({ }, nil, "default_route", r)
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace, error_page)
+      if error_page == nil then
+        error_page = self.app.error_page
+      end
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end,
+    cookie_attributes = function(self, name, value)
+      return "Path=/; HttpOnly"
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  local _list_0 = {
+    "get",
+    "post",
+    "delete",
+    "put"
+  }
+  for _index_0 = 1, #_list_0 do
+    local meth = _list_0[_index_0]
+    local upper_meth = meth:upper()
+    self.__base[meth] = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.responders = self.responders or { }
+      local existing = self.responders[route_name or path]
+      local tbl = {
+        [upper_meth] = handler
+      }
+      if existing then
+        setmetatable(tbl, {
+          __index = function(self, key)
+            if key:match("%u") then
+              return existing
+            end
+          end
+        })
+      end
+      local responder = respond_to(tbl)
+      self.responders[route_name or path] = responder
+      return self:match(route_name, path, responder)
+    end
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out, tbl
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error or error_response
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg, ...)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing, msg, ...
+end
+local json_params
+json_params = function(fn)
+  return function(self, ...)
+    do
+      local content_type = self.req.headers["content-type"]
+      if content_type then
+        if string.find(content_type:lower(), "application/json", nil, true) then
+          local obj
+          pcall(function()
+            local err
+            obj, err = json.decode(ngx.req.get_body_data())
+          end)
+          if obj then
+            self:add_params(obj, "json")
+          end
+        end
+      end
+    end
+    return fn(self, ...)
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  json_params = json_params,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.50 view
@@ -0,0 +1,516 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json = _table_0.parse_cookie_string, _table_0.to_json
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          parsed.query = query
+        end
+        if not path:match("^/") then
+          path = "/" .. tostring(path)
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          local _list_0 = extra
+          for _index_0 = 1, #_list_0 do
+            local p = _list_0[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              filter(r)
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.51 view
@@ -0,0 +1,516 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json = _table_0.parse_cookie_string, _table_0.to_json
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params())
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          parsed.query = query
+        end
+        if not path:match("^/") then
+          path = "/" .. tostring(path)
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          local _list_0 = extra
+          for _index_0 = 1, #_list_0 do
+            local p = _list_0[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              filter(r)
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.52 view
@@ -0,0 +1,514 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json = _table_0.parse_cookie_string, _table_0.to_json
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params())
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          parsed.query = query
+        end
+        if not path:match("^/") then
+          path = "/" .. tostring(path)
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      do
+        local extra = self.app.cookie_attributes
+        if extra then
+          i = i + 3
+          local _list_0 = extra
+          for _index_0 = 1, #_list_0 do
+            local p = _list_0[_index_0]
+            parts[i] = p
+            i = i + 1
+          end
+        end
+      end
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.53 view
@@ -0,0 +1,502 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json = _table_0.parse_cookie_string, _table_0.to_json
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params())
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          parsed.query = query
+        end
+        if not path:match("^/") then
+          path = "/" .. tostring(path)
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.54 view
@@ -0,0 +1,502 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json = _table_0.parse_cookie_string, _table_0.to_json
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params())
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          parsed.query = query
+        end
+        if not path:match("^/") then
+          path = "/" .. tostring(path)
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace)
+      local r = Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        self.app.error_page({
+          staus = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.55 view
@@ -0,0 +1,498 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json = _table_0.parse_cookie_string, _table_0.to_json
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params())
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          parsed.query = query
+        end
+        if not path:match("^/") then
+          path = "/" .. tostring(path)
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          content_type = "text/html",
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return self.app:handle_404()
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.56 view
@@ -0,0 +1,495 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string, to_json
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string, to_json = _table_0.parse_cookie_string, _table_0.to_json
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params())
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          parsed.query = query
+        end
+        if not path:match("^/") then
+          path = "/" .. tostring(path)
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          content_type = "text/html",
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return error("Failed to find route: " .. tostring(self.req.cmd_url))
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.57 view
@@ -0,0 +1,496 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = json.encode(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params())
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          parsed.query = query
+        end
+        if not path:match("^/") then
+          path = "/" .. tostring(path)
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          content_type = "text/html",
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return error("Failed to find route: " .. tostring(self.req.cmd_url))
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.58 view
@@ -0,0 +1,495 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = json.encode(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params())
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          parsed.query = query
+        end
+        if not path:match("^/") then
+          path = "/" .. tostring(path)
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return error("Failed to find route: " .. tostring(self.req.cmd_url))
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.59 view
@@ -0,0 +1,495 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = json.encode(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        url = self.options.redirect_to
+        if url then
+          if url:match("^/") then
+            url = self:build_url(url)
+          end
+          self.res:add_header("Location", url)
+          self.res.status = 302
+        end
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params())
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          parsed.query = query
+        end
+        if not path:match("^/") then
+          path = "/" .. tostring(path)
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return error("Failed to find route: " .. tostring(self.req.cmd_url))
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.6 view
@@ -0,0 +1,692 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local insert
+do
+  local _obj_0 = table
+  insert = _obj_0.insert
+end
+local json = require("cjson")
+local capture_errors, capture_errors_json, respond_to
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-Type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-Type"] = ct
+        end
+      end
+      if not self.res.headers["Content-Type"] then
+        self.res.headers["Content-Type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+          return ""
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      local widget = self.options.render
+      if widget == true then
+        widget = self.route_name
+      end
+      if widget then
+        if type(widget) == "string" then
+          widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+        end
+        local view = widget(self.options.locals)
+        if self.layout_opts then
+          self.layout_opts.view_widget = view
+        end
+        view:include_helper(self)
+        self:write(view)
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-Cookie", cookie)
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    enable = function(self, feature)
+      local fn = require("lapis.features." .. tostring(feature))
+      if type(fn) == "function" then
+        return fn(self)
+      end
+    end,
+    match = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.ordered_routes = self.ordered_routes or { }
+      local key
+      if route_name then
+        local tuple = self.ordered_routes[route_name]
+        do
+          local old_path = tuple and tuple[next(tuple)]
+          if old_path then
+            if old_path ~= path then
+              error("named route mismatch (" .. tostring(old_path) .. " != " .. tostring(path) .. ")")
+            end
+          end
+        end
+        if tuple then
+          key = tuple
+        else
+          tuple = {
+            [route_name] = path
+          }
+          self.ordered_routes[route_name] = tuple
+          key = tuple
+        end
+      else
+        key = path
+      end
+      if not (self[key]) then
+        insert(self.ordered_routes, key)
+      end
+      self[key] = handler
+      self.router = nil
+      return handler
+    end,
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_route
+      add_route = function(path, handler)
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          return self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          add_route(path, handler)
+        end
+        do
+          local ordered = self.ordered_routes
+          if ordered then
+            for _index_0 = 1, #ordered do
+              local path = ordered[_index_0]
+              add_route(path, self[path])
+            end
+          else
+            for path, handler in pairs(self) do
+              add_route(path, handler)
+            end
+          end
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          handler({ }, nil, "default_route", r)
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace, error_page)
+      if error_page == nil then
+        error_page = self.app.error_page
+      end
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  local _list_0 = {
+    "get",
+    "post",
+    "delete",
+    "put"
+  }
+  for _index_0 = 1, #_list_0 do
+    local meth = _list_0[_index_0]
+    local upper_meth = meth:upper()
+    self.__base[meth] = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.responders = self.responders or { }
+      local existing = self.responders[route_name or path]
+      local tbl = {
+        [upper_meth] = handler
+      }
+      if existing then
+        setmetatable(tbl, {
+          __index = function(self, key)
+            if key:match("%u") then
+              return existing
+            end
+          end
+        })
+      end
+      local responder = respond_to(tbl)
+      self.responders[route_name or path] = responder
+      return self:match(route_name, path, responder)
+    end
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out, tbl
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error or error_response
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg, ...)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing, msg, ...
+end
+local json_params
+json_params = function(fn)
+  return function(self, ...)
+    do
+      local content_type = self.req.headers["content-type"]
+      if content_type then
+        if string.find(content_type:lower(), "application/json", nil, true) then
+          local obj
+          pcall(function()
+            local err
+            obj, err = json.decode(ngx.req.get_body_data())
+          end)
+          if obj then
+            self:add_params(obj, "json")
+          end
+        end
+      end
+    end
+    return fn(self, ...)
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  json_params = json_params,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.60 view
@@ -0,0 +1,486 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params())
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          parsed.query = query
+        end
+        if not path:match("^/") then
+          path = "/" .. tostring(path)
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return error("Failed to find route: " .. tostring(self.req.cmd_url))
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.61 view
@@ -0,0 +1,482 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          parsed.query = query
+        end
+        if not path:match("^/") then
+          path = "/" .. tostring(path)
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return error("Failed to find route: " .. tostring(self.req.cmd_url))
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.62 view
@@ -0,0 +1,481 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        if query then
+          path = _path
+          parsed.query = query
+        end
+        if not path:match("^/") then
+          path = "/" .. tostring(path)
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return error("Failed to find route: " .. tostring(self.req.cmd_url))
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.63 view
@@ -0,0 +1,480 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path then
+        local query
+        path, query = path:match("(.-)%?(.*)")
+        if query then
+          parsed.query = query
+        end
+        if not path:match("^/") then
+          path = "/" .. tostring(path)
+        end
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return error("Failed to find route: " .. tostring(self.req.cmd_url))
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.64 view
@@ -0,0 +1,473 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      local i = #parts
+      parts[i + 1] = "Path=/"
+      parts[i + 2] = "HttpOnly"
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return error("Failed to find route: " .. tostring(self.req.cmd_url))
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.65 view
@@ -0,0 +1,470 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        local layout = layout_cls({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return error("Failed to find route: " .. tostring(self.req.cmd_url))
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.66 view
@@ -0,0 +1,463 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return error("Failed to find route: " .. tostring(self.req.cmd_url))
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.67 view
@@ -0,0 +1,457 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return error("Failed to find route: " .. tostring(self.req.cmd_url))
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.68 view
@@ -0,0 +1,453 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return error("Failed to find route: " .. tostring(self.req.cmd_url))
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.69 view
@@ -0,0 +1,436 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return error("Failed to find route: " .. tostring(self.req.cmd_url))
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.7 view
@@ -0,0 +1,658 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local json = require("cjson")
+local capture_errors, capture_errors_json, respond_to
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-Type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-Type"] = ct
+        end
+      end
+      if not self.res.headers["Content-Type"] then
+        self.res.headers["Content-Type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+          return ""
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      local widget = self.options.render
+      if widget == true then
+        widget = self.route_name
+      end
+      if widget then
+        if type(widget) == "string" then
+          widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+        end
+        local view = widget(self.options.locals)
+        if self.layout_opts then
+          self.layout_opts.view_widget = view
+        end
+        view:include_helper(self)
+        self:write(view)
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-Cookie", cookie)
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    enable = function(self, feature)
+      local fn = require("lapis.features." .. tostring(feature))
+      if type(fn) == "function" then
+        return fn(self)
+      end
+    end,
+    match = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      local key
+      if route_name then
+        key = {
+          [route_name] = path
+        }
+      else
+        key = path
+      end
+      self[key] = handler
+      self.router = nil
+      return handler
+    end,
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_route
+      add_route = function(path, handler)
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          return self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          add_route(path, handler)
+        end
+        for path, handler in pairs(self) do
+          add_route(path, handler)
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          handler({ }, nil, "default_route", r)
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace, error_page)
+      if error_page == nil then
+        error_page = self.app.error_page
+      end
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  local _list_0 = {
+    "get",
+    "post",
+    "delete",
+    "put"
+  }
+  for _index_0 = 1, #_list_0 do
+    local meth = _list_0[_index_0]
+    local upper_meth = meth:upper()
+    self.__base[meth] = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.responders = self.responders or { }
+      local existing = self.responders[route_name or path]
+      local tbl = {
+        [upper_meth] = handler
+      }
+      if existing then
+        setmetatable(tbl, {
+          __index = function(self, key)
+            if key:match("%u") then
+              return existing
+            end
+          end
+        })
+      end
+      local responder = respond_to(tbl)
+      self.responders[route_name or path] = responder
+      return self:match(route_name, path, responder)
+    end
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out, tbl
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error or error_response
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg, ...)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing, msg, ...
+end
+local json_params
+json_params = function(fn)
+  return function(self, ...)
+    do
+      local content_type = self.req.headers["content-type"]
+      if content_type then
+        if string.find(content_type:lower(), "application/json", nil, true) then
+          local obj
+          pcall(function()
+            local err
+            obj, err = json.decode(ngx.req.get_body_data())
+          end)
+          if obj then
+            self:add_params(obj, "json")
+          end
+        end
+      end
+    end
+    return fn(self, ...)
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  json_params = json_params,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.70 view
@@ -0,0 +1,436 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write:", tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 1
+        for k, v in pairs(self.cookies) do
+          _accum_0[_len_0] = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          _len_0 = _len_0 + 1
+        end
+        return _accum_0
+      end)()
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return error("Failed to find route: " .. tostring(self.req.cmd_url))
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.71 view
@@ -0,0 +1,439 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write:", tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 0
+        for k, v in pairs(self.cookies) do
+          local _value_0 = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          if _value_0 ~= nil then
+            _len_0 = _len_0 + 1
+            _accum_0[_len_0] = _value_0
+          end
+        end
+        return _accum_0
+      end)()
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return error("Failed to find route: " .. tostring(self.req.cmd_url))
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+local capture_errors
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local capture_errors_json
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.72 view
@@ -0,0 +1,421 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write:", tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 0
+        for k, v in pairs(self.cookies) do
+          local _value_0 = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          if _value_0 ~= nil then
+            _len_0 = _len_0 + 1
+            _accum_0[_len_0] = _value_0
+          end
+        end
+        return _accum_0
+      end)()
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return error("Failed to find route: " .. tostring(self.req.cmd_url))
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local capture_errors
+capture_errors = function(fn)
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return {
+          render = true
+        }
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.73 view
@@ -0,0 +1,417 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write:", tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 0
+        for k, v in pairs(self.cookies) do
+          local _value_0 = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          if _value_0 ~= nil then
+            _len_0 = _len_0 + 1
+            _accum_0[_len_0] = _value_0
+          end
+        end
+        return _accum_0
+      end)()
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return error("Failed to find route: " .. tostring(self.req.cmd_url))
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      do
+        local before = tbl.before
+        if before then
+          before(self)
+        end
+      end
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local capture_errors
+capture_errors = function(fn)
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      self.errors = {
+        unpack(out, 2)
+      }
+      return {
+        render = true
+      }
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    coroutine.yield(msg)
+  end
+  return thing
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield(msg)
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.74 view
@@ -0,0 +1,411 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write:", tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 0
+        for k, v in pairs(self.cookies) do
+          local _value_0 = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          if _value_0 ~= nil then
+            _len_0 = _len_0 + 1
+            _accum_0[_len_0] = _value_0
+          end
+        end
+        return _accum_0
+      end)()
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return error("Failed to find route: " .. tostring(self.req.cmd_url))
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+local capture_errors
+capture_errors = function(fn)
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      self.errors = {
+        unpack(out, 2)
+      }
+      return {
+        render = true
+      }
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+local assert_error
+assert_error = function(thing, msg)
+  if not (thing) then
+    coroutine.yield(msg)
+  end
+  return thing
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield(msg)
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.75 view
@@ -0,0 +1,375 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write:", tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 0
+        for k, v in pairs(self.cookies) do
+          local _value_0 = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          if _value_0 ~= nil then
+            _len_0 = _len_0 + 1
+            _accum_0[_len_0] = _value_0
+          end
+        end
+        return _accum_0
+      end)()
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          r:write(handler({ }, nil, "default_route", r))
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.cmd_url:match("./$") then
+        local stripped = self.req.cmd_url:match("^(.+)/+$")
+        return {
+          redirect_to = stripped,
+          status = 301
+        }
+      else
+        return error("Failed to find route: " .. tostring(self.req.cmd_url))
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to
+}
+ examples/LUA_project/application.lua.76 view
@@ -0,0 +1,358 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write:", tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 0
+        for k, v in pairs(self.cookies) do
+          local _value_0 = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          if _value_0 ~= nil then
+            _len_0 = _len_0 + 1
+            _accum_0[_len_0] = _value_0
+          end
+        end
+        return _accum_0
+      end)()
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        self.router:resolve(req.parsed_url.path, r)
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+        logger.request(r)
+      end
+      return res
+    end,
+    serve = function(self) end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to
+}
+ examples/LUA_project/application.lua.77 view
@@ -0,0 +1,357 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write:", tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 0
+        for k, v in pairs(self.cookies) do
+          local _value_0 = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          if _value_0 ~= nil then
+            _len_0 = _len_0 + 1
+            _accum_0[_len_0] = _value_0
+          end
+        end
+        return _accum_0
+      end)()
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace
+      local success = xpcall((function()
+        local r = Request(self, req, res)
+        self.router:resolve(req.parsed_url.path, r)
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        local r = Request(self, req, res)
+        r:write({
+          status = 500,
+          layout = false,
+          self.error_page({
+            staus = 500,
+            err = err,
+            trace = trace
+          })
+        })
+        r:render()
+      end
+      return res
+    end,
+    serve = function(self) end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to
+}
+ examples/LUA_project/application.lua.78 view
@@ -0,0 +1,331 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self)
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write:", tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 0
+        for k, v in pairs(self.cookies) do
+          local _value_0 = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          if _value_0 ~= nil then
+            _len_0 = _len_0 + 1
+            _accum_0[_len_0] = _value_0
+          end
+        end
+        return _accum_0
+      end)()
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.views.layout"),
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local r = Request(self, req, res)
+      self.router:resolve(req.parsed_url.path, r)
+      r:render()
+      logger.request(r)
+      return res
+    end,
+    serve = function(self) end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to
+}
+ examples/LUA_project/application.lua.79 view
@@ -0,0 +1,331 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self)
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write:", tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 0
+        for k, v in pairs(self.cookies) do
+          local _value_0 = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          if _value_0 ~= nil then
+            _len_0 = _len_0 + 1
+            _accum_0[_len_0] = _value_0
+          end
+        end
+        return _accum_0
+      end)()
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.layout").Default,
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local r = Request(self, req, res)
+      self.router:resolve(req.parsed_url.path, r)
+      r:render()
+      logger.request(r)
+      return res
+    end,
+    serve = function(self) end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to
+}
+ examples/LUA_project/application.lua.8 view
@@ -0,0 +1,658 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local json = require("cjson")
+local capture_errors, capture_errors_json, respond_to
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+          return ""
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      local widget = self.options.render
+      if widget == true then
+        widget = self.route_name
+      end
+      if widget then
+        if type(widget) == "string" then
+          widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+        end
+        local view = widget(self.options.locals)
+        if self.layout_opts then
+          self.layout_opts.view_widget = view
+        end
+        view:include_helper(self)
+        self:write(view)
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-cookie", cookie)
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    enable = function(self, feature)
+      local fn = require("lapis.features." .. tostring(feature))
+      if type(fn) == "function" then
+        return fn(self)
+      end
+    end,
+    match = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      local key
+      if route_name then
+        key = {
+          [route_name] = path
+        }
+      else
+        key = path
+      end
+      self[key] = handler
+      self.router = nil
+      return handler
+    end,
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_route
+      add_route = function(path, handler)
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          return self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          add_route(path, handler)
+        end
+        for path, handler in pairs(self) do
+          add_route(path, handler)
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          handler({ }, nil, "default_route", r)
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace, error_page)
+      if error_page == nil then
+        error_page = self.app.error_page
+      end
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  local _list_0 = {
+    "get",
+    "post",
+    "delete",
+    "put"
+  }
+  for _index_0 = 1, #_list_0 do
+    local meth = _list_0[_index_0]
+    local upper_meth = meth:upper()
+    self.__base[meth] = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.responders = self.responders or { }
+      local existing = self.responders[route_name or path]
+      local tbl = {
+        [upper_meth] = handler
+      }
+      if existing then
+        setmetatable(tbl, {
+          __index = function(self, key)
+            if key:match("%u") then
+              return existing
+            end
+          end
+        })
+      end
+      local responder = respond_to(tbl)
+      self.responders[route_name or path] = responder
+      return self:match(route_name, path, responder)
+    end
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out, tbl
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error or error_response
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg, ...)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing, msg, ...
+end
+local json_params
+json_params = function(fn)
+  return function(self, ...)
+    do
+      local content_type = self.req.headers["content-type"]
+      if content_type then
+        if string.find(content_type:lower(), "application/json", nil, true) then
+          local obj
+          pcall(function()
+            local err
+            obj, err = json.decode(ngx.req.get_body_data())
+          end)
+          if obj then
+            self:add_params(obj, "json")
+          end
+        end
+      end
+    end
+    return fn(self, ...)
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  json_params = json_params,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.80 view
@@ -0,0 +1,330 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self)
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          view:include_helper(self)
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write:", tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 0
+        for k, v in pairs(self.cookies) do
+          local _value_0 = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          if _value_0 ~= nil then
+            _len_0 = _len_0 + 1
+            _accum_0[_len_0] = _value_0
+          end
+        end
+        return _accum_0
+      end)()
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.layout").Default,
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local r = Request(self, req, res)
+      self.router:resolve(req.parsed_url.path, r)
+      r:render()
+      logger.request(r)
+      return res
+    end,
+    serve = function(self) end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to
+}
+ examples/LUA_project/application.lua.81 view
@@ -0,0 +1,345 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self)
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local widget = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          local view = widget(self.options.locals)
+          if not (self.options.locals) then
+            for k, v in pairs(self) do
+              local _continue_0 = false
+              repeat
+                if k == "buffer" or k == "options" then
+                  _continue_0 = true
+                  break
+                end
+                view[k] = v
+                _continue_0 = true
+              until true
+              if not _continue_0 then
+                break
+              end
+            end
+          end
+          self:write(view)
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write:", tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 0
+        for k, v in pairs(self.cookies) do
+          local _value_0 = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          if _value_0 ~= nil then
+            _len_0 = _len_0 + 1
+            _accum_0[_len_0] = _value_0
+          end
+        end
+        return _accum_0
+      end)()
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.layout").Default,
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local r = Request(self, req, res)
+      self.router:resolve(req.parsed_url.path, r)
+      r:render()
+      logger.request(r)
+      return res
+    end,
+    serve = function(self) end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to
+}
+ examples/LUA_project/application.lua.82 view
@@ -0,0 +1,328 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self)
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      do
+        local rpath = self.options.render
+        if rpath then
+          if rpath == true then
+            rpath = self.route_name
+          end
+          local view = require(tostring(self.app.views_prefix) .. "." .. tostring(rpath))
+          self:write(view(self.options.locals or self))
+        end
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write:", tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 0
+        for k, v in pairs(self.cookies) do
+          local _value_0 = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          if _value_0 ~= nil then
+            _len_0 = _len_0 + 1
+            _accum_0[_len_0] = _value_0
+          end
+        end
+        return _accum_0
+      end)()
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.layout").Default,
+    views_prefix = "views",
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local r = Request(self, req, res)
+      self.router:resolve(req.parsed_url.path, r)
+      r:render()
+      logger.request(r)
+      return res
+    end,
+    serve = function(self) end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to
+}
+ examples/LUA_project/application.lua.83 view
@@ -0,0 +1,317 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local session = require("lapis.session")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local auto_table
+auto_table = function(fn)
+  return setmetatable({ }, {
+    __index = function(self, name)
+      local result = fn()
+      setmetatable(self, {
+        __index = result
+      })
+      return result[name]
+    end
+  })
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self)
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write:", tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 0
+        for k, v in pairs(self.cookies) do
+          local _value_0 = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          if _value_0 ~= nil then
+            _len_0 = _len_0 + 1
+            _accum_0[_len_0] = _value_0
+          end
+        end
+        return _accum_0
+      end)()
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = auto_table(function()
+        return session.get_session(self)
+      end)
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.layout").Default,
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local r = Request(self, req, res)
+      self.router:resolve(req.parsed_url.path, r)
+      r:render()
+      logger.request(r)
+      return res
+    end,
+    serve = function(self) end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to
+}
+ examples/LUA_project/application.lua.84 view
@@ -0,0 +1,306 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local parse_cookie_string
+do
+  local _table_0 = require("lapis.util")
+  parse_cookie_string = _table_0.parse_cookie_string
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self)
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      self:write_cookies()
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write:", tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      local parts = (function()
+        local _accum_0 = { }
+        local _len_0 = 0
+        for k, v in pairs(self.cookies) do
+          local _value_0 = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v))
+          if _value_0 ~= nil then
+            _len_0 = _len_0 + 1
+            _accum_0[_len_0] = _value_0
+          end
+        end
+        return _accum_0
+      end)()
+      return self.res:add_header("Set-cookie", table.concat(parts, "; "))
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = setmetatable({ }, {
+        __index = function(tbl, name)
+          local parsed = parse_cookie_string(self.req.headers.cookie)
+          setmetatable(self.cookies, {
+            __index = parsed
+          })
+          return parsed[name]
+        end
+      })
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.layout").Default,
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local r = Request(self, req, res)
+      self.router:resolve(req.parsed_url.path, r)
+      r:render()
+      logger.request(r)
+      return res
+    end,
+    serve = function(self) end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to
+}
+ examples/LUA_project/application.lua.85 view
@@ -0,0 +1,276 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self)
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write:", tostring(thing))
+        end
+      end
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.layout").Default,
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local r = Request(self, req, res)
+      self.router:resolve(req.parsed_url.path, r)
+      r:render()
+      logger.request(r)
+      return res
+    end,
+    serve = function(self) end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+local respond_to
+respond_to = function(tbl)
+  return function(self)
+    local fn = tbl[self.req.cmd_mth]
+    if fn then
+      return fn(self)
+    else
+      return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+    end
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to
+}
+ examples/LUA_project/application.lua.86 view
@@ -0,0 +1,264 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self)
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write:", tostring(thing))
+        end
+      end
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.layout").Default,
+    before_filters = { },
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          local _list_0 = self.before_filters
+          for _index_0 = 1, #_list_0 do
+            local filter = _list_0[_index_0]
+            filter(r)
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local r = Request(self, req, res)
+      self.router:resolve(req.parsed_url.path, r)
+      r:render()
+      logger.request(r)
+      return res
+    end,
+    serve = function(self) end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.before_filter = function(self, fn)
+    return table.insert(self.before_filters, fn)
+  end
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+return {
+  Request = Request,
+  Application = Application
+}
+ examples/LUA_project/application.lua.87 view
@@ -0,0 +1,254 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local json = require("cjson")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self)
+      if self.options.json then
+        self.res.headers["Content-type"] = "application/json"
+        self.res.content = json.encode(self.options.json)
+        return 
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write:", tostring(thing))
+        end
+      end
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.layout").Default,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local r = Request(self, req, res)
+      self.router:resolve(req.parsed_url.path, r)
+      r:render()
+      logger.request(r)
+      return res
+    end,
+    serve = function(self) end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+return {
+  Request = Request,
+  Application = Application
+}
+ examples/LUA_project/application.lua.88 view
@@ -0,0 +1,248 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self)
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.options.redirect_to then
+        self.res:add_header("Location", self:build_url(self.options.redirect_to))
+        self.res.status = 302
+      end
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    build_url = function(self, path, options)
+      local parsed = (function()
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        return _tbl_0
+      end)()
+      parsed.authority = nil
+      if path and not path:match("^/") then
+        path = "/" .. tostring(path)
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return url.build(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write:", tostring(thing))
+        end
+      end
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.layout").Default,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local r = Request(self, req, res)
+      self.router:resolve(req.parsed_url.path, r)
+      r:render()
+      logger.request(r)
+      return res
+    end,
+    serve = function(self) end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+return {
+  Request = Request,
+  Application = Application
+}
+ examples/LUA_project/application.lua.89 view
@@ -0,0 +1,211 @@+local logger = require("lapis.logging")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self)
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    write = function(self, thing)
+      local t = type(thing)
+      if t == "table" then
+        local mt = getmetatable(thing)
+        if mt and mt.__call then
+          t = "function"
+        end
+      end
+      local _exp_0 = t
+      if "string" == _exp_0 then
+        return table.insert(self.buffer, thing)
+      elseif "table" == _exp_0 then
+        for k, v in pairs(thing) do
+          if type(k) == "string" then
+            self.options[k] = v
+          else
+            self:write(v)
+          end
+        end
+      elseif "function" == _exp_0 then
+        return self:write(thing(self.buffer))
+      elseif "nil" == _exp_0 then
+        return nil
+      else
+        return error("Don't know how to write:", tostring(thing))
+      end
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.layout").Default,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local r = Request(self, req, res)
+      self.router:resolve(req.parsed_url.path, r)
+      r:render()
+      logger.request(r)
+      return res
+    end,
+    serve = function(self) end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+return {
+  Request = Request,
+  Application = Application
+}
+ examples/LUA_project/application.lua.9 view
@@ -0,0 +1,656 @@+local logger = require("lapis.logging")
+local url = require("socket.url")
+local session = require("lapis.session")
+local Router
+do
+  local _obj_0 = require("lapis.router")
+  Router = _obj_0.Router
+end
+local html_writer
+do
+  local _obj_0 = require("lapis.html")
+  html_writer = _obj_0.html_writer
+end
+local parse_cookie_string, to_json, build_url, auto_table
+do
+  local _obj_0 = require("lapis.util")
+  parse_cookie_string, to_json, build_url, auto_table = _obj_0.parse_cookie_string, _obj_0.to_json, _obj_0.build_url, _obj_0.auto_table
+end
+local json = require("cjson")
+local capture_errors, capture_errors_json, respond_to
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local run_before_filter
+run_before_filter = function(filter, r)
+  local _write = r.write
+  local written = false
+  r.write = function(...)
+    written = true
+    return _write(...)
+  end
+  filter(r)
+  r.write = nil
+  return written
+end
+local Request
+do
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        do
+          local front = k:match("^([^%[]+)%[")
+          if front then
+            local curr = self.params
+            for match in k:gmatch("%[(.-)%]") do
+              local new = curr[front]
+              if new == nil then
+                new = { }
+                curr[front] = new
+              end
+              curr = new
+              front = match
+            end
+            curr[front] = v
+          else
+            self.params[k] = v
+          end
+        end
+      end
+    end,
+    render = function(self, opts)
+      if opts == nil then
+        opts = false
+      end
+      if opts then
+        self.options = opts
+      end
+      session.write_session(self)
+      self:write_cookies()
+      if self.options.status then
+        self.res.status = self.options.status
+      end
+      do
+        local obj = self.options.json
+        if obj then
+          self.res.headers["Content-type"] = "application/json"
+          self.res.content = to_json(obj)
+          return 
+        end
+      end
+      do
+        local ct = self.options.content_type
+        if ct then
+          self.res.headers["Content-type"] = ct
+        end
+      end
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      do
+        local redirect_url = self.options.redirect_to
+        if redirect_url then
+          if redirect_url:match("^/") then
+            redirect_url = self:build_url(redirect_url)
+          end
+          self.res:add_header("Location", redirect_url)
+          self.res.status = self.res.status or 302
+          return ""
+        end
+      end
+      local has_layout = self.app.layout and set_and_truthy(self.options.layout, true)
+      if has_layout then
+        self.layout_opts = {
+          inner = nil
+        }
+      end
+      local widget = self.options.render
+      if widget == true then
+        widget = self.route_name
+      end
+      if widget then
+        if type(widget) == "string" then
+          widget = require(tostring(self.app.views_prefix) .. "." .. tostring(widget))
+        end
+        local view = widget(self.options.locals)
+        if self.layout_opts then
+          self.layout_opts.view_widget = view
+        end
+        view:include_helper(self)
+        self:write(view)
+      end
+      if has_layout then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout_path = self.options.layout
+        local layout_cls
+        if type(layout_path) == "string" then
+          layout_cls = require(tostring(self.app.views_prefix) .. "." .. tostring(layout_path))
+        else
+          layout_cls = self.app.layout
+        end
+        self.layout_opts.inner = self.layout_opts.inner or function()
+          return raw(inner)
+        end
+        local layout = layout_cls(self.layout_opts)
+        layout:include_helper(self)
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, first, ...)
+      if type(first) == "table" then
+        return self.app.router:url_for(first:url_params(self, ...))
+      else
+        return self.app.router:url_for(first, ...)
+      end
+    end,
+    build_url = function(self, path, options)
+      if path and path:match("^%a+:") then
+        return path
+      end
+      local parsed
+      do
+        local _tbl_0 = { }
+        for k, v in pairs(self.req.parsed_url) do
+          _tbl_0[k] = v
+        end
+        parsed = _tbl_0
+      end
+      parsed.query = nil
+      if path then
+        local _path, query = path:match("^(.-)%?(.*)$")
+        path = _path or path
+        parsed.query = query
+      end
+      parsed.path = path
+      if parsed.port == "80" then
+        parsed.port = nil
+      end
+      if options then
+        for k, v in pairs(options) do
+          parsed[k] = v
+        end
+      end
+      return build_url(parsed)
+    end,
+    write = function(self, ...)
+      local _list_0 = {
+        ...
+      }
+      for _index_0 = 1, #_list_0 do
+        local thing = _list_0[_index_0]
+        local t = type(thing)
+        if t == "table" then
+          local mt = getmetatable(thing)
+          if mt and mt.__call then
+            t = "function"
+          end
+        end
+        local _exp_0 = t
+        if "string" == _exp_0 then
+          table.insert(self.buffer, thing)
+        elseif "table" == _exp_0 then
+          for k, v in pairs(thing) do
+            if type(k) == "string" then
+              self.options[k] = v
+            else
+              self:write(v)
+            end
+          end
+        elseif "function" == _exp_0 then
+          self:write(thing(self.buffer))
+        elseif "nil" == _exp_0 then
+          local _ = nil
+        else
+          error("Don't know how to write: (" .. tostring(t) .. ") " .. tostring(thing))
+        end
+      end
+    end,
+    write_cookies = function(self)
+      if not (next(self.cookies)) then
+        return 
+      end
+      local extra = self.app.cookie_attributes
+      if extra then
+        extra = "; " .. table.concat(self.app.cookie_attributes, "; ")
+      end
+      for k, v in pairs(self.cookies) do
+        local cookie = tostring(url.escape(k)) .. "=" .. tostring(url.escape(v)) .. "; Path=/; HttpOnly"
+        if extra then
+          cookie = cookie .. extra
+        end
+        self.res:add_header("Set-cookie", cookie)
+      end
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+      self.cookies = auto_table(function()
+        return parse_cookie_string(self.req.headers.cookie)
+      end)
+      self.session = session.lazy_session(self)
+    end,
+    __base = _base_0,
+    __name = "Request"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  Request = _class_0
+end
+local Application
+do
+  local _base_0 = {
+    Request = Request,
+    layout = require("lapis.views.layout"),
+    error_page = require("lapis.views.error"),
+    views_prefix = "views",
+    enable = function(self, feature)
+      local fn = require("lapis.features." .. tostring(feature))
+      return fn(self)
+    end,
+    match = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      local key
+      if route_name then
+        key = {
+          [route_name] = path
+        }
+      else
+        key = path
+      end
+      self[key] = handler
+      self.router = nil
+      return handler
+    end,
+    build_router = function(self)
+      self.router = Router()
+      self.router.default_route = function(self)
+        return false
+      end
+      local add_route
+      add_route = function(path, handler)
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          return self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+      local add_routes
+      add_routes = function(cls)
+        for path, handler in pairs(cls.__base) do
+          add_route(path, handler)
+        end
+        for path, handler in pairs(self) do
+          add_route(path, handler)
+        end
+        do
+          local parent = cls.__parent
+          if parent then
+            return add_routes(parent)
+          end
+        end
+      end
+      return add_routes(self.__class)
+    end,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(r.req.params_get, "GET")
+          _with_0:add_params(r.req.params_post, "POST")
+          _with_0:add_params(params, "url_params")
+          if self.before_filters then
+            local _list_0 = self.before_filters
+            for _index_0 = 1, #_list_0 do
+              local filter = _list_0[_index_0]
+              if run_before_filter(filter, r) then
+                return r
+              end
+            end
+          end
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local err, trace, r
+      local success = xpcall((function()
+        r = self.Request(self, req, res)
+        if not (self.router:resolve(req.parsed_url.path, r)) then
+          local handler = self:wrap_handler(self.default_route)
+          handler({ }, nil, "default_route", r)
+        end
+        r:render()
+        return logger.request(r)
+      end), function(_err)
+        err = _err
+        trace = debug.traceback("", 2)
+      end)
+      if not (success) then
+        self.handle_error(r, err, trace)
+      end
+      return res
+    end,
+    serve = function(self) end,
+    default_route = function(self)
+      if self.req.parsed_url.path:match("./$") then
+        local stripped = self.req.parsed_url.path:match("^(.+)/+$")
+        return {
+          redirect_to = self:build_url(stripped, {
+            query = self.req.parsed_url.query
+          }),
+          status = 301
+        }
+      else
+        return self.app.handle_404(self)
+      end
+    end,
+    handle_404 = function(self)
+      return error("Failed to find route: " .. tostring(self.req.cmd_url))
+    end,
+    handle_error = function(self, err, trace, error_page)
+      if error_page == nil then
+        error_page = self.app.error_page
+      end
+      local r = self.app.Request(self, self.req, self.res)
+      r:write({
+        status = 500,
+        layout = false,
+        content_type = "text/html",
+        error_page({
+          status = 500,
+          err = err,
+          trace = trace
+        })
+      })
+      r:render()
+      logger.request(r)
+      return r
+    end
+  }
+  _base_0.__index = _base_0
+  local _class_0 = setmetatable({
+    __init = function(self)
+      return self:build_router()
+    end,
+    __base = _base_0,
+    __name = "Application"
+  }, {
+    __index = _base_0,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  local self = _class_0
+  self.find_action = function(self, name)
+    self._named_route_cache = self._named_route_cache or { }
+    local route = self._named_route_cache[name]
+    if not (route) then
+      for app_route in pairs(self.__base) do
+        if type(app_route) == "table" then
+          local app_route_name = next(app_route)
+          self._named_route_cache[app_route_name] = app_route
+          if app_route_name == name then
+            route = app_route
+          end
+        end
+      end
+    end
+    return route and self[route], route
+  end
+  local _list_0 = {
+    "get",
+    "post",
+    "delete",
+    "put"
+  }
+  for _index_0 = 1, #_list_0 do
+    local meth = _list_0[_index_0]
+    local upper_meth = meth:upper()
+    self.__base[meth] = function(self, route_name, path, handler)
+      if handler == nil then
+        handler = path
+        path = route_name
+        route_name = nil
+      end
+      self.responders = self.responders or { }
+      local existing = self.responders[route_name or path]
+      local tbl = {
+        [upper_meth] = handler
+      }
+      if existing then
+        setmetatable(tbl, {
+          __index = function(self, key)
+            if key:match("%u") then
+              return existing
+            end
+          end
+        })
+      end
+      local responder = respond_to(tbl)
+      self.responders[route_name or path] = responder
+      return self:match(route_name, path, responder)
+    end
+  end
+  self.before_filter = function(self, fn)
+    self.__base.before_filters = self.__base.before_filters or { }
+    return table.insert(self.before_filters, fn)
+  end
+  self.include = function(self, other_app, opts, into)
+    if into == nil then
+      into = self.__base
+    end
+    if type(other_app) == "string" then
+      other_app = require(other_app)
+    end
+    local path_prefix = opts and opts.path or other_app.path
+    local name_prefix = opts and opts.name or other_app.name
+    for path, action in pairs(other_app.__base) do
+      local _continue_0 = false
+      repeat
+        local t = type(path)
+        if t == "table" then
+          if path_prefix then
+            local name = next(path)
+            path[name] = path_prefix .. path[name]
+          end
+          if name_prefix then
+            local name = next(path)
+            path[name_prefix .. name] = path[name]
+            path[name] = nil
+          end
+        elseif t == "string" and path:match("^/") then
+          if path_prefix then
+            path = path_prefix .. path
+          end
+        else
+          _continue_0 = true
+          break
+        end
+        do
+          local before_filters = other_app.before_filters
+          if before_filters then
+            local fn = action
+            action = function(r)
+              for _index_0 = 1, #before_filters do
+                local filter = before_filters[_index_0]
+                if run_before_filter(filter, r) then
+                  return 
+                end
+              end
+              return fn(r)
+            end
+          end
+        end
+        into[path] = action
+        _continue_0 = true
+      until true
+      if not _continue_0 then
+        break
+      end
+    end
+  end
+  Application = _class_0
+end
+do
+  local default_head
+  default_head = function()
+    return {
+      layout = false
+    }
+  end
+  respond_to = function(tbl)
+    if not (tbl.HEAD) then
+      tbl.HEAD = default_head
+    end
+    local out
+    out = function(self)
+      local fn = tbl[self.req.cmd_mth]
+      if fn then
+        do
+          local before = tbl.before
+          if before then
+            if run_before_filter(before, self) then
+              return 
+            end
+          end
+        end
+        return fn(self)
+      else
+        return error("don't know how to respond to " .. tostring(self.req.cmd_mth))
+      end
+    end
+    do
+      local error_response = tbl.on_error
+      if error_response then
+        out = capture_errors(out, error_response)
+      end
+    end
+    return out, tbl
+  end
+end
+local default_error_response
+default_error_response = function()
+  return {
+    render = true
+  }
+end
+capture_errors = function(fn, error_response)
+  if error_response == nil then
+    error_response = default_error_response
+  end
+  if type(fn) == "table" then
+    error_response = fn.on_error or error_response
+    fn = fn[1]
+  end
+  return function(self, ...)
+    local co = coroutine.create(fn)
+    local out = {
+      coroutine.resume(co, self)
+    }
+    if not (out[1]) then
+      error(debug.traceback(co, out[2]))
+    end
+    if coroutine.status(co) == "suspended" then
+      if out[2] == "error" then
+        self.errors = out[3]
+        return error_response(self)
+      else
+        return error("Unknown yield")
+      end
+    else
+      return unpack(out, 2)
+    end
+  end
+end
+capture_errors_json = function(fn)
+  return capture_errors(fn, function(self)
+    return {
+      json = {
+        errors = self.errors
+      }
+    }
+  end)
+end
+local yield_error
+yield_error = function(msg)
+  return coroutine.yield("error", {
+    msg
+  })
+end
+local assert_error
+assert_error = function(thing, msg, ...)
+  if not (thing) then
+    yield_error(msg)
+  end
+  return thing, msg, ...
+end
+local json_params
+json_params = function(fn)
+  return function(self, ...)
+    do
+      local content_type = self.req.headers["content-type"]
+      if content_type then
+        if string.find(content_type:lower(), "application/json", nil, true) then
+          local obj
+          pcall(function()
+            local err
+            obj, err = json.decode(ngx.req.get_body_data())
+          end)
+          if obj then
+            self:add_params(obj, "json")
+          end
+        end
+      end
+    end
+    return fn(self, ...)
+  end
+end
+return {
+  Request = Request,
+  Application = Application,
+  respond_to = respond_to,
+  capture_errors = capture_errors,
+  capture_errors_json = capture_errors_json,
+  json_params = json_params,
+  assert_error = assert_error,
+  yield_error = yield_error
+}
+ examples/LUA_project/application.lua.90 view
@@ -0,0 +1,209 @@+local logger = require("lapis.logging")
+local Router
+do
+  local _table_0 = require("lapis.router")
+  Router = _table_0.Router
+end
+local html_writer
+do
+  local _table_0 = require("lapis.html")
+  html_writer = _table_0.html_writer
+end
+local set_and_truthy
+set_and_truthy = function(val, default)
+  if default == nil then
+    default = true
+  end
+  if val == nil then
+    return default
+  end
+  return val
+end
+local Request
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    add_params = function(self, params, name)
+      self[name] = params
+      for k, v in pairs(params) do
+        self.params[k] = v
+      end
+    end,
+    render = function(self)
+      if not self.res.headers["Content-type"] then
+        self.res.headers["Content-type"] = "text/html"
+      end
+      if self.app.layout and set_and_truthy(self.options.layout, true) then
+        local inner = self.buffer
+        self.buffer = { }
+        local layout = self.app.layout({
+          inner = function()
+            return raw(inner)
+          end
+        })
+        layout:render(self.buffer)
+      end
+      if next(self.buffer) then
+        local content = table.concat(self.buffer)
+        if self.res.content then
+          self.res.content = self.res.content .. content
+        else
+          self.res.content = content
+        end
+      end
+    end,
+    html = function(self, fn)
+      return html_writer(fn)
+    end,
+    url_for = function(self, ...)
+      return self.app.router:url_for(...)
+    end,
+    write = function(self, thing)
+      local t = type(thing)
+      if t == "table" then
+        local mt = getmetatable(thing)
+        if mt and mt.__call then
+          t = "function"
+        end
+      end
+      local _exp_0 = t
+      if "string" == _exp_0 then
+        return table.insert(self.buffer, thing)
+      elseif "table" == _exp_0 then
+        for k, v in pairs(thing) do
+          if type(k) == "string" then
+            self.options[k] = v
+          else
+            self:write(v)
+          end
+        end
+      elseif "function" == _exp_0 then
+        return self:write(thing(self.buffer))
+      elseif "nil" == _exp_0 then
+        return nil
+      else
+        return error("Don't know how to write:", tostring(thing))
+      end
+    end,
+    _debug = function(self)
+      self.buffer = {
+        "<html>",
+        "req:",
+        "<pre>",
+        moon.dump(self.req),
+        "</pre>",
+        "res:",
+        "<pre>",
+        moon.dump(self.res),
+        "</pre>",
+        "</html>"
+      }
+    end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self, app, req, res)
+      self.app, self.req, self.res = app, req, res
+      self.buffer = { }
+      self.params = { }
+      self.options = { }
+    end,
+    __base = _base_0,
+    __name = "Request",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Request = _class_0
+end
+local Application
+do
+  local _parent_0 = nil
+  local _base_0 = {
+    layout = require("lapis.layout").Default,
+    wrap_handler = function(self, handler)
+      return function(params, path, name, r)
+        do
+          local _with_0 = r
+          _with_0.route_name = name
+          _with_0:add_params(params, "url_params")
+          _with_0:write(handler(r))
+          return _with_0
+        end
+      end
+    end,
+    dispatch = function(self, req, res)
+      local r = Request(self, req, res)
+      self.router:resolve(req.parsed_url.path, r)
+      r:render()
+      logger.request(r)
+      return res
+    end,
+    serve = function(self) end
+  }
+  _base_0.__index = _base_0
+  if _parent_0 then
+    setmetatable(_base_0, _parent_0.__base)
+  end
+  local _class_0 = setmetatable({
+    __init = function(self)
+      self.router = Router()
+      do
+        local _with_0 = require("lapis.server")
+        self.__class.__base["/static/*"] = _with_0.make_static_handler("static")
+        self.__class.__base["/favicon.ico"] = _with_0.serve_from_static()
+      end
+      for path, handler in pairs(self.__class.__base) do
+        local t = type(path)
+        if t == "table" or t == "string" and path:match("^/") then
+          self.router:add_route(path, self:wrap_handler(handler))
+        end
+      end
+    end,
+    __base = _base_0,
+    __name = "Application",
+    __parent = _parent_0
+  }, {
+    __index = function(cls, name)
+      local val = rawget(_base_0, name)
+      if val == nil and _parent_0 then
+        return _parent_0[name]
+      else
+        return val
+      end
+    end,
+    __call = function(cls, ...)
+      local _self_0 = setmetatable({}, _base_0)
+      cls.__init(_self_0, ...)
+      return _self_0
+    end
+  })
+  _base_0.__class = _class_0
+  if _parent_0 and _parent_0.__inherited then
+    _parent_0.__inherited(_parent_0, _class_0)
+  end
+  Application = _class_0
+end
+return {
+  Request = Request,
+  Application = Application
+}
examples/Lang.lhs view
@@ -1,368 +1,368 @@-Source: http://www.haskell.org/haskellwiki/Parsing_a_simple_imperative_language--This tutorial will present how to parse a subset of a simple imperative-programming language called W<small>HILE</small> (introduced in a book-"Principles of Program Analysis" by Nielson, Nielson and Hankin). It includes-only a few statements and basic boolean/arithmetic expressions, which makes it-a nice material for a tutorial.--== Imports ==--First let's specify the name of the module:--<haskell>--> module Lang where--</haskell>--And then import the necessary libraries:--<haskell>--> import System.IO-> import Control.Monad-> import Text.ParserCombinators.Parsec-> import Text.ParserCombinators.Parsec.Expr-> import Text.ParserCombinators.Parsec.Language-> import qualified Text.ParserCombinators.Parsec.Token as Token--</haskell>--== The language ==--The grammar for expressions is defined as follows:--<tt>--''a''   ::=  ''x'' | ''n'' | - ''a'' | ''a'' ''opa'' ''a''--''b''   ::=  true | false | not ''b'' | ''b'' ''opb'' ''b'' | ''a'' ''opr'' ''a''--''opa'' ::=  + | - | * | /--''opb'' ::=  and | or--''opr'' ::=  > | <--</tt>--Note that we have three groups of operators - arithmetic, booloan and-relational ones.--And now the definition of statements:--<tt>--''S''   ::=  x := ''a'' | skip | ''S1''; ''S2'' | ''( S )'' | if ''b'' then ''S1'' else ''S2'' | while ''b'' do ''S''--</tt>--We probably want to parse that into some internal representation of the-language (abstract syntax tree). Therefore we need to define the data-structures for the expressions and statements.--== Data structures ==--We need to take care of boolean and arithmetic expressions and the-appropriate operators. First let's look at the boolean expressions:--<haskell>--> data BExpr = BConst Bool->            | Not BExpr->            | And BExpr BExpr->            | Greater AExpr AExpr->             deriving (Show, Eq)--</haskell>--Now we define the types for arithmetic expressions:--<haskell>--> data AExpr = Var String->            | Const Integer->            | Neg AExpr->            | Add AExpr AExpr->              deriving (Show, Eq)--</haskell>--Finally let's take care of the statements:--<haskell>--> data Stmt = Seq [Stmt]->           | Assign String AExpr->           | If BExpr Stmt Stmt->           | While BExpr Stmt->           | Skip->             deriving (Show, Eq)--</haskell>--== Lexer ==--Having all the data structures we can go on with writing the code to do actual-parsing. First of all we create the language definition using Haskell's record-syntax and the constructor <hask>emptyDef</hask> (from-<hask>Text.ParserCombinators.Parsec.Language</hask>):--<haskell>--> languageDef =->   emptyDef { Token.commentStart    = "/*"->            , Token.commentEnd      = "*/"->            , Token.commentLine     = "//"->            , Token.identStart      = letter->            , Token.identLetter     = alphaNum->            , Token.reservedNames   = [ "if"->                                      , "then"->                                      , "else"->                                      , "while"->                                      , "do"->                                      , "skip"->                                      , "true"->                                      , "false"->                                      , "not"->                                      , "and"->                                      , "or"->                                      ]->            , Token.reservedOpNames = ["+", "-", "*", "/", ":="->                                      , "<", ">", "and", "or", "not"->                                      ]->            }--</haskell>--This creates a language definition that accepts the C-style comments, requires-that the identifiers start with a letter, and end with alphanumeric-characters. Moreover there is a number of reserved names, that cannot be used-by the identifiers.--Having the above definition we can create a lexer:--<haskell>--> lexer = Token.makeTokenParser languageDef--</haskell>--<tt>lexer</tt> contains a number of lexical parsers, that we can us to parse-identifiers, reserved words/operations, etc. Now we can select/extract them in-the following way:--<haskell>--> identifier = Token.identifier lexer -- parses an identifier-> reserved   = Token.reserved   lexer -- parses a reserved name-> reservedOp = Token.reservedOp lexer -- parses an operator-> parens     = Token.parens     lexer -- parses surrounding parenthesis:->                                     --   parens p->                                     -- takes care of the parenthesis and->                                     -- uses p to parse what's inside them-> integer    = Token.integer    lexer -- parses an integer-> semi       = Token.semi       lexer -- parses a semicolon-> whiteSpace = Token.whiteSpace lexer -- parses whitespace--</haskell>--This isn't really necessary, but should make the code much more readable (also-this is the reason why we used the qualified import of-<hask>Text.ParserCombinators.Parsec.Token</hask>). Now we can use them to-parse the source code at the token level. One of the nice features of these-parsers is that they take care of all whitespace after the tokens.--== Main parser ==--As already mentioned a program in this language is simply a statement, so the-main parser should basically only parse a statement. But remember to take care of-initial whitespace - our parsers only get rid of whitespace after the tokens!--<haskell>--> whileParser :: Parser Stmt-> whileParser = whiteSpace >> statement--</haskell>--Now because any statement might be actually a sequence of statements separated-by semicolon, we use <hask>sepBy1</hask> to parse at least one statement. The-result is a list of statements. We also allow grouping statements by the-parenthesis, which is useful, for instance, in the <tt>while</tt> loop.--<haskell>--> statement :: Parser Stmt-> statement =   parens statement->           <|> sequenceOfStmt--> sequenceOfStmt =->   do list <- (sepBy1 statement' semi)->      -- If there's only one statement return it without using Seq.->      return $ if length list == 1 then head list else Seq list--</haskell>--Now a single statement is quite simple, it's either an if conditional, a while-loop, an assignment or simply a skip statement. We use <hask><|></hask> to-express choice. So <hask>a <|> b</hask> will first try parser <hask>a</hask>-and if it fails (but without actually consuming any input) then parser-<hask>b</hask> will be used. Note: this means that the order is important.--<haskell>--> statement' :: Parser Stmt-> statement' =   ifStmt->            <|> whileStmt->            <|> skipStmt->            <|> assignStmt--</haskell>--If you have a parser that might fail after consuming some input, and you still-want to try the next parser, you should look into <hask>try</hask> combinator.-For instance <hask>try p <|> q</hask> will try parsing with <hask>p</hask> and-if it fails, even after consuming the input, the <hask>q</hask> parser will be-used as if nothing has been consumed by <hask>p</hask>.--Now let's define the parsers for all the possible statements. This is quite-straightforward as we just use the parsers from the lexer and then use all the-necessary information to create appropriate data structures.--<haskell>--> ifStmt :: Parser Stmt-> ifStmt =->   do reserved "if"->      cond  <- bExpression->      reserved "then"->      stmt1 <- statement->      reserved "else"->      stmt2 <- statement->      return $ If cond stmt1 stmt2--> whileStmt :: Parser Stmt-> whileStmt =->   do reserved "while"->      cond <- bExpression->      reserved "do"->      stmt <- statement->      return $ While cond stmt--> assignStmt :: Parser Stmt-> assignStmt =->   do var  <- identifier->      reservedOp ":="->      expr <- aExpression->      return $ Assign var expr--> skipStmt :: Parser Stmt-> skipStmt = reserved "skip" >> return Skip--</haskell>--== Expressions ==--What's left is to parse the expressions. Fortunately Parsec provides a very-easy way to do that. Let's define the arithmetic and boolean expressions:--<haskell>--> aExpression :: Parser AExpr-> aExpression = buildExpressionParser aOperators aTerm--> bExpression :: Parser BExpr-> bExpression = buildExpressionParser bOperators bTerm--</haskell>--Now we have to define the lists with operator precedence, associativity and-what constructors to use in each case.--<haskell>--> aOperators = [ [Prefix (reservedOp "-"   >> return (Neg             ))          ]->              , [Infix  (reservedOp "+"   >> return (Add             )) AssocLeft]->               ]--> bOperators = [ [Prefix (reservedOp "not" >> return (Not             ))          ]->              , [Infix  (reservedOp "and" >> return (And             )) AssocLeft]->              ]--</haskell>--In case of Prefix operators it is enough to specify which one should be parsed-and what is the associated data constructor. Infix operators are defined-similarly, but it's necessary to add information about associativity. Note-that the operator precedence depends only on the order of the elements in the-list.--Finally we have to define the terms. In case of arithmetic expressions, it is-quite simple:--<haskell>--> aTerm =  parens aExpression->      <|> liftM Var identifier->      <|> liftM Const integer--</haskell>--However, the term in a boolean expression is a bit more tricky. In this case,-a term can also be an expression with relational operator consisting of-arithmetic expressions.--<haskell>--> bTerm =  parens bExpression->      <|> (reserved "true"  >> return (BConst True ))->      <|> (reserved "false" >> return (BConst False))->      <|> rExpression--</haskell>--Therefore we have to define a parser for relational expressions:--<haskell>--> rExpression =->   do a1 <- aExpression->      op <- reservedOp ">"->      a2 <- aExpression->      return $ Greater a1 a2--</haskell>--And that's it. We have a quite simple parser able to parse a few statements and-arithmetic/boolean expressions.--== Notes ==--If you want to experiment with the parser inside ghci, these functions might be-handy:--<haskell>--> parseString :: String -> Stmt-> parseString str =->   case parse whileParser "" str of->     Left e  -> error $ show e->     Right r -> r--> parseFile :: String -> IO Stmt-> parseFile file =->   do program  <- readFile file->      case parse whileParser "" program of->        Left e  -> print e >> fail "parse error"->        Right r -> return r--</haskell>--Now you can simply load the module in ghci and then do-<hask>ast <- parseFile "<filename>"</hask> to parse a file and get the-result if parsing was successful. If you already have a string with-the program, you can use <hask>parseString</hask>.-+Source: http://www.haskell.org/haskellwiki/Parsing_a_simple_imperative_language
+
+This tutorial will present how to parse a subset of a simple imperative
+programming language called W<small>HILE</small> (introduced in a book
+"Principles of Program Analysis" by Nielson, Nielson and Hankin). It includes
+only a few statements and basic boolean/arithmetic expressions, which makes it
+a nice material for a tutorial.
+
+== Imports ==
+
+First let's specify the name of the module:
+
+<haskell>
+
+> module Lang where
+
+</haskell>
+
+And then import the necessary libraries:
+
+<haskell>
+
+> import System.IO
+> import Control.Monad
+> import Text.ParserCombinators.Parsec
+> import Text.ParserCombinators.Parsec.Expr
+> import Text.ParserCombinators.Parsec.Language
+> import qualified Text.ParserCombinators.Parsec.Token as Token
+
+</haskell>
+
+== The language ==
+
+The grammar for expressions is defined as follows:
+
+<tt>
+
+''a''   ::=  ''x'' | ''n'' | - ''a'' | ''a'' ''opa'' ''a''
+
+''b''   ::=  true | false | not ''b'' | ''b'' ''opb'' ''b'' | ''a'' ''opr'' ''a''
+
+''opa'' ::=  + | - | * | /
+
+''opb'' ::=  and | or
+
+''opr'' ::=  > | <
+
+</tt>
+
+Note that we have three groups of operators - arithmetic, booloan and
+relational ones.
+
+And now the definition of statements:
+
+<tt>
+
+''S''   ::=  x := ''a'' | skip | ''S1''; ''S2'' | ''( S )'' | if ''b'' then ''S1'' else ''S2'' | while ''b'' do ''S''
+
+</tt>
+
+We probably want to parse that into some internal representation of the
+language (abstract syntax tree). Therefore we need to define the data
+structures for the expressions and statements.
+
+== Data structures ==
+
+We need to take care of boolean and arithmetic expressions and the
+appropriate operators. First let's look at the boolean expressions:
+
+<haskell>
+
+> data BExpr = BConst Bool
+>            | Not BExpr
+>            | And BExpr BExpr
+>            | Greater AExpr AExpr
+>             deriving (Show, Eq)
+
+</haskell>
+
+Now we define the types for arithmetic expressions:
+
+<haskell>
+
+> data AExpr = Var String
+>            | Const Integer
+>            | Neg AExpr
+>            | Add AExpr AExpr
+>              deriving (Show, Eq)
+
+</haskell>
+
+Finally let's take care of the statements:
+
+<haskell>
+
+> data Stmt = Seq [Stmt]
+>           | Assign String AExpr
+>           | If BExpr Stmt Stmt
+>           | While BExpr Stmt
+>           | Skip
+>             deriving (Show, Eq)
+
+</haskell>
+
+== Lexer ==
+
+Having all the data structures we can go on with writing the code to do actual
+parsing. First of all we create the language definition using Haskell's record
+syntax and the constructor <hask>emptyDef</hask> (from
+<hask>Text.ParserCombinators.Parsec.Language</hask>):
+
+<haskell>
+
+> languageDef =
+>   emptyDef { Token.commentStart    = "/*"
+>            , Token.commentEnd      = "*/"
+>            , Token.commentLine     = "//"
+>            , Token.identStart      = letter
+>            , Token.identLetter     = alphaNum
+>            , Token.reservedNames   = [ "if"
+>                                      , "then"
+>                                      , "else"
+>                                      , "while"
+>                                      , "do"
+>                                      , "skip"
+>                                      , "true"
+>                                      , "false"
+>                                      , "not"
+>                                      , "and"
+>                                      , "or"
+>                                      ]
+>            , Token.reservedOpNames = ["+", "-", "*", "/", ":="
+>                                      , "<", ">", "and", "or", "not"
+>                                      ]
+>            }
+
+</haskell>
+
+This creates a language definition that accepts the C-style comments, requires
+that the identifiers start with a letter, and end with alphanumeric
+characters. Moreover there is a number of reserved names, that cannot be used
+by the identifiers.
+
+Having the above definition we can create a lexer:
+
+<haskell>
+
+> lexer = Token.makeTokenParser languageDef
+
+</haskell>
+
+<tt>lexer</tt> contains a number of lexical parsers, that we can us to parse
+identifiers, reserved words/operations, etc. Now we can select/extract them in
+the following way:
+
+<haskell>
+
+> identifier = Token.identifier lexer -- parses an identifier
+> reserved   = Token.reserved   lexer -- parses a reserved name
+> reservedOp = Token.reservedOp lexer -- parses an operator
+> parens     = Token.parens     lexer -- parses surrounding parenthesis:
+>                                     --   parens p
+>                                     -- takes care of the parenthesis and
+>                                     -- uses p to parse what's inside them
+> integer    = Token.integer    lexer -- parses an integer
+> semi       = Token.semi       lexer -- parses a semicolon
+> whiteSpace = Token.whiteSpace lexer -- parses whitespace
+
+</haskell>
+
+This isn't really necessary, but should make the code much more readable (also
+this is the reason why we used the qualified import of
+<hask>Text.ParserCombinators.Parsec.Token</hask>). Now we can use them to
+parse the source code at the token level. One of the nice features of these
+parsers is that they take care of all whitespace after the tokens.
+
+== Main parser ==
+
+As already mentioned a program in this language is simply a statement, so the
+main parser should basically only parse a statement. But remember to take care of
+initial whitespace - our parsers only get rid of whitespace after the tokens!
+
+<haskell>
+
+> whileParser :: Parser Stmt
+> whileParser = whiteSpace >> statement
+
+</haskell>
+
+Now because any statement might be actually a sequence of statements separated
+by semicolon, we use <hask>sepBy1</hask> to parse at least one statement. The
+result is a list of statements. We also allow grouping statements by the
+parenthesis, which is useful, for instance, in the <tt>while</tt> loop.
+
+<haskell>
+
+> statement :: Parser Stmt
+> statement =   parens statement
+>           <|> sequenceOfStmt
+
+> sequenceOfStmt =
+>   do list <- (sepBy1 statement' semi)
+>      -- If there's only one statement return it without using Seq.
+>      return $ if length list == 1 then head list else Seq list
+
+</haskell>
+
+Now a single statement is quite simple, it's either an if conditional, a while
+loop, an assignment or simply a skip statement. We use <hask><|></hask> to
+express choice. So <hask>a <|> b</hask> will first try parser <hask>a</hask>
+and if it fails (but without actually consuming any input) then parser
+<hask>b</hask> will be used. Note: this means that the order is important.
+
+<haskell>
+
+> statement' :: Parser Stmt
+> statement' =   ifStmt
+>            <|> whileStmt
+>            <|> skipStmt
+>            <|> assignStmt
+
+</haskell>
+
+If you have a parser that might fail after consuming some input, and you still
+want to try the next parser, you should look into <hask>try</hask> combinator.
+For instance <hask>try p <|> q</hask> will try parsing with <hask>p</hask> and
+if it fails, even after consuming the input, the <hask>q</hask> parser will be
+used as if nothing has been consumed by <hask>p</hask>.
+
+Now let's define the parsers for all the possible statements. This is quite
+straightforward as we just use the parsers from the lexer and then use all the
+necessary information to create appropriate data structures.
+
+<haskell>
+
+> ifStmt :: Parser Stmt
+> ifStmt =
+>   do reserved "if"
+>      cond  <- bExpression
+>      reserved "then"
+>      stmt1 <- statement
+>      reserved "else"
+>      stmt2 <- statement
+>      return $ If cond stmt1 stmt2
+
+> whileStmt :: Parser Stmt
+> whileStmt =
+>   do reserved "while"
+>      cond <- bExpression
+>      reserved "do"
+>      stmt <- statement
+>      return $ While cond stmt
+
+> assignStmt :: Parser Stmt
+> assignStmt =
+>   do var  <- identifier
+>      reservedOp ":="
+>      expr <- aExpression
+>      return $ Assign var expr
+
+> skipStmt :: Parser Stmt
+> skipStmt = reserved "skip" >> return Skip
+
+</haskell>
+
+== Expressions ==
+
+What's left is to parse the expressions. Fortunately Parsec provides a very
+easy way to do that. Let's define the arithmetic and boolean expressions:
+
+<haskell>
+
+> aExpression :: Parser AExpr
+> aExpression = buildExpressionParser aOperators aTerm
+
+> bExpression :: Parser BExpr
+> bExpression = buildExpressionParser bOperators bTerm
+
+</haskell>
+
+Now we have to define the lists with operator precedence, associativity and
+what constructors to use in each case.
+
+<haskell>
+
+> aOperators = [ [Prefix (reservedOp "-"   >> return (Neg             ))          ]
+>              , [Infix  (reservedOp "+"   >> return (Add             )) AssocLeft]
+>               ]
+
+> bOperators = [ [Prefix (reservedOp "not" >> return (Not             ))          ]
+>              , [Infix  (reservedOp "and" >> return (And             )) AssocLeft]
+>              ]
+
+</haskell>
+
+In case of Prefix operators it is enough to specify which one should be parsed
+and what is the associated data constructor. Infix operators are defined
+similarly, but it's necessary to add information about associativity. Note
+that the operator precedence depends only on the order of the elements in the
+list.
+
+Finally we have to define the terms. In case of arithmetic expressions, it is
+quite simple:
+
+<haskell>
+
+> aTerm =  parens aExpression
+>      <|> liftM Var identifier
+>      <|> liftM Const integer
+
+</haskell>
+
+However, the term in a boolean expression is a bit more tricky. In this case,
+a term can also be an expression with relational operator consisting of
+arithmetic expressions.
+
+<haskell>
+
+> bTerm =  parens bExpression
+>      <|> (reserved "true"  >> return (BConst True ))
+>      <|> (reserved "false" >> return (BConst False))
+>      <|> rExpression
+
+</haskell>
+
+Therefore we have to define a parser for relational expressions:
+
+<haskell>
+
+> rExpression =
+>   do a1 <- aExpression
+>      op <- reservedOp ">"
+>      a2 <- aExpression
+>      return $ Greater a1 a2
+
+</haskell>
+
+And that's it. We have a quite simple parser able to parse a few statements and
+arithmetic/boolean expressions.
+
+== Notes ==
+
+If you want to experiment with the parser inside ghci, these functions might be
+handy:
+
+<haskell>
+
+> parseString :: String -> Stmt
+> parseString str =
+>   case parse whileParser "" str of
+>     Left e  -> error $ show e
+>     Right r -> r
+
+> parseFile :: String -> IO Stmt
+> parseFile file =
+>   do program  <- readFile file
+>      case parse whileParser "" program of
+>        Left e  -> print e >> fail "parse error"
+>        Right r -> return r
+
+</haskell>
+
+Now you can simply load the module in ghci and then do
+<hask>ast <- parseFile "<filename>"</hask> to parse a file and get the
+result if parsing was successful. If you already have a string with
+the program, you can use <hask>parseString</hask>.
+
 [[Category:How to]]
examples/MultiRec.hs view
@@ -1,129 +1,120 @@-{-# LANGUAGE GADTs                 #-}-{-# LANGUAGE KindSignatures        #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE TypeFamilies          #-}-{-# LANGUAGE TypeOperators         #-}-{-# LANGUAGE TypeSynonymInstances  #-}-{-# LANGUAGE EmptyDataDecls        #-}-{-# LANGUAGE TemplateHaskell       #-}-{-# LANGUAGE FlexibleInstances     #-}-{-# LANGUAGE DataKinds             #-}-{-# LANGUAGE PolyKinds             #-}-{-# LANGUAGE TypeFamilies          #-}-{-# LANGUAGE StandaloneDeriving    #-}--module MultiRec where--import Datatypes-import Generics.MultiRec.Any-import Generics.MultiRec.Transformations.RewriteRules as RR-import Generics.MultiRec.Transformations.ZipperState-import Generics.MultiRec.Transformations.Explicit as Ex-import Generics.MultiRec.Transformations.TH-import Generics.MultiRec.Rewriting-import Generics.MultiRec.Zipper--import Generics.MultiRec hiding (show)-import Generics.MultiRec.TH--import Control.Monad ( (>=>) )------------------------------------------------------------------------------------- Multirec representations for the example datatypes----------------------------------------------------------------------------------data TreeAST :: * -> * where-  Tree :: TreeAST Tree--$(deriveAll ''TreeAST)--data ListAST :: * -> * -> * where-  List :: ListAST a (List a)--$(deriveAll ''ListAST)--data XAST :: * -> * where-  X :: XAST X--$(deriveAll ''XAST)--data ZigZag :: * -> * where-  Zig :: ZigZag Zig-  Zag :: ZigZag Zag--$(deriveAll ''ZigZag)--data AST i where-  BExpr  :: AST BExpr-  AExpr  :: AST AExpr-  Stmt   :: AST Stmt--$(deriveAll ''AST)------------------------------------------------------------------------------------- Rewrite rules solution----------------------------------------------------------------------------------instance RR.Transform AST---- Now we can simply do the above transformation in a nice way!-rr = RR.apply [insert (down >=> right >=> right) change] Stmt prog1 == Just prog2-  where-    change = rule $ \e a b -> If e a b :~> If (Not e) b a---- The same one in two steps, which illustrates that rules can be of different--- types-rr2 = RR.apply [ insert (down >=> right >=> right) swap-               , insert down addNot] Stmt prog1          == Just prog2-  where-    swap   :: Rule AST Stmt-    swap   = rule $ \e a b -> If e a b :~> If e b a-    addNot :: Rule AST BExpr-    addNot = rule $ \e -> e :~> Not e------------------------------------------------------------------------------------- Zipper with state----------------------------------------------------------------------------------zs = navigate Stmt prog1 $ do-  downMonad >> rightMonad >> rightMonad-  -- Swap-  l <- downMonad >> rightMonad-  r <- rightMonad-  updateMonad (\p _ -> matchAny p l)-  leftMonad-  updateMonad (\p _ -> matchAny p r)-  -- Add the not-  leftMonad-  updateMonad (\p e -> case p of-                  BExpr -> Just (Not e)-                  _     -> Nothing)------------------------------------------------------------------------------------- Explicit----------------------------------------------------------------------------------instance Ex.Transform AST---- Ordering index of AST as AExpr < BExpr < Stmt-instance OrdI AST where-  indexI AExpr = 0-  indexI BExpr = 1-  indexI Stmt  = 2--$(deriveRefRep ''AST (postfix "EH"))-deriving instance Show AExprEH-deriving instance Show BExprEH-deriving instance Show StmtEH---- Show existentials-instance Show (NiceInsert AST) where-  show (NiceInsert AExpr x l) = "NiceInsert AExpr " ++ show x ++ " (" ++ show l ++ ")"-  show (NiceInsert BExpr x l) = "NiceInsert BExpr " ++ show x ++ " (" ++ show l ++ ")"-  show (NiceInsert Stmt x l)  = "NiceInsert Stmt " ++ show x ++ " (" ++ show l ++ ")"---- Actual example-{- This prints: (note the different reference types here)-  [ NiceInsert BExpr [2,0] (NotEH (RefBExpr [2,0]))-  , NiceInsert Stmt [2,1] (RefStmt [2,2])-  , NiceInsert Stmt [2,2] (RefStmt [2,1]) ]--}-expl1 = print $ toNiceTransformation $ diff Stmt prog1 prog2-expl2 = print $ toNiceTransformation $ diff Stmt prog3 prog4-expl3 = print $ toNiceTransformation $ diff Stmt prog5 prog6+{-# LANGUAGE GADTs                 #-}
+{-# LANGUAGE KindSignatures        #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TypeFamilies          #-}
+{-# LANGUAGE TypeOperators         #-}
+{-# LANGUAGE TypeSynonymInstances  #-}
+{-# LANGUAGE EmptyDataDecls        #-}
+{-# LANGUAGE TemplateHaskell       #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE DataKinds             #-}
+{-# LANGUAGE PolyKinds             #-}
+{-# LANGUAGE TypeFamilies          #-}
+{-# LANGUAGE StandaloneDeriving    #-}
+{-# LANGUAGE PatternSynonyms       #-}
+{-# LANGUAGE ScopedTypeVariables   #-}
+{-# LANGUAGE RankNTypes            #-}
+
+module MultiRec where
+
+import Datatypes
+import Generics.MultiRec.Transformations.Main
+import Generics.MultiRec.Transformations.Path
+
+import Generics.MultiRec hiding ( show )
+import Generics.MultiRec.TH
+
+import Control.Monad ( (>=>) )
+
+--------------------------------------------------------------------------------
+-- Multirec representations for the example datatypes
+--------------------------------------------------------------------------------
+data TreeAST :: * -> * where
+  Tree :: TreeAST Tree
+
+$(deriveAll ''TreeAST)
+
+data ListAST :: * -> * -> * where
+  List :: ListAST a (List a)
+
+$(deriveAll ''ListAST)
+
+data XAST :: * -> * where
+  X :: XAST X
+
+$(deriveAll ''XAST)
+
+data ZigZag :: * -> * where
+  Zig :: ZigZag Zig
+  Zag :: ZigZag Zag
+
+$(deriveAll ''ZigZag)
+
+data AST i where
+  BExpr  :: AST BExpr
+  AExpr  :: AST AExpr
+  Stmt   :: AST Stmt
+
+$(deriveAll ''AST)
+
+--------------------------------------------------------------------------------
+-- Examples
+--------------------------------------------------------------------------------
+
+type instance Ixs AST = '[ AExpr, BExpr, Stmt ]
+
+deriving instance Ord AExpr
+deriving instance Ord BExpr
+deriving instance Ord Stmt
+
+test1 :: Transformation AST Stmt
+test1 = diff Stmt prog1 prog2
+
+-- Constructor pattern synonyms
+pattern Ref' :: Path phi ix top -> HWithRef phi top ix
+pattern Ref' x = HIn (Ref x)
+
+pattern Const' :: forall top. Integer -> HWithRef AST top AExpr
+pattern Const' i = HIn (InR (R (L (Tag (R (L (C (K i))))))))
+
+pattern BConst' :: forall top. Bool -> HWithRef AST top BExpr
+pattern BConst' b = HIn (InR (L (Tag (L (C (K b))))))
+
+pattern And' :: forall top. HWithRef AST top BExpr -> HWithRef AST top BExpr
+             -> HWithRef AST top BExpr
+pattern And' b1 b2 = HIn (InR (L (Tag (R (R (L (C (I b1 :*: I b2))))))))
+
+pattern GT' :: forall top. HWithRef AST top AExpr -> HWithRef AST top AExpr
+            -> HWithRef AST top BExpr
+pattern GT' a1 a2 = HIn (InR (L (Tag (R (R (R (C (I a1 :*: I a2))))))))
+
+test2 :: HWithRef AST top BExpr
+test2 = BConst' True
+
+test3 :: HWithRef AST top BExpr
+test3 = And' test2 test2
+
+-- test6 :: HWithRef AST BExpr BExpr
+-- test6 = And' (GT' (Ref' test7) (Const' 2)) (Ref' test4)
+
+-- Path pattern synonyms
+pattern End     = Empty
+pattern Not_0 p = Push BExpr (CL (CTag (CR (CL (CC CId))))) p
+
+pattern GT_0 :: Path AST top AExpr -> Path AST top BExpr
+pattern GT_0  p = Push AExpr (CL (CTag (CR (CR (CR (CC (C1 CId (I (K0 ()))))))))) p
+
+pattern Neg_0 :: Path AST top AExpr -> Path AST top AExpr
+pattern Neg_0 p = Push AExpr (CR (CL (CTag (CR (CR (CL (CC CId))))))) p
+
+test4 :: Path AST BExpr BExpr
+test4 = Not_0 (Not_0 End)
+
+test5 :: Path AST AExpr BExpr
+test5 = Not_0 (GT_0 End)
+
+test7 :: Path AST AExpr AExpr
+test7 = Neg_0 End
+
+--
+testPrgm = diff Stmt prog1 prog2
examples/Regular.hs view
@@ -1,212 +1,118 @@-{-# LANGUAGE FlexibleInstances  #-}-{-# LANGUAGE FlexibleContexts   #-}-{-# LANGUAGE TypeFamilies       #-}-{-# LANGUAGE TemplateHaskell    #-}-{-# LANGUAGE TypeOperators      #-}-{-# LANGUAGE StandaloneDeriving #-}-{-# LANGUAGE EmptyDataDecls     #-}-module Regular where--import Datatypes-import Generics.Regular hiding (right)-import Generics.Regular.Transformations.Explicit as Ex-import Generics.Regular.Transformations.RewriteRules as RR-import Generics.Regular.Zipper-import Generics.Regular.Transformations.ZipperState-import Generics.Regular.Transformations.TH--import Control.Monad ( (>=>) )-import Generics.Regular.Rewriting hiding (left, right)-import Data.Maybe (fromJust)------------------------------------------------------------------------------------- Regular representations for the example datatypes------------------------------------------------------------------------------------Trees-$(deriveAll ''Tree "PFTree")-type instance PF Tree = PFTree---- Lists-$(deriveAll ''List "PFL")-type instance PF (List a) = PFL a---- Something more exotic-$(deriveAll ''X "PFX")-type instance PF X = PFX---- Example for paper (do manual instance to avoid C's)-type instance PF Expr = K String :+: K Integer :+: I :+: I :*: I--instance Regular Expr where-  from  (Var s)       = L (K s)-  from  (Const i)     = R (L (K i))-  from  (Neg e)       = R (R (L (I e)))-  from  (Add e1 e2)   = R (R (R (I e1 :*: I e2)))--  to (L (K s))                    = Var s-  to (R (L (K i)))                = Const i-  to (R (R (L (I e))))            = Neg e-  to (R (R (R (I e1 :*: I e2))))  = Add e1 e2-------------------------------------------------------------------------------------- Examples for the paper------------------------------------------------------------------------------------ Some example values-expr1 :: Expr-expr1 = Add (Const 1) (Var "a")--expr2 :: Expr-expr2 = Add (Const 1) (Neg (Var "a"))--expr3 :: Expr-expr3 = Add (Var "a") (Const 1)--instance RR.Transform Expr-instance Ex.Transform Expr--instance Show (Fix (WithRef Expr)) where-  show (In (Ref p)) = "Ref " ++ show p---- Insertion (expr1 => expr2)-rewriteRulesIns :: Maybe Expr-rewriteRulesIns = RR.apply [(down >=> right, rule1)] expr1-  where rule1 :: Rule Expr-        rule1 = rule $ \x -> x :~> Neg x--zipperStateIns :: Maybe Expr-zipperStateIns = navigate expr1 $ do-  downMonad >> rightMonad-  updateMonad Neg--explicitIns :: Maybe Expr-explicitIns = Ex.apply addNeg expr1-  where addNeg :: Ex.Transformation Expr-        addNeg = [ ([1], In . InR . R . R . L . I . In $ Ref [1]) ]---- Deletion (expr2 => expr1)--rewriteRulesDel :: Maybe Expr-rewriteRulesDel = RR.apply [(down >=> right, rule2)] expr2-  where rule2 :: Rule Expr-        rule2 = rule $ \x -> Neg x :~> x--zipperStateDel :: Maybe Expr-zipperStateDel = navigate expr2 $ do-  r <- downMonad >> rightMonad >> downMonad-  upMonad-  updateMonad (const r)--explicitDel :: Maybe Expr-explicitDel = Ex.apply delNeg expr2-  where delNeg :: Ex.Transformation Expr-        delNeg = [ ([1], In (Ref [1,0])) ]---- Swapping (expr1 => expr3)-rewriteRulesSwap :: Maybe Expr-rewriteRulesSwap = RR.apply [(return, rule3)] expr1-  where rule3 :: Rule Expr-        rule3 = rule $ \l r -> Add l r :~> Add r l--zipperStateSwap :: Maybe Expr-zipperStateSwap = navigate expr1 $ do-  l <- downMonad-  r <- rightMonad-  updateMonad (const l)-  leftMonad-  updateMonad (const r)--explicitSwap :: Maybe Expr-explicitSwap = Ex.apply swap' expr1-  where swap' :: Ex.Transformation Expr-        swap' = [ ([0], In $ Ref [1]) -                , ([1], In $ Ref [0])]---- Rotation-rotate1 = Add (Var "a") (Add (Var "b") (Var "c"))-rotate2 = Add (Add (Var "a") (Var "b")) (Var "c")-rotate = diff rotate1 rotate2------------------------------------------------------------------------------------- Other RewriteRules examples----------------------------------------------------------------------------------instance RR.Transform Tree-instance RR.Transform X---- Test swapping two subtrees. Note the nice syntax!-swap :: Rule Tree-swap = rule $ \t1 t2 -> Bin t1 t2 :~> Bin t2 t1--t1 = RR.apply [(return        , swap)]      exTree4-t2 = RR.apply [(down          , swap)]      exTree4-t3 = RR.apply [(down >=> right, swap)]      exTree4-t4 = RR.apply [(down >=> right, swap), (return, swap)] exTree4 -- == id---- A tricky example-ruleSwapC, ruleAddB :: Rule X-ruleSwapC = rule $ \x y -> XC x y :~> XC y x-ruleAddB  = rule $ \x   -> XA x   :~> XA (XB x)--t6 = RR.apply [(down >=> down >=> down, ruleSwapC)] exX1-t7 = RR.apply [(down >=> down,          ruleAddB)]  (fromJust t6)-t8 = RR.apply [(return,                 ruleAddB)]  (fromJust t7)-t9 = t8 == Just exX2 -- True------------------------------------------------------------------------------------- Other ZipperState examples------------------------------------------------------------------------------------ An example using a zipper with state-t5 = navigate exTree4 $-       do downMonad >> downMonad-          saveMonad-          upMonad >> rightMonad >> downMonad >> rightMonad-          saveMonad-          x1 <- loadMonad-          updateMonad (const x1)-          upMonad >> leftMonad >> downMonad-          x2 <- loadMonad-          updateMonad (const x2)------------------------------------------------------------------------------------- A nicer interface for Expr, could be generated using Template Haskell----------------------------------------------------------------------------------data ExprEH-  = VarEH String-  | ConstEH Integer-  | NegEH ExprEH-  | AddEH ExprEH ExprEH-  | RefEH Path-  deriving Show--instance HasRef Expr where-  type RefRep Expr = ExprEH-  -  toRef (Ref p)                           = RefEH p-  toRef (InR (L (K s)))                   = VarEH s-  toRef (InR (R (L (K i))))               = ConstEH i-  toRef (InR (R (R (L (I e)))))           = NegEH e-  toRef (InR (R (R (R (I e1 :*: I e2))))) = AddEH e1 e2-  -  fromRef (RefEH p)      = Ref p-  fromRef (VarEH s)      = InR (L (K s))-  fromRef (ConstEH i)    = InR (R (L (K i)))-  fromRef (NegEH e)      = InR (R (R (L (I e))))-  fromRef (AddEH e1 e2)  = InR (R (R (R (I e1 :*: I e2))))---$(deriveRefRep ''Tree (postfix "EH"))-deriving instance Show TreeEH---- Test-instance Ex.Transform Tree-treeSwapNice :: Maybe Tree-treeSwapNice = Ex.apply (fromNiceTransformation swap) exTree1-  where swap = [([0],RefTree [1]),([1],RefTree [0])]--treeDiff :: Ex.NiceTransformation Tree-treeDiff = toNiceTransformation $ Ex.diff exTree3 exTree5--explicitInsNice :: Maybe Expr-explicitInsNice = Ex.apply (fromNiceTransformation addNeg) expr1-  where addNeg :: NiceTransformation Expr-        addNeg = [ ([1], NegEH (RefEH [1])) ]+{-# LANGUAGE FlexibleInstances  #-}
+{-# LANGUAGE FlexibleContexts   #-}
+{-# LANGUAGE TypeFamilies       #-}
+{-# LANGUAGE TemplateHaskell    #-}
+{-# LANGUAGE TypeOperators      #-}
+{-# LANGUAGE StandaloneDeriving #-}
+{-# LANGUAGE EmptyDataDecls     #-}
+{-# LANGUAGE PatternSynonyms    #-}
+
+module Regular where
+
+import Datatypes
+import Generics.Regular hiding (right)
+import Generics.Regular.Transformations.Main
+import Generics.Regular.Zipper
+
+import Control.Monad ( (>=>) )
+import Data.Maybe (fromJust)
+
+--------------------------------------------------------------------------------
+-- Regular representations for the example datatypes
+--------------------------------------------------------------------------------
+--Trees
+$(deriveAll ''Tree "PFTree")
+type instance PF Tree = PFTree
+
+-- Lists
+$(deriveAll ''List "PFL")
+type instance PF (List a) = PFL a
+
+-- Something more exotic
+$(deriveAll ''X "PFX")
+type instance PF X = PFX
+
+-- Example for paper
+$(deriveAll ''AExpr "PFExpr")
+type instance PF Expr = PFExpr
+
+deriving instance Ord AExpr
+
+-- Datatype patterns
+pattern Var'   x = In (InR (L (C (K x))))
+pattern Const' x = In (InR (R (L (C (K x)))))
+pattern Neg'   x = In (InR (R (R (L (C (I x))))))
+pattern Add' x y = In (InR (R (R (R (C (I x :*: I y))))))
+
+pattern Ref' x  = In (Ref x)
+
+-- Path patterns
+pattern End     = []
+pattern Neg_0 x = CR (CR (CL (CC CId))) : x
+pattern Add_0 x = CR (CR (CR (CC (C1 CId (I ()))))) : x
+pattern Add_1 x = CR (CR (CR (CC (C2 (I ()) CId)))) : x
+
+{-
+type instance PF Expr = K String :+: K Integer :+: I :+: I :*: I
+
+instance Regular Expr where
+  from  (Var s)       = L (K s)
+  from  (Const i)     = R (L (K i))
+  from  (Neg e)       = R (R (L (I e)))
+  from  (Add e1 e2)   = R (R (R (I e1 :*: I e2)))
+
+  to (L (K s))                    = Var s
+  to (R (L (K i)))                = Const i
+  to (R (R (L (I e))))            = Neg e
+  to (R (R (R (I e1 :*: I e2))))  = Add e1 e2
+-}
+
+--------------------------------------------------------------------------------
+-- Examples for the paper
+--------------------------------------------------------------------------------
+instance Transform Expr
+
+-- Some example values
+expr1 :: Expr
+expr1 = Add (Const 1) (Var "a")
+
+expr2 :: Expr
+expr2 = Add (Const 1) (Neg (Var "a"))
+
+expr3 :: Expr
+expr3 = Add (Var "a") (Const 1)
+
+explicitIns :: Maybe Expr
+explicitIns = apply addNeg expr1
+  where addNeg :: Transformation Expr
+        addNeg = diff expr1 expr2
+
+-- Testing the nicer notation for paths and expressions with references
+test1 :: Fix (WithRef Expr)
+test1 = Neg' (Ref' test2)
+
+test2 :: Path Expr
+test2 = Add_1 End
+
+test3 :: Transformation Expr
+test3 = [(test2, test1)]
+
+test4 :: Bool
+test4 = show test3 == show (diff expr1 expr2)
+
+-- Deletion (expr2 => expr1)
+explicitDel :: Maybe Expr
+explicitDel = apply delNeg expr2
+  where delNeg :: Transformation Expr
+        delNeg = diff expr2 expr1 --[ ([1], In (Ref [1,0])) ]
+
+-- Swapping (expr1 => expr3)
+explicitSwap :: Maybe Expr
+explicitSwap = apply swap' expr1
+  where swap' :: Transformation Expr
+        swap' = diff expr1 expr3 -- [ ([0], In $ Ref [1]), ([1], In $ Ref [0])]
+
+-- Rotation
+rotate1 = Add (Var "a") (Add (Var "b") (Var "c"))
+rotate2 = Add (Add (Var "a") (Var "b")) (Var "c")
+rotate = diff rotate1 rotate2
transformations.cabal view
@@ -1,57 +1,144 @@-name:                transformations-version:             0.1.1.0-synopsis:            Generic representation of tree transformations-description:-  This library is based on ideas described in the paper:-  .-  *  Jeroen Bransen and Jose Pedro Magalhaes.-     /Generic Representations of Tree Transformations/.-     WGP'13.-     <http://dreixel.net/research/pdf/grtt.pdf>--license:             GPL-3-license-file:        LICENSE-author:              Jeroen Bransen and Jose Pedro Magalhaes-maintainer:          generics@haskell.org--- copyright:           -category:            Language-build-type:          Simple-cabal-version:       >=1.8---extra-source-files:    examples/Datatypes.hs-                       examples/Lang.lhs-                       examples/Regular.hs-                       examples/MultiRec.hs--library-  exposed-modules:-                       -- Regular part-                       Generics.Regular.Zipper,-                       Generics.Regular.Functions.GOrd,-                       Generics.Regular.Transformations.Explicit,-                       Generics.Regular.Transformations.TH,-                       Generics.Regular.Transformations.RewriteRules,-                       Generics.Regular.Transformations.ZipperState,-  -                       -- MultiRec implementation-                       Generics.MultiRec.Any,-                       Generics.MultiRec.Ord,-                       Generics.MultiRec.Transformations.ZipperState,-                       Generics.MultiRec.Transformations.RewriteRules,-                       Generics.MultiRec.Transformations.Explicit,-                       Generics.MultiRec.Transformations.TH,--                       -- Rewriting library for MultiRec-                       Generics.MultiRec.HZip,-                       Generics.MultiRec.LR,-                       Generics.MultiRec.Rewriting,-                       Generics.MultiRec.Rewriting.Machinery,-                       Generics.MultiRec.Rewriting.Rules--  -- other-modules:       -  build-depends:       base >= 4 && < 5, mtl >= 2.1,-                       regular >= 0.3, rewriting >= 0.2,-                       multirec >= 0.7.3, zipper >= 0.4.2,-                       parsec >= 3.1, containers >= 0.1,-                       template-haskell >= 2.7+name:                transformations
+version:             0.2.0.0
+synopsis:            Generic representation of tree transformations
+description:
+  This library is based on ideas described in the paper:
+  .
+  *  Jeroen Bransen and Jose Pedro Magalhaes.
+     /Generic Representations of Tree Transformations/.
+     <http://dreixel.net/research/pdf/grtt_jfp_draft.pdf>
+
+license:             GPL-3
+license-file:        LICENSE
+author:              Jeroen Bransen and Jose Pedro Magalhaes
+maintainer:          generics@haskell.org
+-- copyright:
+category:            Language
+build-type:          Simple
+cabal-version:       >=1.8
+
+extra-source-files:    README
+                       QuickCheck.hs
+                       examples/Datatypes.hs
+                       examples/Expr.hs
+                       examples/Lang.lhs
+                       examples/Regular.hs
+                       examples/MultiRec.hs
+                       examples/LUA.hs
+                       examples/LUA_project/application.lua.1
+                       examples/LUA_project/application.lua.2
+                       examples/LUA_project/application.lua.3
+                       examples/LUA_project/application.lua.4
+                       examples/LUA_project/application.lua.5
+                       examples/LUA_project/application.lua.6
+                       examples/LUA_project/application.lua.7
+                       examples/LUA_project/application.lua.8
+                       examples/LUA_project/application.lua.9
+                       examples/LUA_project/application.lua.10
+                       examples/LUA_project/application.lua.11
+                       examples/LUA_project/application.lua.12
+                       examples/LUA_project/application.lua.13
+                       examples/LUA_project/application.lua.14
+                       examples/LUA_project/application.lua.15
+                       examples/LUA_project/application.lua.16
+                       examples/LUA_project/application.lua.17
+                       examples/LUA_project/application.lua.18
+                       examples/LUA_project/application.lua.19
+                       examples/LUA_project/application.lua.20
+                       examples/LUA_project/application.lua.21
+                       examples/LUA_project/application.lua.22
+                       examples/LUA_project/application.lua.23
+                       examples/LUA_project/application.lua.24
+                       examples/LUA_project/application.lua.25
+                       examples/LUA_project/application.lua.26
+                       examples/LUA_project/application.lua.27
+                       examples/LUA_project/application.lua.28
+                       examples/LUA_project/application.lua.29
+                       examples/LUA_project/application.lua.30
+                       examples/LUA_project/application.lua.31
+                       examples/LUA_project/application.lua.32
+                       examples/LUA_project/application.lua.33
+                       examples/LUA_project/application.lua.34
+                       examples/LUA_project/application.lua.35
+                       examples/LUA_project/application.lua.36
+                       examples/LUA_project/application.lua.37
+                       examples/LUA_project/application.lua.38
+                       examples/LUA_project/application.lua.39
+                       examples/LUA_project/application.lua.40
+                       examples/LUA_project/application.lua.41
+                       examples/LUA_project/application.lua.42
+                       examples/LUA_project/application.lua.43
+                       examples/LUA_project/application.lua.44
+                       examples/LUA_project/application.lua.45
+                       examples/LUA_project/application.lua.46
+                       examples/LUA_project/application.lua.47
+                       examples/LUA_project/application.lua.48
+                       examples/LUA_project/application.lua.49
+                       examples/LUA_project/application.lua.50
+                       examples/LUA_project/application.lua.51
+                       examples/LUA_project/application.lua.52
+                       examples/LUA_project/application.lua.53
+                       examples/LUA_project/application.lua.54
+                       examples/LUA_project/application.lua.55
+                       examples/LUA_project/application.lua.56
+                       examples/LUA_project/application.lua.57
+                       examples/LUA_project/application.lua.58
+                       examples/LUA_project/application.lua.59
+                       examples/LUA_project/application.lua.60
+                       examples/LUA_project/application.lua.61
+                       examples/LUA_project/application.lua.62
+                       examples/LUA_project/application.lua.63
+                       examples/LUA_project/application.lua.64
+                       examples/LUA_project/application.lua.65
+                       examples/LUA_project/application.lua.66
+                       examples/LUA_project/application.lua.67
+                       examples/LUA_project/application.lua.68
+                       examples/LUA_project/application.lua.69
+                       examples/LUA_project/application.lua.70
+                       examples/LUA_project/application.lua.71
+                       examples/LUA_project/application.lua.72
+                       examples/LUA_project/application.lua.73
+                       examples/LUA_project/application.lua.74
+                       examples/LUA_project/application.lua.75
+                       examples/LUA_project/application.lua.76
+                       examples/LUA_project/application.lua.77
+                       examples/LUA_project/application.lua.78
+                       examples/LUA_project/application.lua.79
+                       examples/LUA_project/application.lua.80
+                       examples/LUA_project/application.lua.81
+                       examples/LUA_project/application.lua.82
+                       examples/LUA_project/application.lua.83
+                       examples/LUA_project/application.lua.84
+                       examples/LUA_project/application.lua.85
+                       examples/LUA_project/application.lua.86
+                       examples/LUA_project/application.lua.87
+                       examples/LUA_project/application.lua.88
+                       examples/LUA_project/application.lua.89
+                       examples/LUA_project/application.lua.90
+
+library
+  exposed-modules:     Generics.MultiRec.ShallowEq,
+                       Generics.MultiRec.Zipper,
+                       Generics.MultiRec.CountIs,
+
+                       Generics.MultiRec.Transformations.Main,
+                       Generics.MultiRec.Transformations.Path,
+                       Generics.MultiRec.Transformations.MemoTable,
+                       Generics.MultiRec.Transformations.Children,
+                       Generics.MultiRec.Transformations.ZipChildren
+
+                       Generics.Regular.Zipper
+                       Generics.Regular.Functions.GOrd
+                       Generics.Regular.Transformations.Main
+
+  build-depends:       base >= 4.7 && < 5, mtl >= 2.1, regular >= 0.3.4.4,
+                       multirec >= 0.7.3, containers >= 0.1,
+                       template-haskell >= 2.9
+
+executable Benchmark
+  main-is:             QuickCheck.hs
+
+  build-depends:       base >= 4.7 && < 5, mtl >= 2.1,
+                       multirec >= 0.7.3,
+                       parsec >= 3.1, containers >= 0.1,
+                       criterion >= 1, QuickCheck >= 2.7