-------------------------------------------------------------------------------- -- Title: LWObjectService.lua -- Description: Like a square peg in a round hole -- Author: Raphaël Szwarc http://alt.textdrive.com/lua/ -- Creation Date: February 1, 2005 -- Legal: Copyright (C) 2005 Raphaël Szwarc -------------------------------------------------------------------------------- -- import dependencies local LWService = require( "LWService" ) local LWComponent = require( "LWComponent" ) local LWFormInput = require( "LWFormInput" ) local LWFormSubmit = require( "LWFormSubmit" ) local LWResponse = require( "LWResponse" ) local LUList = require( "LUList" ) local LULog = require( "LULog" ) local LUMap = require( "LUMap" ) local LUObject = require( "LUObject" ) local LUString = require( "LUString" ) local LUURI = require( "LUURI" ) -- define the class local super = LWService local self = super() -- class variable(s) local _services = LUMap() -- initialization method function self:init( aPrefix, aType, anAuthenticator ) self = super.init( self, aPrefix, anAuthenticator ) self._type = aType if aType ~= nil then self:services():put( aType, self ) end return self end -- method access this service type function self:type() return self._type end -- method to define an object for a given path function self:objectWithPath( aPath, aContext ) return nil end -- method to define a path for a given a object function self:pathWithObject( anObject, aContext ) return self:prefix() end -- method to define a value for a given a object function self:valueWithObject( anObject, aContext ) return anObject:toString() end -- method to define a title for given a object function self:titleWithObject( anObject, aContext ) return nil end -- method to define a component for a given object function self:componentWithObject( anObject, aContext ) if anObject ~= nil then return LWComponent:componentWithObject( anObject, aContext ) end return nil end -- method to find an action value for a given context function self:actionValueWithContext( aContext ) local someParameters = aContext:request():parameters() local aPrefix = LWFormSubmit:prefix() for aKey, aValue in someParameters:iterator() do if LUString:startsWith( aKey, aPrefix ) == true then return aKey:sub( aPrefix:len() + 1 ) end end return nil end -- method to find all the actions for a given object path function self:actionValuesWithObjectPath( anObject, aPath, aContext ) local aPath = self:prefix() .. ( aPath or "" ) local anObjectPath = LUURI:decode( self:pathWithObject( anObject ) ) local someActions = LUList() if aPath ~= nil and anObjectPath ~= nil then aPath = aPath:sub( anObjectPath:len(), aPath:len() ) end if aPath ~= nil and aPath:len() > 0 then for anAction in aPath:gmatch( "([^%/]+)" ) do if anAction:len() > 0 then someActions:add( anAction ) end end end return someActions end -- method to decompose an action value into its name and parameters component function self:actionWithValue( aValue ) if aValue ~= nil then local someComponents = LUString:components( aValue, "." ) if someComponents:size() > 0 then local anAction = someComponents:get() someComponents:remove() if LUString:endsWith( anAction, "Action" ) == false then anAction = anAction .. "Action" end return anAction, someComponents end end return nil end -- method to find a component for a given object path function self:componentWithObjectPath( anObject, aPath, aContext ) local aComponent = self:componentWithObject( anObject, aContext ) if aComponent ~= nil then local someActionValues = self:actionValuesWithObjectPath( anObject, aPath, aContext ) local aCount = someActionValues:size() LULog:debug( someActionValues ) for anIndex, aValue in someActionValues:iterator() do local anAction, someParameters = self:actionWithValue( aValue ) if aComponent:respondsTo( anAction ) == true then local anotherComponent = aComponent:invoke( anAction, unpack( someParameters:content() ) ) if self:isObject( anotherComponent ) == true then aComponent = anotherComponent elseif anIndex == aCount then aComponent = tostring( anotherComponent ) break else return nil end else LULog:debug( ( "'%s' does not implement '%s'" ):format( aComponent:className(), anAction ) ) return nil end end end if LUObject:isObject( aComponent ) == true and aComponent:isKindOf( LWComponent ) then local aValue = self:actionValueWithContext( aContext ) LWFormInput:handleComponent( aComponent ) LULog:debug( aValue ) if aValue ~= nil then local anAction, someParameters = self:actionWithValue( aValue ) if aComponent:respondsTo( anAction ) == true then aComponent = aComponent:invoke( anAction, unpack( someParameters:content() ) ) else LULog:debug( ( "'%s' does not implement '%s'" ):format( aComponent:className(), anAction ) ) return nil end end end return aComponent end -- method to define a location for a given object function self:locationWithObject( anObject, aContext ) return nil end -- method to handle a GET method function self:handleGet( aContext, hasContent ) return self:handleInvocation( aContext, hasContent ) end -- method to handle a POST method function self:handlePost( aContext ) return self:handleInvocation( aContext ) end -- method to handle an object invocation function self:handleInvocation( aContext, hasContent ) local aResponse = aContext:response() local aPath = self:pathWithContext( aContext ) local anObject = self:objectWithPath( aPath, aContext ) local aLocation = self:locationWithObject( anObject, aContext ) if aLocation == nil then local aComponent = self:componentWithObjectPath( anObject, aPath, aContext ) if aComponent ~= nil then if self:isObject( aComponent ) == true and aComponent:isKindOf( LWComponent) == true then aLocation = aComponent:location() if aLocation == nil then aResponse:headers():put( "cache-control", "private, no-cache, no-store" ) aResponse:writeContent( aComponent:toString(), hasContent ) else aResponse:headers():put( "location", aLocation ) aResponse:writeStatus( 302, hasContent ) end else aResponse:headers():put( "cache-control", "private, no-cache, no-store" ) aResponse:writeContent( aComponent, hasContent ) end else aResponse:writeStatus( 404, hasContent ) end else aResponse:headers():put( "location", aLocation ) aResponse:writeStatus( 302, hasContent ) end return self end -- method to access the services function self:services() return _services end -- method to access the service for a given object function self:serviceWithObject( anObject, aContext ) if anObject ~= nil then return self:services():get( anObject:className() ) end return nil end return self