Merge branch 'master' of factorcode.org:/git/factor

db4
Eduardo Cavazos 2008-06-14 14:46:16 -05:00
commit 1883929dee
41 changed files with 346 additions and 207 deletions

View File

@ -26,7 +26,7 @@ IN: vectors.tests
[ V{ 1 2 } ] [ [ 1 2 ] >vector ] unit-test [ V{ 1 2 } ] [ [ 1 2 ] >vector ] unit-test
[ t ] [ [ t ] [
100 [ 100 random ] V{ } map-as 100 [ 100 random ] V{ } replicate-as
dup >array >vector = dup >array >vector =
] unit-test ] unit-test

View File

@ -53,7 +53,7 @@ M: sqlite-result-set dispose ( result-set -- )
M: sqlite-statement low-level-bind ( statement -- ) M: sqlite-statement low-level-bind ( statement -- )
[ statement-bind-params ] [ statement-handle ] bi [ statement-bind-params ] [ statement-handle ] bi
swap [ [ key>> ] [ value>> ] [ type>> ] tri sqlite-bind-type ] with each ; [ swap [ key>> ] [ value>> ] [ type>> ] tri sqlite-bind-type ] curry each ;
M: sqlite-statement bind-statement* ( statement -- ) M: sqlite-statement bind-statement* ( statement -- )
sqlite-maybe-prepare sqlite-maybe-prepare

View File

@ -122,6 +122,9 @@ M: retryable execute-statement* ( statement type -- )
: ensure-table ( class -- ) : ensure-table ( class -- )
[ create-table ] curry ignore-errors ; [ create-table ] curry ignore-errors ;
: ensure-tables ( classes -- )
[ ensure-table ] each ;
: insert-db-assigned-statement ( tuple -- ) : insert-db-assigned-statement ( tuple -- )
dup class dup class
db get db-insert-statements [ <insert-db-assigned-statement> ] cache db get db-insert-statements [ <insert-db-assigned-statement> ] cache

View File

@ -3,14 +3,12 @@ namespaces sequences system combinators
editors.vim editors.gvim.backend vocabs.loader ; editors.vim editors.gvim.backend vocabs.loader ;
IN: editors.gvim IN: editors.gvim
TUPLE: gvim ; SINGLETON: gvim
M: gvim vim-command ( file line -- string ) M: gvim vim-command ( file line -- string )
[ "\"" % gvim-path % "\" \"" % swap % "\" +" % # ] "" make ; [ gvim-path , swap , "+" swap number>string append , ] { } make ;
t vim-detach set-global ! don't block the ui gvim vim-editor set-global
T{ gvim } vim-editor set-global
{ {
{ [ os unix? ] [ "editors.gvim.unix" ] } { [ os unix? ] [ "editors.gvim.unix" ] }

View File

@ -11,7 +11,5 @@ $nl
"USE: vim" "USE: vim"
"\"c:\\\\program files\\\\vim\\\\vim70\\\\gvim\" vim-path set-global" "\"c:\\\\program files\\\\vim\\\\vim70\\\\gvim\" vim-path set-global"
} }
"On Unix, you may omit the last line if " { $snippet "\"vim\"" } " is in your " { $snippet "$PATH" } "." "On Unix, you may omit the last line if " { $snippet "\"vim\"" } " is in your " { $snippet "$PATH" } "." ;
$nl
"If you are running the terminal version of Vim, you want it to block Factor until exiting, but for GVim the opposite is desired, so that one can work in Factor and GVim concurrently. The " { $link vim-detach } " global variable can be set to " { $link t } " to detach the Vim process. The default is " { $link f } "." ;

View File

@ -3,24 +3,20 @@ namespaces parser prettyprint sequences editors accessors ;
IN: editors.vim IN: editors.vim
SYMBOL: vim-path SYMBOL: vim-path
SYMBOL: vim-detach
SYMBOL: vim-editor SYMBOL: vim-editor
HOOK: vim-command vim-editor HOOK: vim-command vim-editor ( file line -- array )
TUPLE: vim ; SINGLETON: vim
M: vim vim-command ( file line -- array ) M: vim vim-command
[ [
vim-path get , swap , "+" swap number>string append , vim-path get , swap , "+" swap number>string append ,
] { } make ; ] { } make ;
: vim-location ( file line -- ) : vim-location ( file line -- )
vim-command vim-command try-process ;
<process> swap >>command
vim-detach get-global [ t >>detached ] when
try-process ;
"vim" vim-path set-global "vim" vim-path set-global
[ vim-location ] edit-hook set-global [ vim-location ] edit-hook set-global
T{ vim } vim-editor set-global vim vim-editor set-global

View File

@ -1,12 +1,13 @@
! Copyright (C) 2008 Doug Coleman. ! Copyright (C) 2008 Doug Coleman.
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: arrays io io.styles kernel memoize namespaces peg USING: arrays io io.styles kernel memoize namespaces peg math
sequences strings html.elements xml.entities xmode.code2html combinators sequences strings html.elements xml.entities
splitting io.streams.string peg.parsers xmode.code2html splitting io.streams.string peg.parsers
sequences.deep unicode.categories ; sequences.deep unicode.categories ;
IN: farkup IN: farkup
SYMBOL: relative-link-prefix SYMBOL: relative-link-prefix
SYMBOL: disable-images?
SYMBOL: link-no-follow? SYMBOL: link-no-follow?
<PRIVATE <PRIVATE
@ -67,13 +68,19 @@ MEMO: eq ( -- parser )
</pre> </pre>
] with-string-writer ; ] with-string-writer ;
: invalid-url "javascript:alert('Invalid URL in farkup');" ;
: check-url ( href -- href' ) : check-url ( href -- href' )
CHAR: : over member? [ {
dup { "http://" "https://" "ftp://" } [ head? ] with contains? { [ dup empty? ] [ drop invalid-url ] }
[ drop "/" ] unless { [ dup [ 127 > ] contains? ] [ drop invalid-url ] }
] [ { [ dup first "/\\" member? ] [ drop invalid-url ] }
relative-link-prefix get prepend { [ CHAR: : over member? ] [
] if ; dup { "http://" "https://" "ftp://" } [ head? ] with contains?
[ drop invalid-url ] unless
] }
[ relative-link-prefix get prepend ]
} cond ;
: escape-link ( href text -- href-esc text-esc ) : escape-link ( href text -- href-esc text-esc )
>r check-url escape-quoted-string r> escape-string ; >r check-url escape-quoted-string r> escape-string ;
@ -82,18 +89,22 @@ MEMO: eq ( -- parser )
escape-link escape-link
[ [
"<a" , "<a" ,
" href=\"" , >r , r> " href=\"" , >r , r> "\"" ,
link-no-follow? get [ " nofollow=\"true\"" , ] when link-no-follow? get [ " nofollow=\"true\"" , ] when
"\">" , , "</a>" , ">" , , "</a>" ,
] { } make ; ] { } make ;
: make-image-link ( href alt -- seq ) : make-image-link ( href alt -- seq )
escape-link disable-images? get [
[ 2drop "<strong>Images are not allowed</strong>"
"<img src=\"" , swap , "\"" , ] [
dup empty? [ drop ] [ " alt=\"" , , "\"" , ] if escape-link
"/>" , ] [
{ } make ; "<img src=\"" , swap , "\"" ,
dup empty? [ drop ] [ " alt=\"" , , "\"" , ] if
"/>" ,
] { } make
] if ;
MEMO: image-link ( -- parser ) MEMO: image-link ( -- parser )
[ [

View File

@ -53,7 +53,7 @@ TUPLE: action rest authorize init display validate submit ;
] with-exit-continuation ; ] with-exit-continuation ;
: validation-failed ( -- * ) : validation-failed ( -- * )
request get method>> "POST" = [ f ] [ <400> ] if exit-with ; post-request? [ f ] [ <400> ] if exit-with ;
: (handle-post) ( action -- response ) : (handle-post) ( action -- response )
'[ '[
@ -70,16 +70,13 @@ TUPLE: action rest authorize init display validate submit ;
: revalidate-url-key "__u" ; : revalidate-url-key "__u" ;
: check-url ( url -- ? )
request get url>>
[ [ protocol>> ] [ host>> ] [ port>> ] tri 3array ] bi@ = ;
: revalidate-url ( -- url/f ) : revalidate-url ( -- url/f )
revalidate-url-key param dup [ >url dup check-url swap and ] when ; revalidate-url-key param
dup [ >url [ same-host? ] keep and ] when ;
: handle-post ( action -- response ) : handle-post ( action -- response )
'[ '[
form-nesting-key params get at " " split form-nesting-key params get at " " split harvest
[ , (handle-post) ] [ , (handle-post) ]
[ swap '[ , , nest-values ] ] reduce [ swap '[ , , nest-values ] ] reduce
call call

View File

@ -0,0 +1,31 @@
! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: kernel sequences db.tuples alarms calendar db fry
furnace.cache
furnace.asides
furnace.flash
furnace.sessions
furnace.referrer
furnace.db
furnace.auth.providers ;
IN: furnace.alloy
: <alloy> ( responder db params -- responder' )
'[
<asides>
<flash-scopes>
<sessions>
, , <db-persistence>
<check-form-submissions>
] call ;
: state-classes { session flash-scope aside } ; inline
: init-furnace-tables ( -- )
state-classes ensure-tables
user ensure-table ;
: start-expiring ( db params -- )
'[
, , [ state-classes [ expire-state ] each ] with-db
] 5 minutes every drop ;

View File

@ -2,37 +2,60 @@
! 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
furnace http http.server http.server.filters furnace.sessions html.elements html.templates.chloe.syntax db.types db.tuples
html.elements html.templates.chloe.syntax ; http http.server http.server.filters
furnace furnace.cache furnace.sessions ;
IN: furnace.asides IN: furnace.asides
TUPLE: asides < filter-responder ; TUPLE: aside < server-state session method url post-data ;
C: <asides> asides : <aside> ( id -- aside )
aside new-server-state ;
aside "ASIDES"
{
{ "session" "SESSION" BIG-INTEGER +not-null+ }
{ "method" "METHOD" { VARCHAR 10 } +not-null+ }
{ "url" "URL" URL +not-null+ }
{ "post-data" "POST_DATA" FACTOR-BLOB }
} define-persistent
TUPLE: asides < server-state-manager ;
: <asides> ( responder -- responder' )
asides new-server-state-manager ;
: begin-aside* ( -- id ) : begin-aside* ( -- id )
request get f <aside>
[ url>> ] [ post-data>> ] [ method>> ] tri 3array session get id>> >>session
asides sget set-at-unique request get
session-changed ; [ method>> >>method ]
[ url>> >>url ]
[ post-data>> >>post-data ]
tri
[ asides get touch-state ] [ insert-tuple ] [ id>> ] tri ;
: end-aside-post ( url post-data -- response ) : end-aside-post ( aside -- response )
request [ request [
clone clone
swap >>post-data over post-data>> >>post-data
swap >>url over url>> >>url
] change ] change
request get url>> path>> split-path url>> path>> split-path
asides get responder>> call-responder ; asides get responder>> call-responder ;
ERROR: end-aside-in-get-error ; ERROR: end-aside-in-get-error ;
: get-aside ( id -- aside )
dup [ aside get-state ] when
dup [ dup session>> session get id>> = [ drop f ] unless ] when ;
: end-aside* ( url id -- response ) : end-aside* ( url id -- response )
request get method>> "POST" = [ end-aside-in-get-error ] unless post-request? [ end-aside-in-get-error ] unless
asides sget at [ aside get-state [
first3 { dup method>> {
{ "GET" [ drop <redirect> ] } { "GET" [ url>> <redirect> ] }
{ "HEAD" [ drop <redirect> ] } { "HEAD" [ url>> <redirect> ] }
{ "POST" [ end-aside-post ] } { "POST" [ end-aside-post ] }
} case } case
] [ <redirect> ] ?if ; ] [ <redirect> ] ?if ;
@ -47,13 +70,12 @@ SYMBOL: aside-id
: end-aside ( default -- response ) : end-aside ( default -- response )
aside-id [ f ] change end-aside* ; aside-id [ f ] change end-aside* ;
: request-aside-id ( request -- aside-id )
aside-id-key swap request-params at string>number ;
M: asides call-responder* M: asides call-responder*
dup asides set dup asides set
aside-id-key request get request-params at aside-id set request get request-aside-id aside-id set
call-next-method ;
M: asides init-session*
H{ } clone asides sset
call-next-method ; call-next-method ;
M: asides link-attr ( tag -- ) M: asides link-attr ( tag -- )

View File

@ -14,7 +14,7 @@ login set
"auth-test.db" temp-file sqlite-db [ "auth-test.db" temp-file sqlite-db [
init-users-table user ensure-table
[ t ] [ [ t ] [
"slava" <user> "slava" <user>

View File

@ -18,8 +18,6 @@ user "USERS"
{ "deleted" "DELETED" INTEGER +not-null+ } { "deleted" "DELETED" INTEGER +not-null+ }
} define-persistent } define-persistent
: init-users-table ( -- ) user ensure-table ;
SINGLETON: users-in-db SINGLETON: users-in-db
M: users-in-db get-user M: users-in-db get-user

36
extra/furnace/cache/cache.factor vendored Normal file
View File

@ -0,0 +1,36 @@
! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: kernel accessors math.intervals
calendar alarms fry
random db db.tuples db.types
http.server.filters ;
IN: furnace.cache
TUPLE: server-state id expires ;
: new-server-state ( id class -- server-state )
new swap >>id ; inline
server-state f
{
{ "id" "ID" +random-id+ system-random-generator }
{ "expires" "EXPIRES" TIMESTAMP +not-null+ }
} define-persistent
: get-state ( id class -- state )
new-server-state select-tuple ;
: expire-state ( class -- )
new
-1.0/0.0 now [a,b] >>expires
delete-tuples ;
TUPLE: server-state-manager < filter-responder timeout ;
: new-server-state-manager ( responder class -- responder' )
new
swap >>responder
20 minutes >>timeout ; inline
: touch-state ( state manager -- )
timeout>> from-now >>expires drop ;

View File

@ -1,38 +1,59 @@
! 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: namespaces assocs assocs.lib kernel sequences urls USING: namespaces assocs assocs.lib kernel sequences accessors
urls db.types db.tuples math.parser fry
http http.server http.server.filters http.server.redirection http http.server http.server.filters http.server.redirection
furnace furnace.sessions ; furnace furnace.cache furnace.sessions ;
IN: furnace.flash IN: furnace.flash
TUPLE: flash-scope < server-state session namespace ;
: <flash-scope> ( id -- aside )
flash-scope new-server-state ;
flash-scope "FLASH_SCOPES" {
{ "session" "SESSION" BIG-INTEGER +not-null+ }
{ "namespace" "NAMESPACE" FACTOR-BLOB +not-null+ }
} define-persistent
: flash-id-key "__f" ; : flash-id-key "__f" ;
TUPLE: flash-scopes < filter-responder ; TUPLE: flash-scopes < server-state-manager ;
C: <flash-scopes> flash-scopes : <flash-scopes> ( responder -- responder' )
flash-scopes new-server-state-manager ;
SYMBOL: flash-scope SYMBOL: flash-scope
: fget ( key -- value ) flash-scope get at ; : fget ( key -- value ) flash-scope get at ;
M: flash-scopes call-responder* : get-flash-scope ( id -- flash-scope )
flash-id-key dup [ flash-scope get-state ] when
request get request-params at dup [ dup session>> session get id>> = [ drop f ] unless ] when ;
flash-scopes sget at flash-scope set
call-next-method ;
M: flash-scopes init-session* : request-flash-scope ( request -- flash-scope )
H{ } clone flash-scopes sset flash-id-key swap request-params at string>number get-flash-scope ;
M: flash-scopes call-responder*
dup flash-scopes set
request get request-flash-scope flash-scope set
call-next-method ; call-next-method ;
: make-flash-scope ( seq -- id ) : make-flash-scope ( seq -- id )
[ dup get ] H{ } map>assoc flash-scopes sget set-at-unique f <flash-scope>
session-changed ; session get id>> >>session
swap [ dup get ] H{ } map>assoc >>namespace
[ flash-scopes get touch-state ] [ insert-tuple ] [ id>> ] tri ;
: <flash-redirect> ( url seq -- response ) : <flash-redirect> ( url seq -- response )
make-flash-scope [ clone ] dip
[ clone ] dip flash-id-key set-query-param make-flash-scope flash-id-key set-query-param
<redirect> ; <redirect> ;
: restore-flash ( seq -- ) : restore-flash ( seq -- )
[ flash-scope get key? ] filter [ [ fget ] keep set ] each ; flash-scope get dup [
namespace>>
[ '[ , key? ] filter ]
[ '[ [ , at ] keep set ] each ]
bi
] [ 2drop ] if ;

View File

@ -84,6 +84,17 @@ M: object modify-form drop ;
] } ] }
} case ; } case ;
: referrer ( -- referrer )
#! Typo is intentional, its in the HTTP spec!
"referer" request get header>> at >url ;
: user-agent ( -- user-agent )
"user-agent" request get header>> at "" or ;
: same-host? ( url -- ? )
request get url>>
[ [ protocol>> ] [ host>> ] [ port>> ] tri 3array ] bi@ = ;
SYMBOL: exit-continuation SYMBOL: exit-continuation
: exit-with ( value -- ) : exit-with ( value -- )
@ -98,7 +109,8 @@ SYMBOL: exit-continuation
[ drop f ] [ "," split [ dup value ] H{ } map>assoc ] if ; [ drop f ] [ "," split [ dup value ] H{ } map>assoc ] if ;
: a-url-path ( tag -- string ) : a-url-path ( tag -- string )
[ "href" required-attr ] [ "rest" optional-attr value ] bi [ "href" required-attr ]
[ "rest" optional-attr dup [ value ] when ] bi
[ [ "/" ?tail drop "/" ] dip present 3append ] when* ; [ [ "/" ?tail drop "/" ] dip present 3append ] when* ;
: a-url ( tag -- url ) : a-url ( tag -- url )

View File

@ -0,0 +1,16 @@
USING: accessors kernel
http.server http.server.filters http.server.responses
furnace ;
IN: furnace.referrer
TUPLE: referrer-check < filter-responder quot ;
C: <referrer-check> referrer-check
M: referrer-check call-responder*
referrer over quot>> call
[ call-next-method ]
[ 2drop 403 "Bad referrer" <trivial-response> ] if ;
: <check-form-submissions> ( responder -- responder' )
[ same-host? post-request? not or ] <referrer-check> ;

View File

@ -1,9 +1,9 @@
IN: furnace.sessions.tests IN: furnace.sessions.tests
USING: tools.test http furnace.sessions USING: tools.test http furnace.sessions
furnace.actions http.server http.server.responses furnace.actions http.server http.server.responses
math namespaces kernel accessors math namespaces kernel accessors io.sockets io.server
prettyprint io.streams.string io.files splitting destructors prettyprint io.streams.string io.files splitting destructors
sequences db db.sqlite continuations urls math.parser sequences db db.tuples db.sqlite continuations urls math.parser
furnace ; furnace ;
: with-session : with-session
@ -54,7 +54,9 @@ M: foo call-responder*
"auth-test.db" temp-file sqlite-db [ "auth-test.db" temp-file sqlite-db [
<request> init-request <request> init-request
init-sessions-table session ensure-table
"127.0.0.1" 1234 <inet4> remote-address set
[ ] [ [ ] [
<foo> <sessions> <foo> <sessions>

View File

@ -2,39 +2,28 @@
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: assocs kernel math.intervals math.parser namespaces 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 combinators.lib destructors alarms io.server
db db.tuples db.types db db.tuples db.types
http http.server http.server.dispatchers http.server.filters http http.server http.server.dispatchers http.server.filters
html.elements furnace ; html.elements
furnace furnace.cache ;
IN: furnace.sessions IN: furnace.sessions
TUPLE: session id expires uid namespace changed? ; TUPLE: session < server-state uid namespace user-agent client changed? ;
: <session> ( id -- session ) : <session> ( id -- session )
session new session new-server-state ;
swap >>id ;
session "SESSIONS" session "SESSIONS"
{ {
{ "id" "ID" +random-id+ system-random-generator }
{ "expires" "EXPIRES" TIMESTAMP +not-null+ }
{ "uid" "UID" { VARCHAR 255 } } { "uid" "UID" { VARCHAR 255 } }
{ "namespace" "NAMESPACE" FACTOR-BLOB } { "namespace" "NAMESPACE" FACTOR-BLOB +not-null+ }
{ "user-agent" "USER_AGENT" TEXT +not-null+ }
{ "client" "CLIENT" TEXT +not-null+ }
} define-persistent } define-persistent
: get-session ( id -- session ) : get-session ( id -- session )
dup [ <session> select-tuple ] when ; dup [ session get-state ] when ;
: init-sessions-table ( -- ) session ensure-table ;
: start-expiring-sessions ( db seq -- )
'[
, , [
session new
-1.0/0.0 now [a,b] >>expires
delete-tuples
] with-db
] 5 minutes every drop ;
GENERIC: init-session* ( responder -- ) GENERIC: init-session* ( responder -- )
@ -44,12 +33,11 @@ M: dispatcher init-session* default>> init-session* ;
M: filter-responder init-session* responder>> init-session* ; M: filter-responder init-session* responder>> init-session* ;
TUPLE: sessions < filter-responder timeout domain ; TUPLE: sessions < server-state-manager domain verify? ;
: <sessions> ( responder -- responder' ) : <sessions> ( responder -- responder' )
sessions new sessions new-server-state-manager
swap >>responder t >>verify? ;
20 minutes >>timeout ;
: (session-changed) ( session -- ) : (session-changed) ( session -- )
t >>changed? drop ; t >>changed? drop ;
@ -78,15 +66,20 @@ TUPLE: sessions < filter-responder timeout domain ;
: init-session ( session -- ) : init-session ( session -- )
session [ sessions get init-session* ] with-variable ; session [ sessions get init-session* ] with-variable ;
: cutoff-time ( -- time )
sessions get timeout>> from-now ;
: touch-session ( session -- ) : touch-session ( session -- )
cutoff-time >>expires drop ; sessions get touch-state ;
: remote-host ( -- string )
{
[ request get "x-forwarded-for" header ]
[ remote-address get host>> ]
} 0|| ;
: empty-session ( -- session ) : empty-session ( -- session )
f <session> f <session>
H{ } clone >>namespace H{ } clone >>namespace
remote-host >>client
user-agent >>user-agent
dup touch-session ; dup touch-session ;
: begin-session ( -- session ) : begin-session ( -- session )
@ -125,8 +118,18 @@ M: session-saver dispose
{ "POST" [ post-session-id ] } { "POST" [ post-session-id ] }
} case ; } case ;
: verify-session ( session -- session )
sessions get verify?>> [
dup [
dup
[ client>> remote-host = ]
[ user-agent>> user-agent = ]
bi and [ drop f ] unless
] when
] when ;
: request-session ( -- session/f ) : request-session ( -- session/f )
request-session-id get-session ; request-session-id get-session verify-session ;
: <session-cookie> ( id -- cookie ) : <session-cookie> ( id -- cookie )
session-id-key <cookie> session-id-key <cookie>

View File

@ -156,7 +156,7 @@ M: link-test link-href drop "http://www.apple.com/foo&bar" ;
[ ] [ "-foo\n-bar" "farkup" set-value ] unit-test [ ] [ "-foo\n-bar" "farkup" set-value ] unit-test
[ "<ul><li>foo</li><li>bar</li></ul>" ] [ [ "<ul><li>foo</li><li>bar</li></ul>" ] [
[ "farkup" farkup render ] with-string-writer [ "farkup" T{ farkup } render ] with-string-writer
] unit-test ] unit-test
[ ] [ { 1 2 3 } "object" set-value ] unit-test [ ] [ { 1 2 3 } "object" set-value ] unit-test

View File

@ -10,9 +10,12 @@ IN: html.components
SYMBOL: values SYMBOL: values
: value ( name -- value ) values get at ; : check-value-name ( name -- name )
dup string? [ "Value name not a string" throw ] unless ;
: set-value ( value name -- ) values get set-at ; : value ( name -- value ) check-value-name values get at ;
: set-value ( value name -- ) check-value-name values get set-at ;
: blank-values ( -- ) H{ } clone values set ; : blank-values ( -- ) H{ } clone values set ;
@ -200,10 +203,20 @@ M: code render*
[ string-lines ] [ drop ] [ mode>> value ] tri* htmlize-lines ; [ string-lines ] [ drop ] [ mode>> value ] tri* htmlize-lines ;
! Farkup component ! Farkup component
SINGLETON: farkup TUPLE: farkup no-follow disable-images ;
: string>boolean ( string -- boolean )
{
{ "true" [ t ] }
{ "false" [ f ] }
} case ;
M: farkup render* M: farkup render*
2drop string-lines "\n" join convert-farkup write ; [
[ no-follow>> [ string>boolean link-no-follow? set ] when* ]
[ disable-images>> [ string>boolean disable-images? set ] when* ] bi
drop string-lines "\n" join convert-farkup write
] with-scope ;
! Inspector component ! Inspector component
SINGLETON: inspector SINGLETON: inspector

View File

@ -98,12 +98,12 @@ CHLOE: if dup if-satisfied? [ process-tag-children ] [ drop ] if ;
CHLOE-SINGLETON: label CHLOE-SINGLETON: label
CHLOE-SINGLETON: link CHLOE-SINGLETON: link
CHLOE-SINGLETON: farkup
CHLOE-SINGLETON: inspector CHLOE-SINGLETON: inspector
CHLOE-SINGLETON: comparison CHLOE-SINGLETON: comparison
CHLOE-SINGLETON: html CHLOE-SINGLETON: html
CHLOE-SINGLETON: hidden CHLOE-SINGLETON: hidden
CHLOE-TUPLE: farkup
CHLOE-TUPLE: field CHLOE-TUPLE: field
CHLOE-TUPLE: textarea CHLOE-TUPLE: textarea
CHLOE-TUPLE: password CHLOE-TUPLE: password

View File

@ -121,12 +121,12 @@ read-response-test-1' 1array [
] unit-test ] unit-test
! Live-fire exercise ! Live-fire exercise
USING: http.server http.server.static furnace.sessions USING: http.server http.server.static furnace.sessions furnace.alloy
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.responses http.server.redirection
http.server.dispatchers ; http.server.dispatchers db.tuples ;
: add-quit-action : add-quit-action
<action> <action>
@ -138,7 +138,7 @@ http.server.dispatchers ;
[ test-db drop delete-file ] ignore-errors [ test-db drop delete-file ] ignore-errors
test-db [ test-db [
init-sessions-table init-furnace-tables
] with-db ] with-db
[ ] [ [ ] [

View File

@ -5,8 +5,6 @@ combinators arrays io.launcher io http.server.static http.server
http accessors sequences strings math.parser fry urls ; http accessors sequences strings math.parser fry urls ;
IN: http.server.cgi IN: http.server.cgi
: post? ( -- ? ) request get method>> "POST" = ;
: cgi-variables ( script-path -- assoc ) : cgi-variables ( script-path -- assoc )
#! This needs some work. #! This needs some work.
[ [
@ -34,7 +32,7 @@ IN: http.server.cgi
request get "user-agent" header "HTTP_USER_AGENT" set request get "user-agent" header "HTTP_USER_AGENT" set
request get "accept" header "HTTP_ACCEPT" set request get "accept" header "HTTP_ACCEPT" set
post? [ post-request? [
request get post-data>> raw>> request get post-data>> raw>>
[ "CONTENT_TYPE" set ] [ "CONTENT_TYPE" set ]
[ length number>string "CONTENT_LENGTH" set ] [ length number>string "CONTENT_LENGTH" set ]
@ -53,7 +51,7 @@ IN: http.server.cgi
"CGI output follows" >>message "CGI output follows" >>message
swap '[ swap '[
, output-stream get swap <cgi-process> <process-stream> [ , output-stream get swap <cgi-process> <process-stream> [
post? [ request get post-data>> raw>> write flush ] when post-request? [ request get post-data>> raw>> write flush ] when
input-stream get swap (stream-copy) input-stream get swap (stream-copy)
] with-stream ] with-stream
] >>body ; ] >>body ;

View File

@ -13,13 +13,15 @@ io.encodings.ascii
io.encodings.binary io.encodings.binary
io.streams.limited io.streams.limited
io.timeouts io.timeouts
fry logging calendar urls fry logging logging.insomniac calendar urls
http http
http.server.responses http.server.responses
html.elements html.elements
html.streams ; html.streams ;
IN: http.server IN: http.server
: post-request? ( -- ? ) request get method>> "POST" = ;
SYMBOL: responder-nesting SYMBOL: responder-nesting
SYMBOL: main-responder SYMBOL: main-responder
@ -76,9 +78,15 @@ main-responder global [ <404> <trivial-responder> or ] change-at
LOG: httpd-hit NOTICE LOG: httpd-hit NOTICE
LOG: httpd-header NOTICE
: log-header ( headers name -- )
tuck header 2array httpd-header ;
: log-request ( request -- ) : log-request ( request -- )
[ method>> ] [ url>> [ host>> ] [ path>> ] bi ] bi [ [ method>> ] [ url>> [ host>> ] [ path>> ] bi ] bi 3array httpd-hit ]
3array httpd-hit ; [ { "user-agent" "x-forwarded-for" } [ log-header ] with each ]
bi ;
: split-path ( string -- path ) : split-path ( string -- path )
"/" split harvest ; "/" split harvest ;
@ -90,13 +98,13 @@ LOG: httpd-hit NOTICE
: 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 ;
: prepare-request ( request -- request ) : prepare-request ( request -- )
[ [
local-address get local-address get
[ secure? "https" "http" ? >>protocol ] [ secure? "https" "http" ? >>protocol ]
[ port>> '[ , or ] change-port ] [ port>> '[ , or ] change-port ]
bi bi
] change-url ; ] change-url drop ;
: valid-request? ( request -- ? ) : valid-request? ( request -- ? )
url>> port>> local-address get port>> = ; url>> port>> local-address get port>> = ;
@ -138,4 +146,7 @@ LOG: httpd-hit NOTICE
: httpd-main ( -- ) : httpd-main ( -- )
8888 httpd ; 8888 httpd ;
: httpd-insomniac ( -- )
"http.server" { httpd-hit } schedule-insomniac ;
MAIN: httpd-main MAIN: httpd-main

View File

@ -64,7 +64,3 @@ HELP: (wait-to-read)
HELP: wait-to-read HELP: wait-to-read
{ $values { "port" input-port } { "eof?" "a boolean" } } { $values { "port" input-port } { "eof?" "a boolean" } }
{ $description "If the port's buffer has unread data, returns immediately, otherwise suspends the current thread until some data is available for reading. If the buffer was empty and no more data could be read, outputs " { $link t } " to indicate end-of-file; otherwise outputs " { $link f } "." } ; { $description "If the port's buffer has unread data, returns immediately, otherwise suspends the current thread until some data is available for reading. If the buffer was empty and no more data could be read, outputs " { $link t } " to indicate end-of-file; otherwise outputs " { $link f } "." } ;
HELP: can-write?
{ $values { "len" "a positive integer" } { "buffer" buffer } { "?" "a boolean" } }
{ $description "Tests if the port's output buffer can accomodate " { $snippet "len" } " bytes. If the buffer is empty, this always outputs " { $link t } ", since in that case the buffer will be grown automatically." } ;

View File

@ -98,11 +98,9 @@ TUPLE: output-port < buffered-port ;
: <output-port> ( handle -- output-port ) : <output-port> ( handle -- output-port )
output-port <buffered-port> ; output-port <buffered-port> ;
: can-write? ( len buffer -- ? )
[ buffer-fill + ] keep buffer-capacity <= ;
: wait-to-write ( len port -- ) : wait-to-write ( len port -- )
tuck buffer>> can-write? [ drop ] [ stream-flush ] if ; tuck buffer>> buffer-capacity <=
[ drop ] [ stream-flush ] if ;
M: output-port stream-write1 M: output-port stream-write1
dup check-disposed dup check-disposed

View File

@ -42,11 +42,9 @@ SYMBOL: log-service
<PRIVATE <PRIVATE
PREDICATE: one-string-array < array
[ length 1 = ] [ [ string? ] all? ] bi and ;
: stack>message ( obj -- inputs>message ) : stack>message ( obj -- inputs>message )
dup one-string-array? [ first ] [ dup array? [ dup length 1 = [ first ] when ] when
dup string? [
[ [
string-limit off string-limit off
1 line-limit set 1 line-limit set
@ -54,7 +52,7 @@ PREDICATE: one-string-array < array
0 margin set 0 margin set
unparse unparse
] with-scope ] with-scope
] if ; ] unless ;
PRIVATE> PRIVATE>

View File

@ -3,13 +3,13 @@
USING: kernel math math.functions ; USING: kernel math math.functions ;
IN: math.quadratic IN: math.quadratic
: monic ( c b a -- c' b' ) tuck / >r / r> ; : monic ( c b a -- c' b' ) tuck [ / ] 2bi@ ;
: discriminant ( c b -- b d ) tuck sq 4 / swap - sqrt ; : discriminant ( c b -- b d ) tuck sq 4 / swap - sqrt ;
: critical ( b d -- -b/2 d ) >r -2 / r> ; : critical ( b d -- -b/2 d ) [ -2 / ] dip ;
: +- ( x y -- x+y x-y ) [ + ] 2keep - ; : +- ( x y -- x+y x-y ) [ + ] [ - ] 2bi ;
: quadratic ( c b a -- alpha beta ) : quadratic ( c b a -- alpha beta )
#! Solve a quadratic equation ax^2 + bx + c = 0 #! Solve a quadratic equation ax^2 + bx + c = 0
@ -17,4 +17,4 @@ IN: math.quadratic
: qeval ( x c b a -- y ) : qeval ( x c b a -- y )
#! Evaluate ax^2 + bx + c #! Evaluate ax^2 + bx + c
>r pick * r> roll sq * + + ; [ pick * ] dip roll sq * + + ;

View File

@ -24,7 +24,7 @@ random namespaces vectors math math.order ;
[ t ] swap [ dup >persistent-vector sequence= ] curry unit-test [ t ] swap [ dup >persistent-vector sequence= ] curry unit-test
] each ] each
[ ] [ 10000 [ drop 16 random-bits ] PV{ } map-as "1" set ] unit-test [ ] [ 10000 [ 16 random-bits ] PV{ } replicate-as "1" set ] unit-test
[ ] [ "1" get >vector "2" set ] unit-test [ ] [ "1" get >vector "2" set ] unit-test
[ t ] [ [ t ] [

View File

@ -80,7 +80,6 @@ IN: sequences.lib.tests
[ ] [ { } 0 firstn ] unit-test [ ] [ { } 0 firstn ] unit-test
[ "a" ] [ { "a" } 1 firstn ] unit-test [ "a" ] [ { "a" } 1 firstn ] unit-test
[ { { 1 1 } { 1 2 } { 2 0 } } ] [ { { 2 0 } { 1 1 } { 1 2 } } dup [ first ] insertion-sort ] unit-test
[ "empty" ] [ { } [ "not empty" ] [ "empty" ] if-seq ] unit-test [ "empty" ] [ { } [ "not empty" ] [ "empty" ] if-seq ] unit-test
[ { 1 } "not empty" ] [ { 1 } [ "not empty" ] [ "empty" ] if-seq ] unit-test [ { 1 } "not empty" ] [ { 1 } [ "not empty" ] [ "empty" ] if-seq ] unit-test

View File

@ -0,0 +1,4 @@
IN: sorting.insertion
USING: sorting.insertion sequences kernel tools.test ;
[ { { 1 1 } { 1 2 } { 2 0 } } ] [ { { 2 0 } { 1 1 } { 1 2 } } dup [ first ] insertion-sort ] unit-test

View File

@ -135,6 +135,8 @@ PRIVATE>
GENERIC: >url ( obj -- url ) GENERIC: >url ( obj -- url )
M: f >url drop <url> ;
M: url >url ; M: url >url ;
M: string >url M: string >url

View File

@ -59,8 +59,6 @@ M: post entity-url
: <post> ( id -- post ) \ post new swap >>id ; : <post> ( id -- post ) \ post new swap >>id ;
: init-posts-table ( -- ) \ post ensure-table ;
TUPLE: comment < entity parent ; TUPLE: comment < entity parent ;
comment "COMMENTS" { comment "COMMENTS" {
@ -78,8 +76,6 @@ M: comment entity-url
swap >>id swap >>id
swap >>parent ; swap >>parent ;
: init-comments-table ( -- ) comment ensure-table ;
: post ( id -- post ) : post ( id -- post )
[ <post> select-tuple ] [ f <comment> select-tuples ] bi [ <post> select-tuple ] [ f <comment> select-tuples ] bi
>>comments ; >>comments ;
@ -120,6 +116,7 @@ M: comment entity-url
: <posts-by-feed-action> ( -- action ) : <posts-by-feed-action> ( -- action )
<feed-action> <feed-action>
"author" >>rest
[ validate-author ] >>init [ validate-author ] >>init
[ "Recent Posts by " "author" value append ] >>title [ "Recent Posts by " "author" value append ] >>title
[ list-posts ] >>entries [ list-posts ] >>entries
@ -127,6 +124,7 @@ M: comment entity-url
: <post-feed-action> ( -- action ) : <post-feed-action> ( -- action )
<feed-action> <feed-action>
"id" >>rest
[ validate-integer-id "id" value post "post" set-value ] >>init [ validate-integer-id "id" value post "post" set-value ] >>init
[ "post" value feed-entry-title ] >>title [ "post" value feed-entry-title ] >>title
[ "post" value entity-url ] >>url [ "post" value entity-url ] >>url

View File

@ -37,7 +37,7 @@
</p> </p>
<p class="posting-body"> <p class="posting-body">
<t:farkup t:name="content" /> <t:farkup t:name="content" t:no-follow="true" t:disable-images="true" />
</p> </p>
<t:button t:action="$blogs/delete-comment" t:for="id,parent" class="link-button link">Delete Comment</t:button> <t:button t:action="$blogs/delete-comment" t:for="id,parent" class="link-button link">Delete Comment</t:button>

View File

@ -2,9 +2,11 @@
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: accessors kernel sequences assocs io.files io.sockets USING: accessors kernel sequences assocs io.files io.sockets
io.server io.server
namespaces db db.sqlite smtp namespaces db db.tuples db.sqlite smtp
logging.insomniac
http.server http.server
http.server.dispatchers http.server.dispatchers
furnace.alloy
furnace.db furnace.db
furnace.asides furnace.asides
furnace.flash furnace.flash
@ -25,24 +27,16 @@ IN: webapps.factor-website
: init-factor-db ( -- ) : init-factor-db ( -- )
test-db [ test-db [
init-users-table init-furnace-tables
init-sessions-table
init-pastes-table {
init-annotations-table post comment
paste annotation
init-blog-table blog posting
init-postings-table todo
short-url
init-todo-table article revision
} ensure-tables
init-articles-table
init-revisions-table
init-postings-table
init-comments-table
init-short-url-table
] with-db ; ] with-db ;
TUPLE: factor-website < dispatcher ; TUPLE: factor-website < dispatcher ;
@ -63,18 +57,18 @@ TUPLE: factor-website < dispatcher ;
allow-edit-profile allow-edit-profile
<boilerplate> <boilerplate>
{ factor-website "page" } >>template { factor-website "page" } >>template
<asides> <flash-scopes> <sessions> test-db <alloy> ;
test-db <db-persistence> ;
: init-factor-website ( -- ) : init-factor-website ( -- )
"factorcode.org" 25 <inet> smtp-server set-global "factorcode.org" 25 <inet> smtp-server set-global
"todo@factorcode.org" lost-password-from set-global "todo@factorcode.org" lost-password-from set-global
"website@factorcode.org" insomniac-sender set-global
"slava@factorcode.org" insomniac-recipients set-global
init-factor-db init-factor-db
<factor-website> main-responder set-global ; <factor-website> main-responder set-global ;
: start-factor-website ( -- ) : start-factor-website ( -- )
test-db start-expiring-sessions test-db start-expiring
test-db start-update-task test-db start-update-task
httpd-insomniac
8812 httpd ; 8812 httpd ;

View File

@ -236,7 +236,3 @@ M: annotation entity-url
<delete-annotation-action> "delete-annotation" add-responder <delete-annotation-action> "delete-annotation" add-responder
<boilerplate> <boilerplate>
{ pastebin "pastebin-common" } >>template ; { pastebin "pastebin-common" } >>template ;
: init-pastes-table ( -- ) \ paste ensure-table ;
: init-annotations-table ( -- ) annotation ensure-table ;

View File

@ -49,10 +49,6 @@ posting "POSTINGS"
{ "date" "DATE" TIMESTAMP +not-null+ } { "date" "DATE" TIMESTAMP +not-null+ }
} define-persistent } define-persistent
: init-blog-table ( -- ) blog ensure-table ;
: init-postings-table ( -- ) posting ensure-table ;
: <blog> ( id -- todo ) : <blog> ( id -- todo )
blog new blog new
swap >>id ; swap >>id ;

View File

@ -28,8 +28,6 @@ todo "TODO"
{ "description" "DESCRIPTION" { VARCHAR 256 } } { "description" "DESCRIPTION" { VARCHAR 256 } }
} define-persistent } define-persistent
: init-todo-table ( -- ) todo ensure-table ;
: <todo> ( id -- todo ) : <todo> ( id -- todo )
todo new todo new
swap >>id swap >>id

View File

@ -16,9 +16,6 @@ short-url "SHORT_URLS" {
{ "url" "URL" TEXT +not-null+ } { "url" "URL" TEXT +not-null+ }
} define-persistent } define-persistent
: init-short-url-table ( -- )
short-url ensure-table ;
: letter-bank ( -- seq ) : letter-bank ( -- seq )
CHAR: a CHAR: z [a,b] CHAR: a CHAR: z [a,b]
CHAR: A CHAR: Z [a,b] CHAR: A CHAR: Z [a,b]

View File

@ -16,7 +16,7 @@
<tr> <tr>
<td> <t:a t:href="$wiki/revision" t:rest="id"><t:label t:name="date" /></t:a> </td> <td> <t:a t:href="$wiki/revision" t:rest="id"><t:label t:name="date" /></t:a> </td>
<td> <t:a t:href="$wiki/user-edits" t:rest="author"><t:label t:name="author" /></t:a> </td> <td> <t:a t:href="$wiki/user-edits" t:rest="author"><t:label t:name="author" /></t:a> </td>
<td> <t:button t:action="rollback" t:for="id" class="link link-button">Rollback</t:button> </td> <td> <t:button t:action="$wiki/rollback" t:for="id" class="link link-button">Rollback</t:button> </td>
</tr> </tr>
</t:bind-each> </t:bind-each>
</table> </table>

View File

@ -39,15 +39,11 @@ TUPLE: article title revision ;
article "ARTICLES" { article "ARTICLES" {
{ "title" "TITLE" { VARCHAR 256 } +not-null+ +user-assigned-id+ } { "title" "TITLE" { VARCHAR 256 } +not-null+ +user-assigned-id+ }
! { "AUTHOR" INTEGER +not-null+ } ! uid
! { "PROTECTED" BOOLEAN +not-null+ }
{ "revision" "REVISION" INTEGER +not-null+ } ! revision id { "revision" "REVISION" INTEGER +not-null+ } ! revision id
} define-persistent } define-persistent
: <article> ( title -- article ) article new swap >>title ; : <article> ( title -- article ) article new swap >>title ;
: init-articles-table ( -- ) article ensure-table ;
TUPLE: revision id title author date content ; TUPLE: revision id title author date content ;
revision "REVISIONS" { revision "REVISIONS" {
@ -71,8 +67,6 @@ M: revision feed-entry-url id>> revision-url ;
: <revision> ( id -- revision ) : <revision> ( id -- revision )
revision new swap >>id ; revision new swap >>id ;
: init-revisions-table ( -- ) revision ensure-table ;
: validate-title ( -- ) : validate-title ( -- )
{ { "title" [ v-one-line ] } } validate-params ; { { "title" [ v-one-line ] } } validate-params ;
@ -115,14 +109,17 @@ M: revision feed-entry-url id>> revision-url ;
{ wiki "view" } >>template ; { wiki "view" } >>template ;
: amend-article ( revision article -- )
swap id>> >>revision update-tuple ;
: add-article ( revision -- )
[ title>> ] [ id>> ] bi article boa insert-tuple ;
: add-revision ( revision -- ) : add-revision ( revision -- )
[ insert-tuple ] [ insert-tuple ]
[ [
dup title>> <article> select-tuple [ dup title>> <article> select-tuple
swap id>> >>revision update-tuple [ amend-article ] [ add-article ] if*
] [
[ title>> ] [ id>> ] bi article boa insert-tuple
] if*
] bi ; ] bi ;
: <edit-article-action> ( -- action ) : <edit-article-action> ( -- action )