Big web framework refactoring
parent
81d417f265
commit
9bd38767ab
|
@ -1,9 +1,16 @@
|
||||||
! Copyright (C) 2008 Slava Pestov.
|
! Copyright (C) 2008 Slava Pestov.
|
||||||
! See http://factorcode.org/license.txt for BSD license.
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
USING: accessors sequences kernel assocs combinators http.server
|
USING: accessors sequences kernel assocs combinators
|
||||||
validators http hashtables namespaces fry continuations locals
|
validators http hashtables namespaces fry continuations locals
|
||||||
boxes xml.entities html.elements html.components
|
io arrays math boxes
|
||||||
html.templates.chloe io arrays math ;
|
xml.entities
|
||||||
|
http.server
|
||||||
|
http.server.responses
|
||||||
|
furnace
|
||||||
|
html.elements
|
||||||
|
html.components
|
||||||
|
html.templates.chloe
|
||||||
|
html.templates.chloe.syntax ;
|
||||||
IN: furnace.actions
|
IN: furnace.actions
|
||||||
|
|
||||||
SYMBOL: params
|
SYMBOL: params
|
||||||
|
@ -92,9 +99,3 @@ TUPLE: page-action < action template ;
|
||||||
: <page-action> ( -- page )
|
: <page-action> ( -- page )
|
||||||
page-action new-action
|
page-action new-action
|
||||||
dup '[ , template>> <chloe-content> ] >>display ;
|
dup '[ , template>> <chloe-content> ] >>display ;
|
||||||
|
|
||||||
TUPLE: feed-action < action feed ;
|
|
||||||
|
|
||||||
: <feed-action> ( -- feed )
|
|
||||||
feed-action new-action
|
|
||||||
dup '[ , feed>> call <feed-content> ] >>display ;
|
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
! See http://factorcode.org/license.txt for BSD license.
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
USING: accessors assocs namespaces kernel sequences sets
|
USING: accessors assocs namespaces kernel sequences sets
|
||||||
http.server
|
http.server
|
||||||
|
http.server.filters
|
||||||
|
http.server.dispatchers
|
||||||
furnace.sessions
|
furnace.sessions
|
||||||
furnace.auth.providers ;
|
furnace.auth.providers ;
|
||||||
IN: furnace.auth
|
IN: furnace.auth
|
||||||
|
|
|
@ -18,6 +18,10 @@ html.elements
|
||||||
urls
|
urls
|
||||||
http
|
http
|
||||||
http.server
|
http.server
|
||||||
|
http.server.dispatchers
|
||||||
|
http.server.filters
|
||||||
|
http.server.responses
|
||||||
|
furnace
|
||||||
furnace.auth
|
furnace.auth
|
||||||
furnace.auth.providers
|
furnace.auth.providers
|
||||||
furnace.auth.providers.db
|
furnace.auth.providers.db
|
||||||
|
@ -60,7 +64,7 @@ M: user-saver dispose
|
||||||
|
|
||||||
! ! ! Login
|
! ! ! Login
|
||||||
: successful-login ( user -- response )
|
: successful-login ( user -- response )
|
||||||
username>> set-uid "$login" end-flow ;
|
username>> set-uid URL" $login" end-flow ;
|
||||||
|
|
||||||
: login-failed ( -- * )
|
: login-failed ( -- * )
|
||||||
"invalid username or password" validation-error
|
"invalid username or password" validation-error
|
||||||
|
@ -68,7 +72,7 @@ M: user-saver dispose
|
||||||
|
|
||||||
: <login-action> ( -- action )
|
: <login-action> ( -- action )
|
||||||
<page-action>
|
<page-action>
|
||||||
"$login/login" >>template
|
{ login "login" } >>template
|
||||||
|
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
@ -97,7 +101,7 @@ M: user-saver dispose
|
||||||
|
|
||||||
: <register-action> ( -- action )
|
: <register-action> ( -- action )
|
||||||
<page-action>
|
<page-action>
|
||||||
"$login/register" >>template
|
{ login "register" } >>template
|
||||||
|
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
@ -138,7 +142,7 @@ M: user-saver dispose
|
||||||
tri
|
tri
|
||||||
] >>init
|
] >>init
|
||||||
|
|
||||||
"$login/edit-profile" >>template
|
{ login "edit-profile" } >>template
|
||||||
|
|
||||||
[
|
[
|
||||||
uid "username" set-value
|
uid "username" set-value
|
||||||
|
@ -173,7 +177,7 @@ M: user-saver dispose
|
||||||
|
|
||||||
drop
|
drop
|
||||||
|
|
||||||
"$login" end-flow
|
URL" $login" end-flow
|
||||||
] >>submit ;
|
] >>submit ;
|
||||||
|
|
||||||
! ! ! Password recovery
|
! ! ! Password recovery
|
||||||
|
@ -219,7 +223,7 @@ SYMBOL: lost-password-from
|
||||||
|
|
||||||
: <recover-action-1> ( -- action )
|
: <recover-action-1> ( -- action )
|
||||||
<page-action>
|
<page-action>
|
||||||
"$login/recover-1" >>template
|
{ login "recover-1" } >>template
|
||||||
|
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
@ -240,7 +244,7 @@ SYMBOL: lost-password-from
|
||||||
|
|
||||||
: <recover-action-2> ( -- action )
|
: <recover-action-2> ( -- action )
|
||||||
<page-action>
|
<page-action>
|
||||||
"$login/recover-2" >>template ;
|
{ login "recover-2" } >>template ;
|
||||||
|
|
||||||
: <recover-action-3> ( -- action )
|
: <recover-action-3> ( -- action )
|
||||||
<page-action>
|
<page-action>
|
||||||
|
@ -251,7 +255,7 @@ SYMBOL: lost-password-from
|
||||||
} validate-params
|
} validate-params
|
||||||
] >>init
|
] >>init
|
||||||
|
|
||||||
"$login/recover-3" >>template
|
{ login "recover-3" } >>template
|
||||||
|
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
@ -273,20 +277,20 @@ SYMBOL: lost-password-from
|
||||||
|
|
||||||
URL" $login/recover-4" <redirect>
|
URL" $login/recover-4" <redirect>
|
||||||
] [
|
] [
|
||||||
<400>
|
<403>
|
||||||
] if*
|
] if*
|
||||||
] >>submit ;
|
] >>submit ;
|
||||||
|
|
||||||
: <recover-action-4> ( -- action )
|
: <recover-action-4> ( -- action )
|
||||||
<page-action>
|
<page-action>
|
||||||
"$login/recover-4" >>template ;
|
{ login "recover-4" } >>template ;
|
||||||
|
|
||||||
! ! ! Logout
|
! ! ! Logout
|
||||||
: <logout-action> ( -- action )
|
: <logout-action> ( -- action )
|
||||||
<action>
|
<action>
|
||||||
[
|
[
|
||||||
f set-uid
|
f set-uid
|
||||||
"$login/login" end-flow
|
URL" $login" end-flow
|
||||||
] >>submit ;
|
] >>submit ;
|
||||||
|
|
||||||
! ! ! Authentication logic
|
! ! ! Authentication logic
|
||||||
|
@ -320,7 +324,7 @@ M: login call-responder* ( path responder -- response )
|
||||||
|
|
||||||
: <login-boilerplate> ( responder -- responder' )
|
: <login-boilerplate> ( responder -- responder' )
|
||||||
<boilerplate>
|
<boilerplate>
|
||||||
"$login/boilerplate" >>template ;
|
{ login "boilerplate" } >>template ;
|
||||||
|
|
||||||
: <login> ( responder -- auth )
|
: <login> ( responder -- auth )
|
||||||
login new-dispatcher
|
login new-dispatcher
|
||||||
|
|
|
@ -30,11 +30,11 @@
|
||||||
</t:form>
|
</t:form>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<t:if code="http.server.auth.login:login-failed?">
|
<t:if t:code="furnace.auth.login:allow-registration?">
|
||||||
<t:a t:href="register">Register</t:a>
|
<t:a t:href="register">Register</t:a>
|
||||||
</t:if>
|
</t:if>
|
||||||
|
|
|
|
||||||
<t:if code="http.server.auth.login:allow-password-recovery?">
|
<t:if t:code="furnace.auth.login:allow-password-recovery?">
|
||||||
<t:a t:href="recover-password">Recover Password</t:a>
|
<t:a t:href="recover-password">Recover Password</t:a>
|
||||||
</t:if>
|
</t:if>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
! Copyright (c) 2008 Slava Pestov
|
! Copyright (c) 2008 Slava Pestov
|
||||||
! See http://factorcode.org/license.txt for BSD license.
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
USING: accessors kernel namespaces http.server html.templates
|
USING: accessors kernel namespaces
|
||||||
html.templates.chloe locals ;
|
html.templates html.templates.chloe
|
||||||
|
locals
|
||||||
|
http.server
|
||||||
|
http.server.filters
|
||||||
|
furnace ;
|
||||||
IN: furnace.boilerplate
|
IN: furnace.boilerplate
|
||||||
|
|
||||||
TUPLE: boilerplate < filter-responder template ;
|
TUPLE: boilerplate < filter-responder template ;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
! Copyright (C) 2008 Slava Pestov.
|
! Copyright (C) 2008 Slava Pestov.
|
||||||
! See http://factorcode.org/license.txt for BSD license.
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
USING: db db.pools io.pools http.server furnace.sessions
|
USING: kernel accessors continuations namespaces destructors
|
||||||
kernel accessors continuations namespaces destructors ;
|
db db.pools io.pools http.server http.server.filters
|
||||||
|
furnace.sessions ;
|
||||||
IN: furnace.db
|
IN: furnace.db
|
||||||
|
|
||||||
TUPLE: db-persistence < filter-responder pool ;
|
TUPLE: db-persistence < filter-responder pool ;
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
! See http://factorcode.org/license.txt for BSD license.
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
USING: accessors namespaces sequences arrays kernel
|
USING: accessors namespaces sequences arrays kernel
|
||||||
assocs assocs.lib hashtables math.parser urls combinators
|
assocs assocs.lib hashtables math.parser urls combinators
|
||||||
html.elements http http.server furnace.sessions
|
furnace http http.server http.server.filters furnace.sessions
|
||||||
html.templates.chloe.syntax ;
|
html.elements html.templates.chloe.syntax ;
|
||||||
IN: furnace.flows
|
IN: furnace.flows
|
||||||
|
|
||||||
TUPLE: flows < filter-responder ;
|
TUPLE: flows < filter-responder ;
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
IN: furnace.tests
|
||||||
|
USING: http.server.dispatchers http.server.responses
|
||||||
|
http.server furnace tools.test kernel namespaces accessors ;
|
||||||
|
TUPLE: funny-dispatcher < dispatcher ;
|
||||||
|
|
||||||
|
: <funny-dispatcher> funny-dispatcher new-dispatcher ;
|
||||||
|
|
||||||
|
TUPLE: base-path-check-responder ;
|
||||||
|
|
||||||
|
C: <base-path-check-responder> base-path-check-responder
|
||||||
|
|
||||||
|
M: base-path-check-responder call-responder*
|
||||||
|
2drop
|
||||||
|
"$funny-dispatcher" resolve-base-path
|
||||||
|
"text/plain" <content> ;
|
||||||
|
|
||||||
|
[ ] [
|
||||||
|
<dispatcher>
|
||||||
|
<dispatcher>
|
||||||
|
<funny-dispatcher>
|
||||||
|
<base-path-check-responder> "c" add-responder
|
||||||
|
"b" add-responder
|
||||||
|
"a" add-responder
|
||||||
|
main-responder set
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
[ "/a/b/" ] [
|
||||||
|
V{ } responder-nesting set
|
||||||
|
"a/b/c" split-path main-responder get call-responder body>>
|
||||||
|
] unit-test
|
|
@ -1,7 +1,69 @@
|
||||||
! Copyright (C) 2003, 2008 Slava Pestov.
|
! Copyright (C) 2003, 2008 Slava Pestov.
|
||||||
! See http://factorcode.org/license.txt for BSD license.
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
|
USING: accessors arrays kernel combinators assocs
|
||||||
|
continuations namespaces sequences splitting words
|
||||||
|
vocabs.loader classes
|
||||||
|
fry urls multiline
|
||||||
|
xml
|
||||||
|
xml.data
|
||||||
|
xml.writer
|
||||||
|
xml.utilities
|
||||||
|
html.components
|
||||||
|
html.elements
|
||||||
|
html.templates
|
||||||
|
html.templates.chloe
|
||||||
|
html.templates.chloe.syntax
|
||||||
|
http
|
||||||
|
http.server
|
||||||
|
http.server.redirection
|
||||||
|
http.server.responses
|
||||||
|
qualified ;
|
||||||
|
QUALIFIED-WITH: assocs a
|
||||||
IN: furnace
|
IN: furnace
|
||||||
|
|
||||||
|
: nested-responders ( -- seq )
|
||||||
|
responder-nesting get a:values ;
|
||||||
|
|
||||||
|
: each-responder ( quot -- )
|
||||||
|
nested-responders swap each ; inline
|
||||||
|
|
||||||
|
: base-path ( string -- pair )
|
||||||
|
dup responder-nesting get
|
||||||
|
[ second class word-name = ] with find nip
|
||||||
|
[ first ] [ "No such responder: " swap append throw ] ?if ;
|
||||||
|
|
||||||
|
: resolve-base-path ( string -- string' )
|
||||||
|
"$" ?head [
|
||||||
|
[
|
||||||
|
"/" split1 [ base-path [ "/" % % ] each "/" % ] dip %
|
||||||
|
] "" make
|
||||||
|
] when ;
|
||||||
|
|
||||||
|
: vocab-path ( vocab -- path )
|
||||||
|
dup vocab-dir vocab-append-path ;
|
||||||
|
|
||||||
|
: resolve-template-path ( pair -- path )
|
||||||
|
[
|
||||||
|
first2 [ word-vocabulary vocab-path % ] [ "/" % % ] bi*
|
||||||
|
] "" make ;
|
||||||
|
|
||||||
|
GENERIC: modify-query ( query responder -- query' )
|
||||||
|
|
||||||
|
M: object modify-query drop ;
|
||||||
|
|
||||||
|
: adjust-url ( url -- url' )
|
||||||
|
clone
|
||||||
|
[ [ modify-query ] each-responder ] change-query
|
||||||
|
[ resolve-base-path ] change-path
|
||||||
|
relative-to-request ;
|
||||||
|
|
||||||
|
: <redirect> ( url -- response )
|
||||||
|
adjust-url request get method>> {
|
||||||
|
{ "GET" [ <temporary-redirect> ] }
|
||||||
|
{ "HEAD" [ <temporary-redirect> ] }
|
||||||
|
{ "POST" [ <permanent-redirect> ] }
|
||||||
|
} case ;
|
||||||
|
|
||||||
GENERIC: hidden-form-field ( responder -- )
|
GENERIC: hidden-form-field ( responder -- )
|
||||||
|
|
||||||
M: object hidden-form-field drop ;
|
M: object hidden-form-field drop ;
|
||||||
|
@ -13,12 +75,6 @@ M: object hidden-form-field drop ;
|
||||||
{ "POST" [ post-data>> ] }
|
{ "POST" [ post-data>> ] }
|
||||||
} case ;
|
} case ;
|
||||||
|
|
||||||
: <feed-content> ( body -- response )
|
|
||||||
feed>xml "application/atom+xml" <content> ;
|
|
||||||
|
|
||||||
: <json-content> ( obj -- response )
|
|
||||||
>json "application/json" <content> ;
|
|
||||||
|
|
||||||
SYMBOL: exit-continuation
|
SYMBOL: exit-continuation
|
||||||
|
|
||||||
: exit-with exit-continuation get continue-with ;
|
: exit-with exit-continuation get continue-with ;
|
||||||
|
@ -38,7 +94,7 @@ CHLOE: atom
|
||||||
<url>
|
<url>
|
||||||
swap >>query
|
swap >>query
|
||||||
swap >>path
|
swap >>path
|
||||||
adjust-url
|
adjust-url relative-to-request
|
||||||
add-atom-feed ;
|
add-atom-feed ;
|
||||||
|
|
||||||
CHLOE: write-atom drop write-atom-feeds ;
|
CHLOE: write-atom drop write-atom-feeds ;
|
||||||
|
@ -62,7 +118,7 @@ M: object link-attr 2drop ;
|
||||||
<url>
|
<url>
|
||||||
swap >>query
|
swap >>query
|
||||||
swap >>path
|
swap >>path
|
||||||
adjust-url =href
|
adjust-url relative-to-request =href
|
||||||
a>
|
a>
|
||||||
] with-scope ;
|
] with-scope ;
|
||||||
|
|
||||||
|
@ -94,8 +150,6 @@ CHLOE: form
|
||||||
[ drop </form> ]
|
[ drop </form> ]
|
||||||
tri ;
|
tri ;
|
||||||
|
|
||||||
DEFER: process-chloe-tag
|
|
||||||
|
|
||||||
STRING: button-tag-markup
|
STRING: button-tag-markup
|
||||||
<t:form class="inline" xmlns:t="http://factorcode.org/chloe/1.0">
|
<t:form class="inline" xmlns:t="http://factorcode.org/chloe/1.0">
|
||||||
<button type="submit"></button>
|
<button type="submit"></button>
|
||||||
|
@ -124,13 +178,6 @@ CHLOE: button
|
||||||
] unless ;
|
] unless ;
|
||||||
|
|
||||||
: if-satisfied? ( tag -- ? )
|
: if-satisfied? ( tag -- ? )
|
||||||
t swap
|
"code" required-attr attr>word execute ;
|
||||||
{
|
|
||||||
[ "code" optional-attr [ attr>word execute and ] when* ]
|
|
||||||
[ "var" optional-attr [ attr>var get and ] when* ]
|
|
||||||
[ "svar" optional-attr [ attr>var sget and ] when* ]
|
|
||||||
[ "uvar" optional-attr [ attr>var uget and ] when* ]
|
|
||||||
[ "value" optional-attr [ value and ] when* ]
|
|
||||||
} cleave ;
|
|
||||||
|
|
||||||
CHLOE: if dup if-satisfied? [ process-tag-children ] [ drop ] if ;
|
CHLOE: if dup if-satisfied? [ process-tag-children ] [ drop ] if ;
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
! Copyright (C) 2008 Slava Pestov.
|
||||||
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
|
USING: json.writer http.server.responses ;
|
||||||
|
IN: furnace.json
|
||||||
|
|
||||||
|
: <json-content> ( body -- response )
|
||||||
|
>json "application/json" <content> ;
|
|
@ -0,0 +1,14 @@
|
||||||
|
! Copyright (C) 2008 Slava Pestov.
|
||||||
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
|
USING: accessors kernel fry
|
||||||
|
rss http.server.responses furnace.actions ;
|
||||||
|
IN: furnace.rss
|
||||||
|
|
||||||
|
: <feed-content> ( body -- response )
|
||||||
|
feed>xml "application/atom+xml" <content> ;
|
||||||
|
|
||||||
|
TUPLE: feed-action < action feed ;
|
||||||
|
|
||||||
|
: <feed-action> ( -- feed )
|
||||||
|
feed-action new-action
|
||||||
|
dup '[ , feed>> call <feed-content> ] >>display ;
|
|
@ -1,8 +1,10 @@
|
||||||
IN: furnace.sessions.tests
|
IN: furnace.sessions.tests
|
||||||
USING: tools.test http furnace.sessions
|
USING: tools.test http furnace.sessions
|
||||||
furnace.actions http.server math namespaces kernel accessors
|
furnace.actions http.server http.server.responses
|
||||||
|
math namespaces kernel accessors
|
||||||
prettyprint io.streams.string io.files splitting destructors
|
prettyprint io.streams.string io.files splitting destructors
|
||||||
sequences db db.sqlite continuations urls ;
|
sequences db db.sqlite continuations urls math.parser
|
||||||
|
furnace ;
|
||||||
|
|
||||||
: with-session
|
: with-session
|
||||||
[
|
[
|
||||||
|
|
|
@ -4,7 +4,8 @@ USING: assocs kernel math.intervals math.parser namespaces
|
||||||
random accessors quotations hashtables sequences continuations
|
random accessors quotations hashtables sequences continuations
|
||||||
fry calendar combinators destructors alarms
|
fry calendar combinators destructors alarms
|
||||||
db db.tuples db.types
|
db db.tuples db.types
|
||||||
http http.server html.elements html.templates.chloe ;
|
http http.server http.server.dispatchers http.server.filters
|
||||||
|
html.elements furnace ;
|
||||||
IN: furnace.sessions
|
IN: furnace.sessions
|
||||||
|
|
||||||
TUPLE: session id expires uid namespace changed? ;
|
TUPLE: session id expires uid namespace changed? ;
|
||||||
|
@ -151,11 +152,3 @@ M: sessions call-responder* ( path responder -- response )
|
||||||
|
|
||||||
: logout-all-sessions ( uid -- )
|
: logout-all-sessions ( uid -- )
|
||||||
session new swap >>uid delete-tuples ;
|
session new swap >>uid delete-tuples ;
|
||||||
|
|
||||||
M: sessions link-attr
|
|
||||||
drop
|
|
||||||
"session" optional-attr {
|
|
||||||
{ "none" [ session off flow-id off ] }
|
|
||||||
{ "current" [ ] }
|
|
||||||
{ f [ ] }
|
|
||||||
} case ;
|
|
||||||
|
|
|
@ -190,7 +190,7 @@ SYMBOL: html
|
||||||
<html "http://www.w3.org/1999/xhtml" =xmlns "en" =xml:lang "en" =lang html>
|
<html "http://www.w3.org/1999/xhtml" =xmlns "en" =xml:lang "en" =lang html>
|
||||||
<head> <title> swap write </title> </head>
|
<head> <title> swap write </title> </head>
|
||||||
<body> call </body>
|
<body> call </body>
|
||||||
</html> ;
|
</html> ; inline
|
||||||
|
|
||||||
: render-error ( message -- )
|
: render-error ( message -- )
|
||||||
<span "error" =class span> escape-string write </span> ;
|
<span "error" =class span> escape-string write </span> ;
|
||||||
|
|
|
@ -49,7 +49,7 @@ IN: html.templates.chloe.tests
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
"test2" test-template call-template
|
"test2" test-template call-template
|
||||||
] "test3" test-template with-boilerplate
|
] [ "test3" test-template ] with-boilerplate
|
||||||
] run-template
|
] run-template
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
|
@ -69,24 +69,6 @@ IN: html.templates.chloe.tests
|
||||||
] run-template
|
] run-template
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
SYMBOL: test6-aux?
|
|
||||||
|
|
||||||
[ "True" ] [
|
|
||||||
[
|
|
||||||
test6-aux? on
|
|
||||||
"test6" test-template call-template
|
|
||||||
] run-template
|
|
||||||
] unit-test
|
|
||||||
|
|
||||||
SYMBOL: test7-aux?
|
|
||||||
|
|
||||||
[ "" ] [
|
|
||||||
[
|
|
||||||
test7-aux? off
|
|
||||||
"test7" test-template call-template
|
|
||||||
] run-template
|
|
||||||
] unit-test
|
|
||||||
|
|
||||||
[ ] [ blank-values ] unit-test
|
[ ] [ blank-values ] unit-test
|
||||||
|
|
||||||
[ ] [ "A label" "label" set-value ] unit-test
|
[ ] [ "A label" "label" set-value ] unit-test
|
||||||
|
@ -127,7 +109,7 @@ M: link-test link-href drop "http://www.apple.com/foo&bar" ;
|
||||||
|
|
||||||
[ "<ul><li>1</li><li>2</li><li>3</li></ul>" ] [
|
[ "<ul><li>1</li><li>2</li><li>3</li></ul>" ] [
|
||||||
[
|
[
|
||||||
"test9" test-template call-template
|
"test7" test-template call-template
|
||||||
] run-template [ blank? not ] filter
|
] run-template [ blank? not ] filter
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
|
@ -142,7 +124,7 @@ TUPLE: person first-name last-name ;
|
||||||
|
|
||||||
[ "<table><tr><td>RBaxter</td><td>Unknown</td></tr><tr><td>Doug</td><td>Coleman</td></tr></table>" ] [
|
[ "<table><tr><td>RBaxter</td><td>Unknown</td></tr><tr><td>Doug</td><td>Coleman</td></tr></table>" ] [
|
||||||
[
|
[
|
||||||
"test10" test-template call-template
|
"test8" test-template call-template
|
||||||
] run-template [ blank? not ] filter
|
] run-template [ blank? not ] filter
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
|
@ -155,7 +137,7 @@ TUPLE: person first-name last-name ;
|
||||||
|
|
||||||
[ "<table><tr><td>RBaxter</td><td>Unknown</td></tr><tr><td>Doug</td><td>Coleman</td></tr></table>" ] [
|
[ "<table><tr><td>RBaxter</td><td>Unknown</td></tr><tr><td>Doug</td><td>Coleman</td></tr></table>" ] [
|
||||||
[
|
[
|
||||||
"test10" test-template call-template
|
"test9" test-template call-template
|
||||||
] run-template [ blank? not ] filter
|
] run-template [ blank? not ] filter
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
|
@ -163,6 +145,6 @@ TUPLE: person first-name last-name ;
|
||||||
|
|
||||||
[ "<a name=\"1\">Hello</a>" ] [
|
[ "<a name=\"1\">Hello</a>" ] [
|
||||||
[
|
[
|
||||||
"test11" test-template call-template
|
"test10" test-template call-template
|
||||||
] run-template
|
] run-template
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
|
@ -12,6 +12,7 @@ html.templates.chloe.syntax ;
|
||||||
IN: html.templates.chloe
|
IN: html.templates.chloe
|
||||||
|
|
||||||
! Chloe is Ed's favorite web designer
|
! Chloe is Ed's favorite web designer
|
||||||
|
SYMBOL: tag-stack
|
||||||
|
|
||||||
TUPLE: chloe path ;
|
TUPLE: chloe path ;
|
||||||
|
|
||||||
|
@ -44,7 +45,8 @@ CHLOE: title children>string set-title ;
|
||||||
|
|
||||||
CHLOE: write-title
|
CHLOE: write-title
|
||||||
drop
|
drop
|
||||||
"head" tags get member? "title" tags get member? not and
|
"head" tag-stack get member?
|
||||||
|
"title" tag-stack get member? not and
|
||||||
[ <title> write-title </title> ] [ write-title ] if ;
|
[ <title> write-title </title> ] [ write-title ] if ;
|
||||||
|
|
||||||
CHLOE: style
|
CHLOE: style
|
||||||
|
@ -92,22 +94,23 @@ CHLOE-SINGLETON: html
|
||||||
CHLOE-SINGLETON: hidden
|
CHLOE-SINGLETON: hidden
|
||||||
|
|
||||||
CHLOE-TUPLE: field
|
CHLOE-TUPLE: field
|
||||||
|
CHLOE-TUPLE: textarea
|
||||||
CHLOE-TUPLE: password
|
CHLOE-TUPLE: password
|
||||||
CHLOE-TUPLE: choice
|
CHLOE-TUPLE: choice
|
||||||
CHLOE-TUPLE: checkbox
|
CHLOE-TUPLE: checkbox
|
||||||
CHLOE-TUPLE: code
|
CHLOE-TUPLE: code
|
||||||
|
|
||||||
: process-chloe-tag ( tag -- )
|
: process-chloe-tag ( tag -- )
|
||||||
dup name-tag tags get at
|
dup name-tag dup tags get at
|
||||||
[ call ] [ "Unknown chloe tag: " prepend throw ] ?if ;
|
[ call ] [ "Unknown chloe tag: " prepend throw ] ?if ;
|
||||||
|
|
||||||
: process-tag ( tag -- )
|
: process-tag ( tag -- )
|
||||||
{
|
{
|
||||||
[ name-tag >lower tags get push ]
|
[ name-tag >lower tag-stack get push ]
|
||||||
[ write-start-tag ]
|
[ write-start-tag ]
|
||||||
[ process-tag-children ]
|
[ process-tag-children ]
|
||||||
[ write-end-tag ]
|
[ write-end-tag ]
|
||||||
[ drop tags get pop* ]
|
[ drop tag-stack get pop* ]
|
||||||
} cleave ;
|
} cleave ;
|
||||||
|
|
||||||
: expand-attrs ( tag -- tag )
|
: expand-attrs ( tag -- tag )
|
||||||
|
@ -127,7 +130,7 @@ CHLOE-TUPLE: code
|
||||||
|
|
||||||
: process-chloe ( xml -- )
|
: process-chloe ( xml -- )
|
||||||
[
|
[
|
||||||
V{ } clone tags set
|
V{ } clone tag-stack set
|
||||||
|
|
||||||
nested-template? get [
|
nested-template? get [
|
||||||
process-template
|
process-template
|
||||||
|
|
|
@ -14,11 +14,10 @@ SYMBOL: tags
|
||||||
|
|
||||||
tags global [ H{ } clone or ] change-at
|
tags global [ H{ } clone or ] change-at
|
||||||
|
|
||||||
: define-chloe-tag ( name quot -- ) tags get set-at ;
|
: define-chloe-tag ( name quot -- ) swap tags get set-at ;
|
||||||
|
|
||||||
: CHLOE:
|
: CHLOE:
|
||||||
scan parse-definition swap define-chloe-tag ;
|
scan parse-definition define-chloe-tag ; parsing
|
||||||
parsing
|
|
||||||
|
|
||||||
: chloe-ns "http://factorcode.org/chloe/1.0" ; inline
|
: chloe-ns "http://factorcode.org/chloe/1.0" ; inline
|
||||||
|
|
||||||
|
@ -38,7 +37,9 @@ MEMO: chloe-name ( string -- name )
|
||||||
[ "name" required-attr ] dip render ;
|
[ "name" required-attr ] dip render ;
|
||||||
|
|
||||||
: CHLOE-SINGLETON:
|
: CHLOE-SINGLETON:
|
||||||
scan dup '[ , singleton-component-tag ] define-chloe-tag ;
|
scan-word
|
||||||
|
[ word-name ] [ '[ , singleton-component-tag ] ] bi
|
||||||
|
define-chloe-tag ;
|
||||||
parsing
|
parsing
|
||||||
|
|
||||||
: attrs>slots ( tag tuple -- )
|
: attrs>slots ( tag tuple -- )
|
||||||
|
@ -54,5 +55,7 @@ MEMO: chloe-name ( string -- name )
|
||||||
2bi render ;
|
2bi render ;
|
||||||
|
|
||||||
: CHLOE-TUPLE:
|
: CHLOE-TUPLE:
|
||||||
scan dup '[ , tuple-component-tag ] define-chloe-tag ;
|
scan-word
|
||||||
|
[ word-name ] [ '[ , tuple-component-tag ] ] bi
|
||||||
|
define-chloe-tag ;
|
||||||
parsing
|
parsing
|
||||||
|
|
|
@ -2,8 +2,26 @@
|
||||||
|
|
||||||
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
||||||
|
|
||||||
<t:if t:var="html.templates.chloe.tests:test6-aux?">
|
<t:label t:name="label" />
|
||||||
True
|
|
||||||
</t:if>
|
<t:link t:name="link" />
|
||||||
|
|
||||||
|
<t:code t:name="code" mode="mode" />
|
||||||
|
|
||||||
|
<t:farkup t:name="farkup" />
|
||||||
|
|
||||||
|
<t:inspector t:name="inspector" />
|
||||||
|
|
||||||
|
<t:html t:name="html" />
|
||||||
|
|
||||||
|
<t:field t:name="field" t:size="13" />
|
||||||
|
|
||||||
|
<t:password t:name="password" t:size="10" />
|
||||||
|
|
||||||
|
<t:textarea t:name="textarea" t:rows="5" t:cols="10" />
|
||||||
|
|
||||||
|
<t:choice t:name="choice" t:choices="choices" />
|
||||||
|
|
||||||
|
<t:checkbox t:name="checkbox">Checkbox</t:checkbox>
|
||||||
|
|
||||||
</t:chloe>
|
</t:chloe>
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
|
|
||||||
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
||||||
|
|
||||||
<t:if t:var="html.templates.chloe.tests:test7-aux?">
|
<ul>
|
||||||
True
|
<t:each t:name="numbers">
|
||||||
</t:if>
|
<li><t:label t:name="value"/></li>
|
||||||
|
</t:each>
|
||||||
|
</ul>
|
||||||
|
|
||||||
</t:chloe>
|
</t:chloe>
|
||||||
|
|
|
@ -2,26 +2,13 @@
|
||||||
|
|
||||||
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
||||||
|
|
||||||
<t:label t:name="label" />
|
<table>
|
||||||
|
<t:bind-each t:name="people">
|
||||||
<t:link t:name="link" />
|
<tr>
|
||||||
|
<td><t:label t:name="first-name"/></td>
|
||||||
<t:code t:name="code" mode="mode" />
|
<td><t:label t:name="last-name"/></td>
|
||||||
|
</tr>
|
||||||
<t:farkup t:name="farkup" />
|
</t:bind-each>
|
||||||
|
</table>
|
||||||
<t:inspector t:name="inspector" />
|
|
||||||
|
|
||||||
<t:html t:name="html" />
|
|
||||||
|
|
||||||
<t:field t:name="field" t:size="13" />
|
|
||||||
|
|
||||||
<t:password t:name="password" t:size="10" />
|
|
||||||
|
|
||||||
<t:textarea t:name="textarea" t:rows="5" t:cols="10" />
|
|
||||||
|
|
||||||
<t:choice t:name="choice" t:choices="choices" />
|
|
||||||
|
|
||||||
<t:checkbox t:name="checkbox">Checkbox</t:checkbox>
|
|
||||||
|
|
||||||
</t:chloe>
|
</t:chloe>
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
<?xml version='1.0' ?>
|
<?xml version='1.0' ?>
|
||||||
|
|
||||||
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0"><a name="@id">Hello</a></t:chloe>
|
||||||
|
|
||||||
<ul>
|
|
||||||
<t:each t:name="numbers">
|
|
||||||
<li><t:label t:name="value"/></li>
|
|
||||||
</t:each>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</t:chloe>
|
|
||||||
|
|
|
@ -10,30 +10,26 @@ tuple-syntax namespaces urls ;
|
||||||
|
|
||||||
[
|
[
|
||||||
TUPLE{ request
|
TUPLE{ request
|
||||||
url: TUPLE{ url protocol: "http" host: "www.apple.com" port: 80 path: "/index.html" query: H{ } }
|
url: TUPLE{ url protocol: "http" host: "www.apple.com" port: 80 path: "/index.html" }
|
||||||
method: "GET"
|
method: "GET"
|
||||||
version: "1.1"
|
version: "1.1"
|
||||||
cookies: V{ }
|
cookies: V{ }
|
||||||
header: H{ { "connection" "close" } { "user-agent" "Factor http.client vocabulary" } }
|
header: H{ { "connection" "close" } { "user-agent" "Factor http.client vocabulary" } }
|
||||||
}
|
}
|
||||||
] [
|
] [
|
||||||
[
|
"http://www.apple.com/index.html"
|
||||||
"http://www.apple.com/index.html"
|
<get-request>
|
||||||
<get-request>
|
|
||||||
] with-scope
|
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
[
|
[
|
||||||
TUPLE{ request
|
TUPLE{ request
|
||||||
url: TUPLE{ url protocol: "https" host: "www.amazon.com" port: 443 path: "/index.html" query: H{ } }
|
url: TUPLE{ url protocol: "https" host: "www.amazon.com" port: 443 path: "/index.html" }
|
||||||
method: "GET"
|
method: "GET"
|
||||||
version: "1.1"
|
version: "1.1"
|
||||||
cookies: V{ }
|
cookies: V{ }
|
||||||
header: H{ { "connection" "close" } { "user-agent" "Factor http.client vocabulary" } }
|
header: H{ { "connection" "close" } { "user-agent" "Factor http.client vocabulary" } }
|
||||||
}
|
}
|
||||||
] [
|
] [
|
||||||
[
|
"https://www.amazon.com/index.html"
|
||||||
"https://www.amazon.com/index.html"
|
<get-request>
|
||||||
<get-request>
|
|
||||||
] with-scope
|
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
|
@ -4,7 +4,7 @@ USING: assocs http kernel math math.parser namespaces sequences
|
||||||
io io.sockets io.streams.string io.files io.timeouts strings
|
io io.sockets io.streams.string io.files io.timeouts strings
|
||||||
splitting calendar continuations accessors vectors math.order
|
splitting calendar continuations accessors vectors math.order
|
||||||
io.encodings.8-bit io.encodings.binary io.streams.duplex
|
io.encodings.8-bit io.encodings.binary io.streams.duplex
|
||||||
fry debugger inspector ascii ;
|
fry debugger inspector ascii urls ;
|
||||||
IN: http.client
|
IN: http.client
|
||||||
|
|
||||||
: max-redirects 10 ;
|
: max-redirects 10 ;
|
||||||
|
@ -21,13 +21,16 @@ DEFER: http-request
|
||||||
|
|
||||||
SYMBOL: redirects
|
SYMBOL: redirects
|
||||||
|
|
||||||
|
: redirect-url ( request url -- request )
|
||||||
|
'[ , >url derive-url ensure-port ] change-url ;
|
||||||
|
|
||||||
: do-redirect ( response data -- response data )
|
: do-redirect ( response data -- response data )
|
||||||
over code>> 300 399 between? [
|
over code>> 300 399 between? [
|
||||||
drop
|
drop
|
||||||
redirects inc
|
redirects inc
|
||||||
redirects get max-redirects < [
|
redirects get max-redirects < [
|
||||||
request get
|
request get
|
||||||
swap "location" header request-with-url
|
swap "location" header redirect-url
|
||||||
"GET" >>method http-request
|
"GET" >>method http-request
|
||||||
] [
|
] [
|
||||||
too-many-redirects
|
too-many-redirects
|
||||||
|
@ -61,8 +64,8 @@ PRIVATE>
|
||||||
|
|
||||||
: <get-request> ( url -- request )
|
: <get-request> ( url -- request )
|
||||||
<request>
|
<request>
|
||||||
swap request-with-url
|
"GET" >>method
|
||||||
"GET" >>method ;
|
swap >url ensure-port >>url ;
|
||||||
|
|
||||||
: http-get* ( url -- response data )
|
: http-get* ( url -- response data )
|
||||||
<get-request> http-request ;
|
<get-request> http-request ;
|
||||||
|
@ -100,7 +103,7 @@ M: download-failed error.
|
||||||
: <post-request> ( content-type content url -- request )
|
: <post-request> ( content-type content url -- request )
|
||||||
<request>
|
<request>
|
||||||
"POST" >>method
|
"POST" >>method
|
||||||
swap request-with-url
|
swap >url ensure-port >>url
|
||||||
swap >>post-data
|
swap >>post-data
|
||||||
swap >>post-data-type ;
|
swap >>post-data-type ;
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,6 @@ io.streams.string kernel arrays splitting sequences
|
||||||
assocs io.sockets db db.sqlite continuations urls ;
|
assocs io.sockets db db.sqlite continuations urls ;
|
||||||
IN: http.tests
|
IN: http.tests
|
||||||
|
|
||||||
[ "/" ] [ "http://foo.com" url>path ] unit-test
|
|
||||||
[ "/" ] [ "http://foo.com/" url>path ] unit-test
|
|
||||||
[ "/bar" ] [ "http://foo.com/bar" url>path ] unit-test
|
|
||||||
[ "/bar" ] [ "/bar" url>path ] unit-test
|
|
||||||
|
|
||||||
: lf>crlf "\n" split "\r\n" join ;
|
: lf>crlf "\n" split "\r\n" join ;
|
||||||
|
|
||||||
STRING: read-request-test-1
|
STRING: read-request-test-1
|
||||||
|
@ -126,7 +121,9 @@ read-response-test-1' 1array [
|
||||||
USING: http.server http.server.static furnace.sessions
|
USING: http.server http.server.static furnace.sessions
|
||||||
furnace.actions furnace.auth.login furnace.db http.client
|
furnace.actions furnace.auth.login furnace.db http.client
|
||||||
io.server io.files io io.encodings.ascii
|
io.server io.files io io.encodings.ascii
|
||||||
accessors namespaces threads ;
|
accessors namespaces threads
|
||||||
|
http.server.responses http.server.redirection
|
||||||
|
http.server.dispatchers ;
|
||||||
|
|
||||||
: add-quit-action
|
: add-quit-action
|
||||||
<action>
|
<action>
|
||||||
|
@ -149,7 +146,7 @@ test-db [
|
||||||
"resource:extra/http/test" <static> >>default
|
"resource:extra/http/test" <static> >>default
|
||||||
"nested" add-responder
|
"nested" add-responder
|
||||||
<action>
|
<action>
|
||||||
[ URL" redirect-loop" <redirect> ] >>display
|
[ URL" redirect-loop" <temporary-redirect> ] >>display
|
||||||
"redirect-loop" add-responder
|
"redirect-loop" add-responder
|
||||||
main-responder set
|
main-responder set
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,7 @@ assocs sequences splitting sorting sets debugger
|
||||||
strings vectors hashtables quotations arrays byte-arrays
|
strings vectors hashtables quotations arrays byte-arrays
|
||||||
math.parser calendar calendar.format
|
math.parser calendar calendar.format
|
||||||
|
|
||||||
io io.streams.string io.encodings.utf8 io.encodings.string
|
io io.server io.sockets.secure
|
||||||
io.sockets io.sockets.secure io.server
|
|
||||||
|
|
||||||
unicode.case unicode.categories qualified
|
unicode.case unicode.categories qualified
|
||||||
|
|
||||||
|
@ -17,22 +16,6 @@ EXCLUDE: fry => , ;
|
||||||
|
|
||||||
IN: http
|
IN: http
|
||||||
|
|
||||||
: secure-protocol? ( protocol -- ? )
|
|
||||||
"https" = ;
|
|
||||||
|
|
||||||
: url-addr ( url -- addr )
|
|
||||||
[ [ host>> ] [ port>> ] bi <inet> ] [ protocol>> ] bi
|
|
||||||
secure-protocol? [ <secure> ] when ;
|
|
||||||
|
|
||||||
: protocol-port ( protocol -- port )
|
|
||||||
{
|
|
||||||
{ "http" [ 80 ] }
|
|
||||||
{ "https" [ 443 ] }
|
|
||||||
} case ;
|
|
||||||
|
|
||||||
: ensure-port ( url -- url' )
|
|
||||||
dup protocol>> '[ , protocol-port or ] change-port ;
|
|
||||||
|
|
||||||
: crlf "\r\n" write ;
|
: crlf "\r\n" write ;
|
||||||
|
|
||||||
: add-header ( value key assoc -- )
|
: add-header ( value key assoc -- )
|
||||||
|
@ -167,19 +150,6 @@ cookies ;
|
||||||
"close" "connection" set-header
|
"close" "connection" set-header
|
||||||
"Factor http.client vocabulary" "user-agent" set-header ;
|
"Factor http.client vocabulary" "user-agent" set-header ;
|
||||||
|
|
||||||
: chop-hostname ( str -- str' )
|
|
||||||
":" split1 "//" ?head drop nip
|
|
||||||
CHAR: / over index over length or tail
|
|
||||||
dup empty? [ drop "/" ] when ;
|
|
||||||
|
|
||||||
: url>path ( url -- path )
|
|
||||||
#! Technically, only proxies are meant to support hostnames
|
|
||||||
#! in HTTP requests, but IE sends these sometimes so we
|
|
||||||
#! just chop the hostname part.
|
|
||||||
url-decode
|
|
||||||
dup { "http://" "https://" } [ head? ] with contains?
|
|
||||||
[ chop-hostname ] when ;
|
|
||||||
|
|
||||||
: read-method ( request -- request )
|
: read-method ( request -- request )
|
||||||
" " read-until [ "Bad request: method" throw ] unless
|
" " read-until [ "Bad request: method" throw ] unless
|
||||||
>>method ;
|
>>method ;
|
||||||
|
@ -299,9 +269,6 @@ SYMBOL: max-post-request
|
||||||
flush
|
flush
|
||||||
drop ;
|
drop ;
|
||||||
|
|
||||||
: request-with-url ( request url -- request )
|
|
||||||
'[ , >url derive-url ensure-port ] change-url ;
|
|
||||||
|
|
||||||
GENERIC: write-response ( response -- )
|
GENERIC: write-response ( response -- )
|
||||||
|
|
||||||
GENERIC: write-full-response ( request response -- )
|
GENERIC: write-full-response ( request response -- )
|
||||||
|
@ -406,7 +373,7 @@ body ;
|
||||||
|
|
||||||
: <raw-response> ( -- response )
|
: <raw-response> ( -- response )
|
||||||
raw-response new
|
raw-response new
|
||||||
"1.1" >>version ;
|
"1.1" >>version ;
|
||||||
|
|
||||||
M: raw-response write-response ( respose -- )
|
M: raw-response write-response ( respose -- )
|
||||||
write-response-version
|
write-response-version
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
USING: http.server http.server.dispatchers http.server.responses
|
||||||
|
tools.test kernel namespaces accessors io http math sequences
|
||||||
|
assocs arrays classes words urls ;
|
||||||
|
IN: http.server.dispatchers.tests
|
||||||
|
|
||||||
|
\ find-responder must-infer
|
||||||
|
\ http-error. must-infer
|
||||||
|
|
||||||
|
TUPLE: mock-responder path ;
|
||||||
|
|
||||||
|
C: <mock-responder> mock-responder
|
||||||
|
|
||||||
|
M: mock-responder call-responder*
|
||||||
|
nip
|
||||||
|
path>> on
|
||||||
|
[ ] "text/plain" <content> ;
|
||||||
|
|
||||||
|
: check-dispatch ( tag path -- ? )
|
||||||
|
V{ } clone responder-nesting set
|
||||||
|
over off
|
||||||
|
split-path
|
||||||
|
main-responder get call-responder
|
||||||
|
write-response get ;
|
||||||
|
|
||||||
|
[
|
||||||
|
<dispatcher>
|
||||||
|
"foo" <mock-responder> "foo" add-responder
|
||||||
|
"bar" <mock-responder> "bar" add-responder
|
||||||
|
<dispatcher>
|
||||||
|
"123" <mock-responder> "123" add-responder
|
||||||
|
"default" <mock-responder> >>default
|
||||||
|
"baz" add-responder
|
||||||
|
main-responder set
|
||||||
|
|
||||||
|
[ "foo" ] [
|
||||||
|
{ "foo" } main-responder get find-responder path>> nip
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
[ "bar" ] [
|
||||||
|
{ "bar" } main-responder get find-responder path>> nip
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "foo" "foo" check-dispatch ] unit-test
|
||||||
|
[ f ] [ "foo" "bar" check-dispatch ] unit-test
|
||||||
|
[ t ] [ "bar" "bar" check-dispatch ] unit-test
|
||||||
|
[ t ] [ "default" "baz/xxx" check-dispatch ] unit-test
|
||||||
|
[ t ] [ "default" "baz/xxx//" check-dispatch ] unit-test
|
||||||
|
[ t ] [ "default" "/baz/xxx//" check-dispatch ] unit-test
|
||||||
|
[ t ] [ "123" "baz/123" check-dispatch ] unit-test
|
||||||
|
[ t ] [ "123" "baz///123" check-dispatch ] unit-test
|
||||||
|
|
||||||
|
] with-scope
|
||||||
|
|
||||||
|
[
|
||||||
|
<dispatcher>
|
||||||
|
"default" <mock-responder> >>default
|
||||||
|
main-responder set
|
||||||
|
|
||||||
|
[ "/default" ] [ "/default" main-responder get find-responder drop ] unit-test
|
||||||
|
] with-scope
|
||||||
|
|
||||||
|
! Make sure path for default responder isn't chopped
|
||||||
|
TUPLE: path-check-responder ;
|
||||||
|
|
||||||
|
C: <path-check-responder> path-check-responder
|
||||||
|
|
||||||
|
M: path-check-responder call-responder*
|
||||||
|
drop
|
||||||
|
>array "text/plain" <content> ;
|
||||||
|
|
||||||
|
[ { "c" } ] [
|
||||||
|
V{ } clone responder-nesting set
|
||||||
|
|
||||||
|
{ "b" "c" }
|
||||||
|
<dispatcher>
|
||||||
|
<dispatcher>
|
||||||
|
<path-check-responder> >>default
|
||||||
|
"b" add-responder
|
||||||
|
call-responder
|
||||||
|
body>>
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
! Test that "" dispatcher works with default>>
|
||||||
|
[ ] [
|
||||||
|
<dispatcher>
|
||||||
|
"" <mock-responder> "" add-responder
|
||||||
|
"bar" <mock-responder> "bar" add-responder
|
||||||
|
"baz" <mock-responder> >>default
|
||||||
|
main-responder set
|
||||||
|
|
||||||
|
[ t ] [ "" "" check-dispatch ] unit-test
|
||||||
|
[ f ] [ "" "quux" check-dispatch ] unit-test
|
||||||
|
[ t ] [ "baz" "quux" check-dispatch ] unit-test
|
||||||
|
[ f ] [ "foo" "bar" check-dispatch ] unit-test
|
||||||
|
[ t ] [ "bar" "bar" check-dispatch ] unit-test
|
||||||
|
[ t ] [ "baz" "xxx" check-dispatch ] unit-test
|
||||||
|
] unit-test
|
|
@ -0,0 +1,47 @@
|
||||||
|
! Copyright (C) 2008 Slava Pestov.
|
||||||
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
|
USING: kernel namespaces sequences assocs accessors
|
||||||
|
http http.server http.server.responses ;
|
||||||
|
IN: http.server.dispatchers
|
||||||
|
|
||||||
|
TUPLE: dispatcher default responders ;
|
||||||
|
|
||||||
|
: new-dispatcher ( class -- dispatcher )
|
||||||
|
new
|
||||||
|
<404> <trivial-responder> >>default
|
||||||
|
H{ } clone >>responders ; inline
|
||||||
|
|
||||||
|
: <dispatcher> ( -- dispatcher )
|
||||||
|
dispatcher new-dispatcher ;
|
||||||
|
|
||||||
|
: find-responder ( path dispatcher -- path responder )
|
||||||
|
over empty? [
|
||||||
|
"" over responders>> at*
|
||||||
|
[ nip ] [ drop default>> ] if
|
||||||
|
] [
|
||||||
|
over first over responders>> at*
|
||||||
|
[ [ drop rest-slice ] dip ] [ drop default>> ] if
|
||||||
|
] if ;
|
||||||
|
|
||||||
|
M: dispatcher call-responder* ( path dispatcher -- response )
|
||||||
|
find-responder call-responder ;
|
||||||
|
|
||||||
|
TUPLE: vhost-dispatcher default responders ;
|
||||||
|
|
||||||
|
: <vhost-dispatcher> ( -- dispatcher )
|
||||||
|
vhost-dispatcher new-dispatcher ;
|
||||||
|
|
||||||
|
: find-vhost ( dispatcher -- responder )
|
||||||
|
request get url>> host>> over responders>> at*
|
||||||
|
[ nip ] [ drop default>> ] if ;
|
||||||
|
|
||||||
|
M: vhost-dispatcher call-responder* ( path dispatcher -- response )
|
||||||
|
find-vhost call-responder ;
|
||||||
|
|
||||||
|
: add-responder ( dispatcher responder path -- dispatcher )
|
||||||
|
pick responders>> set-at ;
|
||||||
|
|
||||||
|
: add-main-responder ( dispatcher responder path -- dispatcher )
|
||||||
|
[ add-responder drop ]
|
||||||
|
[ drop "" add-responder drop ]
|
||||||
|
[ 2drop ] 3tri ;
|
|
@ -0,0 +1,9 @@
|
||||||
|
! Copyright (C) 2008 Slava Pestov.
|
||||||
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
|
USING: http.server accessors ;
|
||||||
|
IN: http.server.filters
|
||||||
|
|
||||||
|
TUPLE: filter-responder responder ;
|
||||||
|
|
||||||
|
M: filter-responder call-responder*
|
||||||
|
responder>> call-responder ;
|
|
@ -0,0 +1,48 @@
|
||||||
|
IN: http.server.redirection.tests
|
||||||
|
USING: http http.server.redirection urls accessors
|
||||||
|
namespaces tools.test ;
|
||||||
|
|
||||||
|
\ relative-to-request must-infer
|
||||||
|
|
||||||
|
[
|
||||||
|
<request>
|
||||||
|
<url>
|
||||||
|
"http" >>protocol
|
||||||
|
"www.apple.com" >>host
|
||||||
|
"/xxx/bar" >>path
|
||||||
|
{ { "a" "b" } } >>query
|
||||||
|
>>url
|
||||||
|
request set
|
||||||
|
|
||||||
|
[ "http://www.apple.com:80/xxx/bar" ] [
|
||||||
|
<url> relative-to-request url>string
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
[ "http://www.apple.com:80/xxx/baz" ] [
|
||||||
|
<url> "baz" >>path relative-to-request url>string
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
[ "http://www.apple.com:80/xxx/baz?c=d" ] [
|
||||||
|
<url> "baz" >>path { { "c" "d" } } >>query relative-to-request url>string
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
[ "http://www.apple.com:80/xxx/bar?c=d" ] [
|
||||||
|
<url> { { "c" "d" } } >>query relative-to-request url>string
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
[ "http://www.apple.com:80/flip" ] [
|
||||||
|
<url> "/flip" >>path relative-to-request url>string
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
[ "http://www.apple.com:80/flip?c=d" ] [
|
||||||
|
<url> "/flip" >>path { { "c" "d" } } >>query relative-to-request url>string
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
[ "http://www.jedit.org:80/" ] [
|
||||||
|
"http://www.jedit.org" >url relative-to-request url>string
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
[ "http://www.jedit.org:80/?a=b" ] [
|
||||||
|
"http://www.jedit.org" >url { { "a" "b" } } >>query relative-to-request url>string
|
||||||
|
] unit-test
|
||||||
|
] with-scope
|
|
@ -0,0 +1,24 @@
|
||||||
|
! Copyright (C) 2008 Slava Pestov.
|
||||||
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
|
USING: kernel accessors combinators namespaces
|
||||||
|
logging urls http http.server http.server.responses ;
|
||||||
|
IN: http.server.redirection
|
||||||
|
|
||||||
|
: relative-to-request ( url -- url' )
|
||||||
|
request get url>>
|
||||||
|
clone
|
||||||
|
f >>query
|
||||||
|
swap derive-url ensure-port ;
|
||||||
|
|
||||||
|
: <custom-redirect> ( url code message -- response )
|
||||||
|
<trivial-response>
|
||||||
|
swap dup url? [ relative-to-request ] when
|
||||||
|
"location" set-header ;
|
||||||
|
|
||||||
|
\ <custom-redirect> DEBUG add-input-logging
|
||||||
|
|
||||||
|
: <permanent-redirect> ( url -- response )
|
||||||
|
301 "Moved Permanently" <custom-redirect> ;
|
||||||
|
|
||||||
|
: <temporary-redirect> ( url -- response )
|
||||||
|
307 "Temporary Redirect" <custom-redirect> ;
|
|
@ -0,0 +1,37 @@
|
||||||
|
! Copyright (C) 2008 Slava Pestov.
|
||||||
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
|
USING: html.elements math.parser http accessors kernel
|
||||||
|
io io.streams.string ;
|
||||||
|
IN: http.server.responses
|
||||||
|
|
||||||
|
: <content> ( body content-type -- response )
|
||||||
|
<response>
|
||||||
|
200 >>code
|
||||||
|
"Document follows" >>message
|
||||||
|
swap >>content-type
|
||||||
|
swap >>body ;
|
||||||
|
|
||||||
|
: trivial-response-body ( code message -- )
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<h1> [ number>string write bl ] [ write ] bi* </h1>
|
||||||
|
</body>
|
||||||
|
</html> ;
|
||||||
|
|
||||||
|
: <trivial-response> ( code message -- response )
|
||||||
|
2dup [ trivial-response-body ] with-string-writer
|
||||||
|
"text/html" <content>
|
||||||
|
swap >>message
|
||||||
|
swap >>code ;
|
||||||
|
|
||||||
|
: <304> ( -- response )
|
||||||
|
304 "Not modified" <trivial-response> ;
|
||||||
|
|
||||||
|
: <403> ( -- response )
|
||||||
|
403 "Forbidden" <trivial-response> ;
|
||||||
|
|
||||||
|
: <400> ( -- response )
|
||||||
|
400 "Bad request" <trivial-response> ;
|
||||||
|
|
||||||
|
: <404> ( -- response )
|
||||||
|
404 "Not found" <trivial-response> ;
|
|
@ -1,217 +1,51 @@
|
||||||
! Copyright (C) 2003, 2008 Slava Pestov.
|
! Copyright (C) 2003, 2008 Slava Pestov.
|
||||||
! See http://factorcode.org/license.txt for BSD license.
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
USING: assocs kernel namespaces io io.timeouts strings splitting
|
USING: kernel accessors sequences arrays namespaces splitting
|
||||||
threads sequences prettyprint io.server logging calendar http
|
vocabs.loader http http.server.responses logging calendar
|
||||||
html.streams html.components html.elements html.templates
|
destructors html.elements html.streams io.server
|
||||||
accessors math.parser combinators.lib tools.vocabs debugger
|
io.encodings.8-bit io.timeouts io assocs debugger continuations
|
||||||
continuations random combinators destructors io.streams.string
|
fry tools.vocabs math ;
|
||||||
io.encodings.8-bit fry classes words math urls
|
|
||||||
arrays vocabs.loader ;
|
|
||||||
IN: http.server
|
IN: http.server
|
||||||
|
|
||||||
|
SYMBOL: responder-nesting
|
||||||
|
|
||||||
|
SYMBOL: main-responder
|
||||||
|
|
||||||
|
SYMBOL: development-mode
|
||||||
|
|
||||||
! path is a sequence of path component strings
|
! path is a sequence of path component strings
|
||||||
GENERIC: call-responder* ( path responder -- response )
|
GENERIC: call-responder* ( path responder -- response )
|
||||||
|
|
||||||
: <content> ( body content-type -- response )
|
|
||||||
<response>
|
|
||||||
200 >>code
|
|
||||||
"Document follows" >>message
|
|
||||||
swap >>content-type
|
|
||||||
swap >>body ;
|
|
||||||
|
|
||||||
TUPLE: trivial-responder response ;
|
TUPLE: trivial-responder response ;
|
||||||
|
|
||||||
C: <trivial-responder> trivial-responder
|
C: <trivial-responder> trivial-responder
|
||||||
|
|
||||||
M: trivial-responder call-responder* nip response>> call ;
|
M: trivial-responder call-responder* nip response>> clone ;
|
||||||
|
|
||||||
: trivial-response-body ( code message -- )
|
main-responder global [ <404> <trivial-responder> get-global or ] change-at
|
||||||
<html>
|
|
||||||
<body>
|
|
||||||
<h1> [ number>string write bl ] [ write ] bi* </h1>
|
|
||||||
</body>
|
|
||||||
</html> ;
|
|
||||||
|
|
||||||
: <trivial-response> ( code message -- response )
|
|
||||||
2dup [ trivial-response-body ] with-string-writer
|
|
||||||
"text/html" <content>
|
|
||||||
swap >>message
|
|
||||||
swap >>code ;
|
|
||||||
|
|
||||||
: <400> ( -- response )
|
|
||||||
400 "Bad request" <trivial-response> ;
|
|
||||||
|
|
||||||
: <404> ( -- response )
|
|
||||||
404 "Not Found" <trivial-response> ;
|
|
||||||
|
|
||||||
SYMBOL: 404-responder
|
|
||||||
|
|
||||||
[ <404> ] <trivial-responder> 404-responder set-global
|
|
||||||
|
|
||||||
SYMBOL: responder-nesting
|
|
||||||
|
|
||||||
: invert-slice ( slice -- slice' )
|
: invert-slice ( slice -- slice' )
|
||||||
dup slice? [
|
dup slice? [ [ seq>> ] [ from>> ] bi head-slice ] [ drop { } ] if ;
|
||||||
[ seq>> ] [ from>> ] bi head-slice
|
|
||||||
] [
|
|
||||||
drop { }
|
|
||||||
] if ;
|
|
||||||
|
|
||||||
: vocab-path ( vocab -- path )
|
: add-responder-nesting ( path responder -- )
|
||||||
dup vocab-dir vocab-append-path ;
|
[ invert-slice ] dip 2array responder-nesting get push ;
|
||||||
|
|
||||||
: vocab-path-of ( dispatcher -- path )
|
|
||||||
class word-vocabulary vocab-path ;
|
|
||||||
|
|
||||||
: add-responder-path ( path dispatcher -- )
|
|
||||||
[ [ invert-slice ] [ [ vocab-path-of ] keep ] bi* 3array ]
|
|
||||||
[ nip class word-name ] 2bi
|
|
||||||
responder-nesting get set-at ;
|
|
||||||
|
|
||||||
: call-responder ( path responder -- response )
|
: call-responder ( path responder -- response )
|
||||||
[ add-responder-path ] [ call-responder* ] 2bi ;
|
[ add-responder-nesting ] [ call-responder* ] 2bi ;
|
||||||
|
|
||||||
: nested-responders ( -- seq )
|
|
||||||
responder-nesting get assocs:values [ third ] map ;
|
|
||||||
|
|
||||||
: each-responder ( quot -- )
|
|
||||||
nested-responders swap each ; inline
|
|
||||||
|
|
||||||
: responder-path ( string -- pair )
|
|
||||||
dup responder-nesting get at
|
|
||||||
[ ] [ "No such responder: " swap append throw ] ?if ;
|
|
||||||
|
|
||||||
: base-path ( string -- path )
|
|
||||||
responder-path first ;
|
|
||||||
|
|
||||||
: template-path ( string -- path )
|
|
||||||
responder-path second ;
|
|
||||||
|
|
||||||
: resolve-responder-path ( string quot -- string' )
|
|
||||||
[ "$" ?head ] dip '[
|
|
||||||
[
|
|
||||||
"/" split1 [ @ [ "/" % % ] each "/" % ] dip %
|
|
||||||
] "" make
|
|
||||||
] when ; inline
|
|
||||||
|
|
||||||
: resolve-base-path ( string -- string' )
|
|
||||||
[ base-path ] resolve-responder-path ;
|
|
||||||
|
|
||||||
: resolve-template-path ( string -- string' )
|
|
||||||
[ template-path ] resolve-responder-path ;
|
|
||||||
|
|
||||||
GENERIC: modify-query ( query responder -- query' )
|
|
||||||
|
|
||||||
M: object modify-query drop ;
|
|
||||||
|
|
||||||
: adjust-url ( url -- url' )
|
|
||||||
clone
|
|
||||||
[ dup [ modify-query ] each-responder ] change-query
|
|
||||||
[ resolve-base-path ] change-path
|
|
||||||
request get url>>
|
|
||||||
clone
|
|
||||||
f >>query
|
|
||||||
swap derive-url ensure-port ;
|
|
||||||
|
|
||||||
: <custom-redirect> ( url code message -- response )
|
|
||||||
<trivial-response>
|
|
||||||
swap dup url? [ adjust-url ] when
|
|
||||||
"location" set-header ;
|
|
||||||
|
|
||||||
\ <custom-redirect> DEBUG add-input-logging
|
|
||||||
|
|
||||||
: <permanent-redirect> ( to query -- response )
|
|
||||||
301 "Moved Permanently" <custom-redirect> ;
|
|
||||||
|
|
||||||
: <temporary-redirect> ( to query -- response )
|
|
||||||
307 "Temporary Redirect" <custom-redirect> ;
|
|
||||||
|
|
||||||
: <redirect> ( to query -- response )
|
|
||||||
request get method>> {
|
|
||||||
{ "GET" [ <temporary-redirect> ] }
|
|
||||||
{ "HEAD" [ <temporary-redirect> ] }
|
|
||||||
{ "POST" [ <permanent-redirect> ] }
|
|
||||||
} case ;
|
|
||||||
|
|
||||||
TUPLE: dispatcher default responders ;
|
|
||||||
|
|
||||||
: new-dispatcher ( class -- dispatcher )
|
|
||||||
new
|
|
||||||
404-responder get >>default
|
|
||||||
H{ } clone >>responders ; inline
|
|
||||||
|
|
||||||
: <dispatcher> ( -- dispatcher )
|
|
||||||
dispatcher new-dispatcher ;
|
|
||||||
|
|
||||||
: find-responder ( path dispatcher -- path responder )
|
|
||||||
over empty? [
|
|
||||||
"" over responders>> at*
|
|
||||||
[ nip ] [ drop default>> ] if
|
|
||||||
] [
|
|
||||||
over first over responders>> at*
|
|
||||||
[ [ drop rest-slice ] dip ] [ drop default>> ] if
|
|
||||||
] if ;
|
|
||||||
|
|
||||||
M: dispatcher call-responder* ( path dispatcher -- response )
|
|
||||||
find-responder call-responder ;
|
|
||||||
|
|
||||||
TUPLE: vhost-dispatcher default responders ;
|
|
||||||
|
|
||||||
: <vhost-dispatcher> ( -- dispatcher )
|
|
||||||
404-responder get H{ } clone vhost-dispatcher boa ;
|
|
||||||
|
|
||||||
: find-vhost ( dispatcher -- responder )
|
|
||||||
request get url>> host>> over responders>> at*
|
|
||||||
[ nip ] [ drop default>> ] if ;
|
|
||||||
|
|
||||||
M: vhost-dispatcher call-responder* ( path dispatcher -- response )
|
|
||||||
find-vhost call-responder ;
|
|
||||||
|
|
||||||
: add-responder ( dispatcher responder path -- dispatcher )
|
|
||||||
pick responders>> set-at ;
|
|
||||||
|
|
||||||
: add-main-responder ( dispatcher responder path -- dispatcher )
|
|
||||||
[ add-responder drop ]
|
|
||||||
[ drop "" add-responder drop ]
|
|
||||||
[ 2drop ] 3tri ;
|
|
||||||
|
|
||||||
TUPLE: filter-responder responder ;
|
|
||||||
|
|
||||||
M: filter-responder call-responder*
|
|
||||||
responder>> call-responder ;
|
|
||||||
|
|
||||||
SYMBOL: main-responder
|
|
||||||
|
|
||||||
main-responder global
|
|
||||||
[ drop 404-responder get-global ] cache
|
|
||||||
drop
|
|
||||||
|
|
||||||
SYMBOL: development-mode
|
|
||||||
|
|
||||||
: http-error. ( error -- )
|
: http-error. ( error -- )
|
||||||
"Internal server error" [
|
"Internal server error" [
|
||||||
development-mode get [
|
[ print-error nl :c ] with-html-stream
|
||||||
[ print-error nl :c ] with-html-stream
|
|
||||||
] [
|
|
||||||
500 "Internal server error"
|
|
||||||
trivial-response-body
|
|
||||||
] if
|
|
||||||
] simple-page ;
|
] simple-page ;
|
||||||
|
|
||||||
: <500> ( error -- response )
|
: <500> ( error -- response )
|
||||||
500 "Internal server error" <trivial-response>
|
500 "Internal server error" <trivial-response>
|
||||||
swap '[ , http-error. ] >>body ;
|
development-mode get [ swap '[ , http-error. ] >>body ] [ drop ] if ;
|
||||||
|
|
||||||
: do-response ( response -- )
|
: do-response ( response -- )
|
||||||
dup write-response
|
dup write-response
|
||||||
request get method>> "HEAD" =
|
request get method>> "HEAD" =
|
||||||
[ drop ] [
|
[ drop ] [ '[ , write-response-body ] [ http-error. ] recover ] if ;
|
||||||
'[
|
|
||||||
, write-response-body
|
|
||||||
] [
|
|
||||||
http-error.
|
|
||||||
] recover
|
|
||||||
] if ;
|
|
||||||
|
|
||||||
LOG: httpd-hit NOTICE
|
LOG: httpd-hit NOTICE
|
||||||
|
|
||||||
|
@ -223,9 +57,7 @@ LOG: httpd-hit NOTICE
|
||||||
|
|
||||||
: init-request ( request -- )
|
: init-request ( request -- )
|
||||||
request set
|
request set
|
||||||
H{ } clone responder-nesting set
|
V{ } clone responder-nesting set ;
|
||||||
[ ] link-hook set
|
|
||||||
[ ] form-hook set ;
|
|
||||||
|
|
||||||
: dispatch-request ( request -- response )
|
: dispatch-request ( request -- response )
|
||||||
url>> path>> split-path main-responder get call-responder ;
|
url>> path>> split-path main-responder get call-responder ;
|
||||||
|
@ -235,9 +67,7 @@ LOG: httpd-hit NOTICE
|
||||||
[ init-request ]
|
[ init-request ]
|
||||||
[ log-request ]
|
[ log-request ]
|
||||||
[ dispatch-request ] tri
|
[ dispatch-request ] tri
|
||||||
]
|
] [ [ \ do-request log-error ] [ <500> ] bi ] recover ;
|
||||||
[ [ \ do-request log-error ] [ <500> ] bi ]
|
|
||||||
recover ;
|
|
||||||
|
|
||||||
: ?refresh-all ( -- )
|
: ?refresh-all ( -- )
|
||||||
development-mode get-global
|
development-mode get-global
|
||||||
|
@ -254,8 +84,7 @@ LOG: httpd-hit NOTICE
|
||||||
|
|
||||||
: httpd ( port -- )
|
: httpd ( port -- )
|
||||||
dup integer? [ internet-server ] when
|
dup integer? [ internet-server ] when
|
||||||
"http.server" latin1
|
"http.server" latin1 [ handle-client ] with-server ;
|
||||||
[ handle-client ] with-server ;
|
|
||||||
|
|
||||||
: httpd-main ( -- )
|
: httpd-main ( -- )
|
||||||
8888 httpd ;
|
8888 httpd ;
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
! Copyright (C) 2004, 2008 Slava Pestov.
|
! Copyright (C) 2004, 2008 Slava Pestov.
|
||||||
! See http://factorcode.org/license.txt for BSD license.
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
USING: calendar io io.files kernel math math.order
|
USING: calendar io io.files kernel math math.order
|
||||||
math.parser http http.server namespaces parser sequences strings
|
math.parser namespaces parser sequences strings
|
||||||
assocs hashtables debugger http.mime sorting html.elements
|
assocs hashtables debugger mime-types sorting logging
|
||||||
html.templates.fhtml logging calendar.format accessors
|
calendar.format accessors
|
||||||
io.encodings.binary fry xml.entities destructors urls ;
|
io.encodings.binary fry xml.entities destructors urls
|
||||||
|
html.elements html.templates.fhtml
|
||||||
|
http
|
||||||
|
http.server
|
||||||
|
http.server.responses
|
||||||
|
http.server.redirection ;
|
||||||
IN: http.server.static
|
IN: http.server.static
|
||||||
|
|
||||||
! special maps mime types to quots with effect ( path -- )
|
! special maps mime types to quots with effect ( path -- )
|
||||||
|
@ -17,12 +22,6 @@ TUPLE: file-responder root hook special allow-listings ;
|
||||||
2drop t
|
2drop t
|
||||||
] if ;
|
] if ;
|
||||||
|
|
||||||
: <304> ( -- response )
|
|
||||||
304 "Not modified" <trivial-response> ;
|
|
||||||
|
|
||||||
: <403> ( -- response )
|
|
||||||
403 "Forbidden" <trivial-response> ;
|
|
||||||
|
|
||||||
: <file-responder> ( root hook -- responder )
|
: <file-responder> ( root hook -- responder )
|
||||||
file-responder new
|
file-responder new
|
||||||
swap >>hook
|
swap >>hook
|
||||||
|
@ -85,7 +84,7 @@ TUPLE: file-responder root hook special allow-listings ;
|
||||||
find-index [ serve-file ] [ list-directory ] ?if
|
find-index [ serve-file ] [ list-directory ] ?if
|
||||||
] [
|
] [
|
||||||
drop
|
drop
|
||||||
request get url>> clone [ "/" append ] change-path <redirect>
|
request get url>> clone [ "/" append ] change-path <permanent-redirect>
|
||||||
] if ;
|
] if ;
|
||||||
|
|
||||||
: serve-object ( filename -- response )
|
: serve-object ( filename -- response )
|
||||||
|
|
|
@ -94,10 +94,10 @@ TUPLE: url protocol username password host port path query anchor ;
|
||||||
|
|
||||||
: <url> ( -- url ) url new ;
|
: <url> ( -- url ) url new ;
|
||||||
|
|
||||||
: query-param ( request key -- value )
|
: query-param ( url key -- value )
|
||||||
swap query>> at ;
|
swap query>> at ;
|
||||||
|
|
||||||
: set-query-param ( request value key -- request )
|
: set-query-param ( url value key -- url )
|
||||||
'[ , , _ ?set-at ] change-query ;
|
'[ , , _ ?set-at ] change-query ;
|
||||||
|
|
||||||
: parse-host ( string -- host port )
|
: parse-host ( string -- host port )
|
||||||
|
|
|
@ -19,7 +19,7 @@ M: counter-app init-session* drop 0 count sset ;
|
||||||
: <display-action> ( -- action )
|
: <display-action> ( -- action )
|
||||||
<page-action>
|
<page-action>
|
||||||
[ count sget "counter" set-value ] >>init
|
[ count sget "counter" set-value ] >>init
|
||||||
"$counter-app/counter" >>template ;
|
{ counter-app "counter" } >>template ;
|
||||||
|
|
||||||
: <counter-app> ( -- responder )
|
: <counter-app> ( -- responder )
|
||||||
counter-app new-dispatcher
|
counter-app new-dispatcher
|
||||||
|
|
|
@ -4,6 +4,7 @@ USING: accessors kernel sequences assocs io.files io.sockets
|
||||||
io.server
|
io.server
|
||||||
namespaces db db.sqlite smtp
|
namespaces db db.sqlite smtp
|
||||||
http.server
|
http.server
|
||||||
|
http.server.dispatchers
|
||||||
furnace.db
|
furnace.db
|
||||||
furnace.flows
|
furnace.flows
|
||||||
furnace.sessions
|
furnace.sessions
|
||||||
|
@ -51,7 +52,7 @@ TUPLE: factor-website < dispatcher ;
|
||||||
allow-password-recovery
|
allow-password-recovery
|
||||||
allow-edit-profile
|
allow-edit-profile
|
||||||
<boilerplate>
|
<boilerplate>
|
||||||
"$factor-website/page" >>template
|
{ factor-website "page" } >>template
|
||||||
<flows>
|
<flows>
|
||||||
<sessions>
|
<sessions>
|
||||||
test-db <db-persistence> ;
|
test-db <db-persistence> ;
|
||||||
|
|
|
@ -11,9 +11,9 @@
|
||||||
<t:a t:href="$pastebin/list">Pastes</t:a>
|
<t:a t:href="$pastebin/list">Pastes</t:a>
|
||||||
| <t:a t:href="$pastebin/new-paste">New Paste</t:a>
|
| <t:a t:href="$pastebin/new-paste">New Paste</t:a>
|
||||||
|
|
||||||
<t:if t:code="http.server.sessions:uid">
|
<t:if t:code="furnace.sessions:uid">
|
||||||
|
|
||||||
<t:if t:code="http.server.auth.login:allow-edit-profile?">
|
<t:if t:code="furnace.auth.login:allow-edit-profile?">
|
||||||
| <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
|
| <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
|
||||||
</t:if>
|
</t:if>
|
||||||
|
|
||||||
|
|
|
@ -3,14 +3,22 @@
|
||||||
USING: namespaces assocs sorting sequences kernel accessors
|
USING: namespaces assocs sorting sequences kernel accessors
|
||||||
hashtables sequences.lib db.types db.tuples db combinators
|
hashtables sequences.lib db.types db.tuples db combinators
|
||||||
calendar calendar.format math.parser rss urls xml.writer
|
calendar calendar.format math.parser rss urls xml.writer
|
||||||
xmode.catalog validators html.components html.templates.chloe
|
xmode.catalog validators
|
||||||
|
html.components
|
||||||
|
html.templates.chloe
|
||||||
http.server
|
http.server
|
||||||
|
http.server.dispatchers
|
||||||
|
http.server.redirection
|
||||||
|
furnace
|
||||||
furnace.actions
|
furnace.actions
|
||||||
furnace.auth
|
furnace.auth
|
||||||
furnace.auth.login
|
furnace.auth.login
|
||||||
furnace.boilerplate ;
|
furnace.boilerplate
|
||||||
|
furnace.rss ;
|
||||||
IN: webapps.pastebin
|
IN: webapps.pastebin
|
||||||
|
|
||||||
|
TUPLE: pastebin < dispatcher ;
|
||||||
|
|
||||||
! ! !
|
! ! !
|
||||||
! DOMAIN MODEL
|
! DOMAIN MODEL
|
||||||
! ! !
|
! ! !
|
||||||
|
@ -91,7 +99,7 @@ M: annotation entity-link
|
||||||
: <pastebin-action> ( -- action )
|
: <pastebin-action> ( -- action )
|
||||||
<page-action>
|
<page-action>
|
||||||
[ pastes "pastes" set-value ] >>init
|
[ pastes "pastes" set-value ] >>init
|
||||||
"$pastebin/pastebin" >>template ;
|
{ pastebin "pastebin" } >>template ;
|
||||||
|
|
||||||
: pastebin-feed-entries ( seq -- entries )
|
: pastebin-feed-entries ( seq -- entries )
|
||||||
<reversed> 20 short head [
|
<reversed> 20 short head [
|
||||||
|
@ -99,7 +107,7 @@ M: annotation entity-link
|
||||||
swap
|
swap
|
||||||
[ summary>> >>title ]
|
[ summary>> >>title ]
|
||||||
[ date>> >>pub-date ]
|
[ date>> >>pub-date ]
|
||||||
[ entity-link adjust-url >>link ]
|
[ entity-link adjust-url relative-to-request >>link ]
|
||||||
tri
|
tri
|
||||||
] map ;
|
] map ;
|
||||||
|
|
||||||
|
@ -130,7 +138,7 @@ M: annotation entity-link
|
||||||
] nest-values
|
] nest-values
|
||||||
] >>init
|
] >>init
|
||||||
|
|
||||||
"$pastebin/paste" >>template ;
|
{ pastebin "paste" } >>template ;
|
||||||
|
|
||||||
: paste-feed-entries ( paste -- entries )
|
: paste-feed-entries ( paste -- entries )
|
||||||
fetch-annotations annotations>> pastebin-feed-entries ;
|
fetch-annotations annotations>> pastebin-feed-entries ;
|
||||||
|
@ -139,7 +147,7 @@ M: annotation entity-link
|
||||||
feed new
|
feed new
|
||||||
swap
|
swap
|
||||||
[ "Paste " swap id>> number>string append >>title ]
|
[ "Paste " swap id>> number>string append >>title ]
|
||||||
[ entity-link adjust-url >>link ]
|
[ entity-link adjust-url relative-to-request >>link ]
|
||||||
[ paste-feed-entries >>entries ]
|
[ paste-feed-entries >>entries ]
|
||||||
tri ;
|
tri ;
|
||||||
|
|
||||||
|
@ -168,7 +176,9 @@ M: annotation entity-link
|
||||||
mode-names "modes" set-value
|
mode-names "modes" set-value
|
||||||
] >>init
|
] >>init
|
||||||
|
|
||||||
"$pastebin/new-paste" >>template
|
{ pastebin "new-paste" } >>template
|
||||||
|
|
||||||
|
[ mode-names "modes" set-value ] >>validate
|
||||||
|
|
||||||
[
|
[
|
||||||
validate-entity
|
validate-entity
|
||||||
|
@ -225,8 +235,6 @@ M: annotation entity-link
|
||||||
bi
|
bi
|
||||||
] >>submit ;
|
] >>submit ;
|
||||||
|
|
||||||
TUPLE: pastebin < dispatcher ;
|
|
||||||
|
|
||||||
SYMBOL: can-delete-pastes?
|
SYMBOL: can-delete-pastes?
|
||||||
|
|
||||||
can-delete-pastes? define-capability
|
can-delete-pastes? define-capability
|
||||||
|
@ -242,7 +250,7 @@ can-delete-pastes? define-capability
|
||||||
<new-annotation-action> "new-annotation" add-responder
|
<new-annotation-action> "new-annotation" add-responder
|
||||||
<delete-annotation-action> { can-delete-pastes? } <protected> "delete-annotation" add-responder
|
<delete-annotation-action> { can-delete-pastes? } <protected> "delete-annotation" add-responder
|
||||||
<boilerplate>
|
<boilerplate>
|
||||||
"$pastebin/pastebin-common" >>template ;
|
{ pastebin "pastebin-common" } >>template ;
|
||||||
|
|
||||||
: init-pastes-table \ paste ensure-table ;
|
: init-pastes-table \ paste ensure-table ;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
<p class="news">
|
<p class="news">
|
||||||
<strong><t:view t:component="title" /></strong> <br/>
|
<strong><t:view t:component="title" /></strong> <br/>
|
||||||
<t:a value="link" t:session="none" class="more">Read More...</t:a>
|
<t:a value="link" class="more">Read More...</t:a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</t:chloe>
|
</t:chloe>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
||||||
|
|
||||||
<h2 class="posting-title">
|
<h2 class="posting-title">
|
||||||
<t:a t:value="link" t:session="none"><t:view t:component="title" /></t:a>
|
<t:a t:value="link"><t:view t:component="title" /></t:a>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<p class="posting-body">
|
<p class="posting-body">
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="posting-date">
|
<p class="posting-date">
|
||||||
<t:a t:value="link" t:session="none"><t:view t:component="pub-date" /></t:a>
|
<t:a t:value="link"><t:view t:component="pub-date" /></t:a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</t:chloe>
|
</t:chloe>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
<p class="news">
|
<p class="news">
|
||||||
<strong><t:view t:component="title" /></strong> <br/>
|
<strong><t:view t:component="title" /></strong> <br/>
|
||||||
<t:a value="link" t:session="none" class="more">Read More...</t:a>
|
<t:a value="link" class="more">Read More...</t:a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</t:bind-each>
|
</t:bind-each>
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
| <t:a t:href="$planet-factor/feed.xml">Atom Feed</t:a>
|
| <t:a t:href="$planet-factor/feed.xml">Atom Feed</t:a>
|
||||||
| <t:a t:href="$planet-factor/admin">Admin</t:a>
|
| <t:a t:href="$planet-factor/admin">Admin</t:a>
|
||||||
|
|
||||||
<t:if t:code="http.server.sessions:uid">
|
<t:if t:code="furnace.sessions:uid">
|
||||||
<t:if t:code="http.server.auth.login:allow-edit-profile?">
|
<t:if t:code="furnace.auth.login:allow-edit-profile?">
|
||||||
| <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
|
| <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
|
||||||
</t:if>
|
</t:if>
|
||||||
|
|
||||||
|
|
|
@ -7,12 +7,19 @@ html.components
|
||||||
rss urls xml.writer
|
rss urls xml.writer
|
||||||
validators
|
validators
|
||||||
http.server
|
http.server
|
||||||
|
http.server.dispatchers
|
||||||
|
furnace
|
||||||
furnace.actions
|
furnace.actions
|
||||||
furnace.boilerplate
|
furnace.boilerplate
|
||||||
furnace.auth.login
|
furnace.auth.login
|
||||||
furnace.auth ;
|
furnace.auth
|
||||||
|
furnace.rss ;
|
||||||
IN: webapps.planet
|
IN: webapps.planet
|
||||||
|
|
||||||
|
TUPLE: planet-factor < dispatcher ;
|
||||||
|
|
||||||
|
TUPLE: planet-factor-admin < dispatcher ;
|
||||||
|
|
||||||
TUPLE: blog id name www-url feed-url ;
|
TUPLE: blog id name www-url feed-url ;
|
||||||
|
|
||||||
M: blog link-title name>> ;
|
M: blog link-title name>> ;
|
||||||
|
@ -58,7 +65,7 @@ posting "POSTINGS"
|
||||||
: <edit-blogroll-action> ( -- action )
|
: <edit-blogroll-action> ( -- action )
|
||||||
<page-action>
|
<page-action>
|
||||||
[ blogroll "blogroll" set-value ] >>init
|
[ blogroll "blogroll" set-value ] >>init
|
||||||
"$planet-factor/admin" >>template ;
|
{ planet-factor "admin" } >>template ;
|
||||||
|
|
||||||
: <planet-action> ( -- action )
|
: <planet-action> ( -- action )
|
||||||
<page-action>
|
<page-action>
|
||||||
|
@ -67,7 +74,7 @@ posting "POSTINGS"
|
||||||
postings "postings" set-value
|
postings "postings" set-value
|
||||||
] >>init
|
] >>init
|
||||||
|
|
||||||
"$planet-factor/planet" >>template ;
|
{ planet-factor "planet" } >>template ;
|
||||||
|
|
||||||
: planet-feed ( -- feed )
|
: planet-feed ( -- feed )
|
||||||
feed new
|
feed new
|
||||||
|
@ -131,7 +138,7 @@ posting "POSTINGS"
|
||||||
|
|
||||||
: <new-blog-action> ( -- action )
|
: <new-blog-action> ( -- action )
|
||||||
<page-action>
|
<page-action>
|
||||||
"$planet-factor/new-blog" >>template
|
{ planet-factor "new-blog" } >>template
|
||||||
|
|
||||||
[ validate-blog ] >>validate
|
[ validate-blog ] >>validate
|
||||||
|
|
||||||
|
@ -155,7 +162,7 @@ posting "POSTINGS"
|
||||||
"id" value <blog> select-tuple from-object
|
"id" value <blog> select-tuple from-object
|
||||||
] >>init
|
] >>init
|
||||||
|
|
||||||
"$planet-factor/edit-blog" >>template
|
{ planet-factor "edit-blog" } >>template
|
||||||
|
|
||||||
[
|
[
|
||||||
validate-integer-id
|
validate-integer-id
|
||||||
|
@ -175,8 +182,6 @@ posting "POSTINGS"
|
||||||
tri
|
tri
|
||||||
] >>submit ;
|
] >>submit ;
|
||||||
|
|
||||||
TUPLE: planet-factor-admin < dispatcher ;
|
|
||||||
|
|
||||||
: <planet-factor-admin> ( -- responder )
|
: <planet-factor-admin> ( -- responder )
|
||||||
planet-factor-admin new-dispatcher
|
planet-factor-admin new-dispatcher
|
||||||
<edit-blogroll-action> "blogroll" add-main-responder
|
<edit-blogroll-action> "blogroll" add-main-responder
|
||||||
|
@ -189,15 +194,13 @@ SYMBOL: can-administer-planet-factor?
|
||||||
|
|
||||||
can-administer-planet-factor? define-capability
|
can-administer-planet-factor? define-capability
|
||||||
|
|
||||||
TUPLE: planet-factor < dispatcher ;
|
|
||||||
|
|
||||||
: <planet-factor> ( -- responder )
|
: <planet-factor> ( -- responder )
|
||||||
planet-factor new-dispatcher
|
planet-factor new-dispatcher
|
||||||
<planet-action> "list" add-main-responder
|
<planet-action> "list" add-main-responder
|
||||||
<feed-action> "feed.xml" add-responder
|
<feed-action> "feed.xml" add-responder
|
||||||
<planet-factor-admin> { can-administer-planet-factor? } <protected> "admin" add-responder
|
<planet-factor-admin> { can-administer-planet-factor? } <protected> "admin" add-responder
|
||||||
<boilerplate>
|
<boilerplate>
|
||||||
"$planet-factor/planet-common" >>template ;
|
{ planet-factor "planet-common" } >>template ;
|
||||||
|
|
||||||
: start-update-task ( db params -- )
|
: start-update-task ( db params -- )
|
||||||
'[ , , [ update-cached-postings ] with-db ] 10 minutes every drop ;
|
'[ , , [ update-cached-postings ] with-db ] 10 minutes every drop ;
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<t:bind-each t:name="postings">
|
<t:bind-each t:name="postings">
|
||||||
|
|
||||||
<h2 class="posting-title">
|
<h2 class="posting-title">
|
||||||
<t:a t:value="link" t:session="none"><t:label t:name="title" /></t:a>
|
<t:a t:value="link"><t:label t:name="title" /></t:a>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<p class="posting-body">
|
<p class="posting-body">
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="posting-date">
|
<p class="posting-date">
|
||||||
<t:a t:value="link" t:session="none"><t:label t:name="pub-date" /></t:a>
|
<t:a t:value="link"><t:label t:name="pub-date" /></t:a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</t:bind-each>
|
</t:bind-each>
|
||||||
|
|
|
@ -4,15 +4,19 @@ USING: accessors kernel sequences namespaces
|
||||||
db db.types db.tuples validators hashtables urls
|
db db.types db.tuples validators hashtables urls
|
||||||
html.components
|
html.components
|
||||||
html.templates.chloe
|
html.templates.chloe
|
||||||
|
http.server
|
||||||
|
http.server.dispatchers
|
||||||
|
furnace
|
||||||
furnace.sessions
|
furnace.sessions
|
||||||
furnace.boilerplate
|
furnace.boilerplate
|
||||||
furnace.auth
|
furnace.auth
|
||||||
furnace.actions
|
furnace.actions
|
||||||
furnace.db
|
furnace.db
|
||||||
furnace.auth.login
|
furnace.auth.login ;
|
||||||
http.server ;
|
|
||||||
IN: webapps.todo
|
IN: webapps.todo
|
||||||
|
|
||||||
|
TUPLE: todo-list < dispatcher ;
|
||||||
|
|
||||||
TUPLE: todo uid id priority summary description ;
|
TUPLE: todo uid id priority summary description ;
|
||||||
|
|
||||||
todo "TODO"
|
todo "TODO"
|
||||||
|
@ -38,7 +42,7 @@ todo "TODO"
|
||||||
"id" value <todo> select-tuple from-object
|
"id" value <todo> select-tuple from-object
|
||||||
] >>init
|
] >>init
|
||||||
|
|
||||||
"$todo-list/view-todo" >>template ;
|
{ todo-list "view-todo" } >>template ;
|
||||||
|
|
||||||
: validate-todo ( -- )
|
: validate-todo ( -- )
|
||||||
{
|
{
|
||||||
|
@ -51,7 +55,7 @@ todo "TODO"
|
||||||
<page-action>
|
<page-action>
|
||||||
[ 0 "priority" set-value ] >>init
|
[ 0 "priority" set-value ] >>init
|
||||||
|
|
||||||
"$todo-list/new-todo" >>template
|
{ todo-list "new-todo" } >>template
|
||||||
|
|
||||||
[ validate-todo ] >>validate
|
[ validate-todo ] >>validate
|
||||||
|
|
||||||
|
@ -75,7 +79,7 @@ todo "TODO"
|
||||||
"id" value <todo> select-tuple from-object
|
"id" value <todo> select-tuple from-object
|
||||||
] >>init
|
] >>init
|
||||||
|
|
||||||
"$todo-list/edit-todo" >>template
|
{ todo-list "edit-todo" } >>template
|
||||||
|
|
||||||
[
|
[
|
||||||
validate-integer-id
|
validate-integer-id
|
||||||
|
@ -107,9 +111,7 @@ todo "TODO"
|
||||||
: <list-action> ( -- action )
|
: <list-action> ( -- action )
|
||||||
<page-action>
|
<page-action>
|
||||||
[ f <todo> select-tuples "items" set-value ] >>init
|
[ f <todo> select-tuples "items" set-value ] >>init
|
||||||
"$todo-list/todo-list" >>template ;
|
{ todo-list "todo-list" } >>template ;
|
||||||
|
|
||||||
TUPLE: todo-list < dispatcher ;
|
|
||||||
|
|
||||||
: <todo-list> ( -- responder )
|
: <todo-list> ( -- responder )
|
||||||
todo-list new-dispatcher
|
todo-list new-dispatcher
|
||||||
|
@ -119,5 +121,5 @@ TUPLE: todo-list < dispatcher ;
|
||||||
<edit-action> "edit" add-responder
|
<edit-action> "edit" add-responder
|
||||||
<delete-action> "delete" add-responder
|
<delete-action> "delete" add-responder
|
||||||
<boilerplate>
|
<boilerplate>
|
||||||
"$todo-list/todo" >>template
|
{ todo-list "todo" } >>template
|
||||||
f <protected> ;
|
f <protected> ;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<t:a t:href="$todo-list/list">List Items</t:a>
|
<t:a t:href="$todo-list/list">List Items</t:a>
|
||||||
| <t:a t:href="$todo-list/new">Add Item</t:a>
|
| <t:a t:href="$todo-list/new">Add Item</t:a>
|
||||||
|
|
||||||
<t:if t:code="http.server.auth.login:allow-edit-profile?">
|
<t:if t:code="furnace.auth.login:allow-edit-profile?">
|
||||||
| <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
|
| <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
|
||||||
</t:if>
|
</t:if>
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ USING: kernel sequences accessors namespaces combinators words
|
||||||
assocs db.tuples arrays splitting strings validators urls
|
assocs db.tuples arrays splitting strings validators urls
|
||||||
html.elements
|
html.elements
|
||||||
html.components
|
html.components
|
||||||
|
furnace
|
||||||
furnace.boilerplate
|
furnace.boilerplate
|
||||||
furnace.auth.providers
|
furnace.auth.providers
|
||||||
furnace.auth.providers.db
|
furnace.auth.providers.db
|
||||||
|
@ -11,9 +12,12 @@ furnace.auth.login
|
||||||
furnace.auth
|
furnace.auth
|
||||||
furnace.sessions
|
furnace.sessions
|
||||||
furnace.actions
|
furnace.actions
|
||||||
http.server ;
|
http.server
|
||||||
|
http.server.dispatchers ;
|
||||||
IN: webapps.user-admin
|
IN: webapps.user-admin
|
||||||
|
|
||||||
|
TUPLE: user-admin < dispatcher ;
|
||||||
|
|
||||||
: word>string ( word -- string )
|
: word>string ( word -- string )
|
||||||
[ word-vocabulary ] [ drop ":" ] [ word-name ] tri 3append ;
|
[ word-vocabulary ] [ drop ":" ] [ word-name ] tri 3append ;
|
||||||
|
|
||||||
|
@ -29,7 +33,7 @@ IN: webapps.user-admin
|
||||||
: <user-list-action> ( -- action )
|
: <user-list-action> ( -- action )
|
||||||
<page-action>
|
<page-action>
|
||||||
[ f <user> select-tuples "users" set-value ] >>init
|
[ f <user> select-tuples "users" set-value ] >>init
|
||||||
"$user-admin/user-list" >>template ;
|
{ user-admin "user-list" } >>template ;
|
||||||
|
|
||||||
: init-capabilities ( -- )
|
: init-capabilities ( -- )
|
||||||
capabilities get words>strings "capabilities" set-value ;
|
capabilities get words>strings "capabilities" set-value ;
|
||||||
|
@ -46,7 +50,7 @@ IN: webapps.user-admin
|
||||||
init-capabilities
|
init-capabilities
|
||||||
] >>init
|
] >>init
|
||||||
|
|
||||||
"$user-admin/new-user" >>template
|
{ user-admin "new-user" } >>template
|
||||||
|
|
||||||
[
|
[
|
||||||
init-capabilities
|
init-capabilities
|
||||||
|
@ -94,7 +98,7 @@ IN: webapps.user-admin
|
||||||
capabilities get words>strings "capabilities" set-value
|
capabilities get words>strings "capabilities" set-value
|
||||||
] >>init
|
] >>init
|
||||||
|
|
||||||
"$user-admin/edit-user" >>template
|
{ user-admin "edit-user" } >>template
|
||||||
|
|
||||||
[
|
[
|
||||||
init-capabilities
|
init-capabilities
|
||||||
|
@ -140,8 +144,6 @@ IN: webapps.user-admin
|
||||||
URL" $user-admin" <redirect>
|
URL" $user-admin" <redirect>
|
||||||
] >>submit ;
|
] >>submit ;
|
||||||
|
|
||||||
TUPLE: user-admin < dispatcher ;
|
|
||||||
|
|
||||||
SYMBOL: can-administer-users?
|
SYMBOL: can-administer-users?
|
||||||
|
|
||||||
can-administer-users? define-capability
|
can-administer-users? define-capability
|
||||||
|
@ -153,7 +155,7 @@ can-administer-users? define-capability
|
||||||
<edit-user-action> "edit" add-responder
|
<edit-user-action> "edit" add-responder
|
||||||
<delete-user-action> "delete" add-responder
|
<delete-user-action> "delete" add-responder
|
||||||
<boilerplate>
|
<boilerplate>
|
||||||
"$user-admin/user-admin" >>template
|
{ user-admin "user-admin" } >>template
|
||||||
{ can-administer-users? } <protected> ;
|
{ can-administer-users? } <protected> ;
|
||||||
|
|
||||||
: make-admin ( username -- )
|
: make-admin ( username -- )
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<t:a t:href="$user-admin">List Users</t:a>
|
<t:a t:href="$user-admin">List Users</t:a>
|
||||||
| <t:a t:href="$user-admin/new">Add User</t:a>
|
| <t:a t:href="$user-admin/new">Add User</t:a>
|
||||||
|
|
||||||
<t:if t:code="http.server.auth.login:allow-edit-profile?">
|
<t:if t:code="furnace.auth.login:allow-edit-profile?">
|
||||||
| <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
|
| <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
|
||||||
</t:if>
|
</t:if>
|
||||||
|
|
||||||
|
|
|
@ -10,9 +10,9 @@
|
||||||
| <t:a t:href="$wiki/articles">All Articles</t:a>
|
| <t:a t:href="$wiki/articles">All Articles</t:a>
|
||||||
| <t:a t:href="$wiki/changes">Recent Changes</t:a>
|
| <t:a t:href="$wiki/changes">Recent Changes</t:a>
|
||||||
|
|
||||||
<t:if t:code="http.server.sessions:uid">
|
<t:if t:code="furnace.sessions:uid">
|
||||||
|
|
||||||
<t:if t:code="http.server.auth.login:allow-edit-profile?">
|
<t:if t:code="furnace.auth.login:allow-edit-profile?">
|
||||||
| <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
|
| <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
|
||||||
</t:if>
|
</t:if>
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@ USING: accessors kernel hashtables calendar
|
||||||
namespaces splitting sequences sorting math.order
|
namespaces splitting sequences sorting math.order
|
||||||
html.components
|
html.components
|
||||||
http.server
|
http.server
|
||||||
|
http.server.dispatchers
|
||||||
|
furnace
|
||||||
furnace.actions
|
furnace.actions
|
||||||
furnace.auth
|
furnace.auth
|
||||||
furnace.auth.login
|
furnace.auth.login
|
||||||
|
@ -12,6 +14,8 @@ validators
|
||||||
db.types db.tuples lcs farkup urls ;
|
db.types db.tuples lcs farkup urls ;
|
||||||
IN: webapps.wiki
|
IN: webapps.wiki
|
||||||
|
|
||||||
|
TUPLE: wiki < dispatcher ;
|
||||||
|
|
||||||
TUPLE: article title revision ;
|
TUPLE: article title revision ;
|
||||||
|
|
||||||
article "ARTICLES" {
|
article "ARTICLES" {
|
||||||
|
@ -64,7 +68,7 @@ revision "REVISIONS" {
|
||||||
[
|
[
|
||||||
"title" value dup <article> select-tuple [
|
"title" value dup <article> select-tuple [
|
||||||
revision>> <revision> select-tuple from-object
|
revision>> <revision> select-tuple from-object
|
||||||
"$wiki/view" <chloe-content>
|
{ wiki "view" } <chloe-content>
|
||||||
] [
|
] [
|
||||||
<url>
|
<url>
|
||||||
"$wiki/edit" >>path
|
"$wiki/edit" >>path
|
||||||
|
@ -81,7 +85,7 @@ revision "REVISIONS" {
|
||||||
select-tuple from-object
|
select-tuple from-object
|
||||||
] >>init
|
] >>init
|
||||||
|
|
||||||
"$wiki/view" >>template ;
|
{ wiki "view" } >>template ;
|
||||||
|
|
||||||
: add-revision ( revision -- )
|
: add-revision ( revision -- )
|
||||||
[ insert-tuple ]
|
[ insert-tuple ]
|
||||||
|
@ -102,7 +106,7 @@ revision "REVISIONS" {
|
||||||
] when*
|
] when*
|
||||||
] >>init
|
] >>init
|
||||||
|
|
||||||
"$wiki/edit" >>template
|
{ wiki "edit" } >>template
|
||||||
|
|
||||||
[
|
[
|
||||||
validate-title
|
validate-title
|
||||||
|
@ -131,7 +135,7 @@ revision "REVISIONS" {
|
||||||
"revisions" set-value
|
"revisions" set-value
|
||||||
] >>init
|
] >>init
|
||||||
|
|
||||||
"$wiki/revisions" >>template ;
|
{ wiki "revisions" } >>template ;
|
||||||
|
|
||||||
: <rollback-action> ( -- action )
|
: <rollback-action> ( -- action )
|
||||||
<action>
|
<action>
|
||||||
|
@ -158,7 +162,7 @@ revision "REVISIONS" {
|
||||||
"changes" set-value
|
"changes" set-value
|
||||||
] >>init
|
] >>init
|
||||||
|
|
||||||
"$wiki/changes" >>template ;
|
{ wiki "changes" } >>template ;
|
||||||
|
|
||||||
: <delete-action> ( -- action )
|
: <delete-action> ( -- action )
|
||||||
<action>
|
<action>
|
||||||
|
@ -185,7 +189,7 @@ revision "REVISIONS" {
|
||||||
2bi
|
2bi
|
||||||
] >>init
|
] >>init
|
||||||
|
|
||||||
"$wiki/diff" >>template ;
|
{ wiki "diff" } >>template ;
|
||||||
|
|
||||||
: <list-articles-action> ( -- action )
|
: <list-articles-action> ( -- action )
|
||||||
<page-action>
|
<page-action>
|
||||||
|
@ -195,7 +199,7 @@ revision "REVISIONS" {
|
||||||
"articles" set-value
|
"articles" set-value
|
||||||
] >>init
|
] >>init
|
||||||
|
|
||||||
"$wiki/articles" >>template ;
|
{ wiki "articles" } >>template ;
|
||||||
|
|
||||||
: <user-edits-action> ( -- action )
|
: <user-edits-action> ( -- action )
|
||||||
<page-action>
|
<page-action>
|
||||||
|
@ -205,9 +209,7 @@ revision "REVISIONS" {
|
||||||
select-tuples "user-edits" set-value
|
select-tuples "user-edits" set-value
|
||||||
] >>init
|
] >>init
|
||||||
|
|
||||||
"$wiki/user-edits" >>template ;
|
{ wiki "user-edits" } >>template ;
|
||||||
|
|
||||||
TUPLE: wiki < dispatcher ;
|
|
||||||
|
|
||||||
: <wiki> ( -- dispatcher )
|
: <wiki> ( -- dispatcher )
|
||||||
wiki new-dispatcher
|
wiki new-dispatcher
|
||||||
|
@ -223,4 +225,4 @@ TUPLE: wiki < dispatcher ;
|
||||||
<edit-article-action> { } <protected> "edit" add-responder
|
<edit-article-action> { } <protected> "edit" add-responder
|
||||||
<delete-action> { } <protected> "delete" add-responder
|
<delete-action> { } <protected> "delete" add-responder
|
||||||
<boilerplate>
|
<boilerplate>
|
||||||
"$wiki/wiki-common" >>template ;
|
{ wiki "wiki-common" } >>template ;
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
IN: furnace.callbacks
|
USING: furnace furnace.actions furnace.callbacks accessors
|
||||||
USING: furnace.actions furnace.callbacks accessors
|
http http.server http.server.responses tools.test
|
||||||
http.server http tools.test namespaces io fry sequences
|
namespaces io fry sequences
|
||||||
splitting kernel hashtables continuations ;
|
splitting kernel hashtables continuations ;
|
||||||
|
IN: furnace.callbacks.tests
|
||||||
|
|
||||||
[ 123 ] [
|
[ 123 ] [
|
||||||
[
|
[
|
||||||
init-request
|
<request> "GET" >>method init-request
|
||||||
|
|
||||||
<request> "GET" >>method request set
|
|
||||||
[
|
[
|
||||||
exit-continuation set
|
exit-continuation set
|
||||||
{ }
|
{ }
|
||||||
|
@ -19,8 +18,6 @@ splitting kernel hashtables continuations ;
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
[
|
[
|
||||||
init-request
|
|
||||||
|
|
||||||
<action> [
|
<action> [
|
||||||
[
|
[
|
||||||
"hello" print
|
"hello" print
|
||||||
|
@ -32,9 +29,11 @@ splitting kernel hashtables continuations ;
|
||||||
<callback-responder> "r" set
|
<callback-responder> "r" set
|
||||||
|
|
||||||
[ 123 ] [
|
[ 123 ] [
|
||||||
|
<request> init-request
|
||||||
|
|
||||||
[
|
[
|
||||||
exit-continuation set
|
exit-continuation set
|
||||||
<request> "GET" >>method request set
|
<request> "GET" >>method init-request
|
||||||
{ } "r" get call-responder
|
{ } "r" get call-responder
|
||||||
] callcc1
|
] callcc1
|
||||||
|
|
||||||
|
@ -42,9 +41,9 @@ splitting kernel hashtables continuations ;
|
||||||
|
|
||||||
<request>
|
<request>
|
||||||
"GET" >>method
|
"GET" >>method
|
||||||
swap cont-id associate >>query
|
dup url>> rot cont-id associate >>query drop
|
||||||
"/" >>path
|
dup url>> "/" >>path drop
|
||||||
request set
|
init-request
|
||||||
|
|
||||||
[
|
[
|
||||||
exit-continuation set
|
exit-continuation set
|
||||||
|
@ -55,9 +54,9 @@ splitting kernel hashtables continuations ;
|
||||||
! get-post-get
|
! get-post-get
|
||||||
<request>
|
<request>
|
||||||
"GET" >>method
|
"GET" >>method
|
||||||
swap "location" header "=" last-split1 nip cont-id associate >>query
|
dup url>> rot "location" header query>> >>query drop
|
||||||
"/" >>path
|
dup url>> "/" >>path drop
|
||||||
request set
|
init-request
|
||||||
|
|
||||||
[
|
[
|
||||||
exit-continuation set
|
exit-continuation set
|
|
@ -3,7 +3,8 @@
|
||||||
! See http://factorcode.org/license.txt for BSD license.
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
USING: http http.server io kernel math namespaces
|
USING: http http.server io kernel math namespaces
|
||||||
continuations calendar sequences assocs hashtables
|
continuations calendar sequences assocs hashtables
|
||||||
accessors arrays alarms quotations combinators fry assocs.lib ;
|
accessors arrays alarms quotations combinators fry
|
||||||
|
http.server.redirection furnace assocs.lib urls ;
|
||||||
IN: furnace.callbacks
|
IN: furnace.callbacks
|
||||||
|
|
||||||
SYMBOL: responder
|
SYMBOL: responder
|
||||||
|
@ -11,9 +12,6 @@ SYMBOL: responder
|
||||||
TUPLE: callback-responder responder callbacks ;
|
TUPLE: callback-responder responder callbacks ;
|
||||||
|
|
||||||
: <callback-responder> ( responder -- responder' )
|
: <callback-responder> ( responder -- responder' )
|
||||||
#! A continuation responder is a special type of session
|
|
||||||
#! manager. However it works entirely differently from
|
|
||||||
#! the URL and cookie session managers.
|
|
||||||
H{ } clone callback-responder boa ;
|
H{ } clone callback-responder boa ;
|
||||||
|
|
||||||
TUPLE: callback cont quot expires alarm responder ;
|
TUPLE: callback cont quot expires alarm responder ;
|
||||||
|
@ -44,7 +42,7 @@ TUPLE: callback cont quot expires alarm responder ;
|
||||||
: register-callback ( cont quot expires? -- id )
|
: register-callback ( cont quot expires? -- id )
|
||||||
<callback> callback-responder get callbacks>> set-at-unique ;
|
<callback> callback-responder get callbacks>> set-at-unique ;
|
||||||
|
|
||||||
: forward-to-url ( url query -- * )
|
: forward-to-url ( url -- * )
|
||||||
#! When executed inside a 'show' call, this will force a
|
#! When executed inside a 'show' call, this will force a
|
||||||
#! HTTP 302 to occur to instruct the browser to forward to
|
#! HTTP 302 to occur to instruct the browser to forward to
|
||||||
#! the request URL.
|
#! the request URL.
|
||||||
|
@ -56,7 +54,8 @@ TUPLE: callback cont quot expires alarm responder ;
|
||||||
#! When executed inside a 'show' call, this will force a
|
#! When executed inside a 'show' call, this will force a
|
||||||
#! HTTP 302 to occur to instruct the browser to forward to
|
#! HTTP 302 to occur to instruct the browser to forward to
|
||||||
#! the request URL.
|
#! the request URL.
|
||||||
f swap cont-id associate forward-to-url ;
|
<url>
|
||||||
|
swap cont-id set-query-param forward-to-url ;
|
||||||
|
|
||||||
: restore-request ( pair -- )
|
: restore-request ( pair -- )
|
||||||
first3 exit-continuation set request set call ;
|
first3 exit-continuation set request set call ;
|
||||||
|
@ -94,7 +93,7 @@ SYMBOL: current-show
|
||||||
call exit-with ; inline
|
call exit-with ; inline
|
||||||
|
|
||||||
: resuming-callback ( responder request -- id )
|
: resuming-callback ( responder request -- id )
|
||||||
cont-id query-param swap callbacks>> at ;
|
url>> cont-id query-param swap callbacks>> at ;
|
||||||
|
|
||||||
M: callback-responder call-responder* ( path responder -- response )
|
M: callback-responder call-responder* ( path responder -- response )
|
||||||
'[
|
'[
|
Loading…
Reference in New Issue