Merge branch 'master' of git://factorcode.org/git/factor
commit
2db4547305
|
@ -3,14 +3,14 @@ debugger ;
|
||||||
IN: alien.strings
|
IN: alien.strings
|
||||||
|
|
||||||
HELP: string>alien
|
HELP: string>alien
|
||||||
{ $values { "string" string } { "encoding" "an encoding descriptor" } { "array" byte-array } }
|
{ $values { "string" string } { "encoding" "an encoding descriptor" } { "byte-array" byte-array } }
|
||||||
{ $description "Encodes a string together with a trailing null code point using the given encoding, and stores the resulting bytes in a freshly-allocated byte array." }
|
{ $description "Encodes a string together with a trailing null code point using the given encoding, and stores the resulting bytes in a freshly-allocated byte array." }
|
||||||
{ $errors "Throws an error if the string contains null characters, or characters not representable in the given encoding." } ;
|
{ $errors "Throws an error if the string contains null characters, or characters not representable in the given encoding." } ;
|
||||||
|
|
||||||
{ string>alien alien>string malloc-string } related-words
|
{ string>alien alien>string malloc-string } related-words
|
||||||
|
|
||||||
HELP: alien>string
|
HELP: alien>string
|
||||||
{ $values { "c-ptr" c-ptr } { "encoding" "an encoding descriptor" } { "string" string } }
|
{ $values { "c-ptr" c-ptr } { "encoding" "an encoding descriptor" } { "string/f" "a string or " { $link f } } }
|
||||||
{ $description "Reads a null-terminated C string from the specified address with the given encoding." } ;
|
{ $description "Reads a null-terminated C string from the specified address with the given encoding." } ;
|
||||||
|
|
||||||
HELP: malloc-string
|
HELP: malloc-string
|
||||||
|
|
|
@ -6,7 +6,7 @@ io.streams.byte-array io.streams.memory io.encodings.utf8
|
||||||
io.encodings.utf16 system alien strings cpu.architecture ;
|
io.encodings.utf16 system alien strings cpu.architecture ;
|
||||||
IN: alien.strings
|
IN: alien.strings
|
||||||
|
|
||||||
GENERIC# alien>string 1 ( alien encoding -- string/f )
|
GENERIC# alien>string 1 ( c-ptr encoding -- string/f )
|
||||||
|
|
||||||
M: c-ptr alien>string
|
M: c-ptr alien>string
|
||||||
>r <memory-stream> r> <decoder>
|
>r <memory-stream> r> <decoder>
|
||||||
|
|
|
@ -150,7 +150,7 @@ M: hashtable hashcode*
|
||||||
drop
|
drop
|
||||||
] [
|
] [
|
||||||
dup length 4 <=
|
dup length 4 <=
|
||||||
over keys [ word? ] contains? or
|
over keys [ [ word? ] [ wrapper? ] bi or ] contains? or
|
||||||
[
|
[
|
||||||
linear-case-quot
|
linear-case-quot
|
||||||
] [
|
] [
|
||||||
|
|
|
@ -6,7 +6,7 @@ strings io.styles vectors words system splitting math.parser
|
||||||
classes.tuple continuations continuations.private combinators
|
classes.tuple continuations continuations.private combinators
|
||||||
generic.math io.streams.duplex classes.builtin classes
|
generic.math io.streams.duplex classes.builtin classes
|
||||||
compiler.units generic.standard vocabs threads threads.private
|
compiler.units generic.standard vocabs threads threads.private
|
||||||
init kernel.private libc io.encodings accessors ;
|
init kernel.private libc io.encodings mirrors accessors ;
|
||||||
IN: debugger
|
IN: debugger
|
||||||
|
|
||||||
GENERIC: error. ( error -- )
|
GENERIC: error. ( error -- )
|
||||||
|
@ -289,6 +289,12 @@ M: encode-error summary drop "Character encoding error" ;
|
||||||
|
|
||||||
M: decode-error summary drop "Character decoding error" ;
|
M: decode-error summary drop "Character decoding error" ;
|
||||||
|
|
||||||
|
M: no-such-slot summary drop "No such slot" ;
|
||||||
|
|
||||||
|
M: immutable-slot summary drop "Slot is immutable" ;
|
||||||
|
|
||||||
|
M: bad-create summary drop "Bad parameters to create" ;
|
||||||
|
|
||||||
<PRIVATE
|
<PRIVATE
|
||||||
|
|
||||||
: init-debugger ( -- )
|
: init-debugger ( -- )
|
||||||
|
|
|
@ -37,10 +37,6 @@ HELP: <mirror>
|
||||||
}
|
}
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
HELP: >mirror<
|
|
||||||
{ $values { "mirror" mirror } { "obj" object } { "slots" "a sequence of " { $link slot-spec } " instances" } }
|
|
||||||
{ $description "Pushes the object being viewed in the mirror together with its slots." } ;
|
|
||||||
|
|
||||||
HELP: make-mirror
|
HELP: make-mirror
|
||||||
{ $values { "obj" object } { "assoc" assoc } }
|
{ $values { "obj" object } { "assoc" assoc } }
|
||||||
{ $description "Creates an assoc which reflects the internal structure of the object." } ;
|
{ $description "Creates an assoc which reflects the internal structure of the object." } ;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
USING: mirrors tools.test assocs kernel arrays ;
|
USING: mirrors tools.test assocs kernel arrays accessors ;
|
||||||
IN: mirrors.tests
|
IN: mirrors.tests
|
||||||
|
|
||||||
TUPLE: foo bar baz ;
|
TUPLE: foo bar baz ;
|
||||||
|
@ -14,3 +14,15 @@ C: <foo> foo
|
||||||
[ 3 ] [
|
[ 3 ] [
|
||||||
3 "baz" 1 2 <foo> [ <mirror> set-at ] keep foo-baz
|
3 "baz" 1 2 <foo> [ <mirror> set-at ] keep foo-baz
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
|
[ 3 "hi" 1 2 <foo> <mirror> set-at ] [
|
||||||
|
[ no-such-slot? ]
|
||||||
|
[ name>> "hi" = ]
|
||||||
|
[ object>> foo? ] tri and and
|
||||||
|
] must-fail-with
|
||||||
|
|
||||||
|
[ 3 "numerator" 1/2 <mirror> set-at ] [
|
||||||
|
[ immutable-slot? ]
|
||||||
|
[ name>> "numerator" = ]
|
||||||
|
[ object>> 1/2 = ] tri and and
|
||||||
|
] must-fail-with
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
! See http://factorcode.org/license.txt for BSD license.
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
USING: assocs hashtables kernel sequences generic words
|
USING: assocs hashtables kernel sequences generic words
|
||||||
arrays classes slots slots.private classes.tuple math vectors
|
arrays classes slots slots.private classes.tuple math vectors
|
||||||
quotations sorting prettyprint ;
|
quotations sorting prettyprint accessors ;
|
||||||
IN: mirrors
|
IN: mirrors
|
||||||
|
|
||||||
: all-slots ( class -- slots )
|
: all-slots ( class -- slots )
|
||||||
|
@ -16,33 +16,32 @@ TUPLE: mirror object slots ;
|
||||||
: <mirror> ( object -- mirror )
|
: <mirror> ( object -- mirror )
|
||||||
dup object-slots mirror boa ;
|
dup object-slots mirror boa ;
|
||||||
|
|
||||||
: >mirror< ( mirror -- obj slots )
|
ERROR: no-such-slot object name ;
|
||||||
dup mirror-object swap mirror-slots ;
|
|
||||||
|
|
||||||
: mirror@ ( slot-name mirror -- obj slot-spec )
|
ERROR: immutable-slot object name ;
|
||||||
>mirror< swapd slot-named ;
|
|
||||||
|
|
||||||
M: mirror at*
|
M: mirror at*
|
||||||
mirror@ dup [ slot-spec-offset slot t ] [ 2drop f f ] if ;
|
[ nip object>> ] [ slots>> slot-named ] 2bi
|
||||||
|
dup [ offset>> slot t ] [ 2drop f f ] if ;
|
||||||
|
|
||||||
M: mirror set-at ( val key mirror -- )
|
M: mirror set-at ( val key mirror -- )
|
||||||
mirror@ dup [
|
[ nip object>> ] [ drop ] [ slots>> slot-named ] 2tri dup [
|
||||||
dup slot-spec-writer [
|
dup writer>> [
|
||||||
slot-spec-offset set-slot
|
nip offset>> set-slot
|
||||||
] [
|
] [
|
||||||
"Immutable slot" throw
|
drop immutable-slot
|
||||||
] if
|
] if
|
||||||
] [
|
] [
|
||||||
"No such slot" throw
|
drop no-such-slot
|
||||||
] if ;
|
] if ;
|
||||||
|
|
||||||
M: mirror delete-at ( key mirror -- )
|
M: mirror delete-at ( key mirror -- )
|
||||||
f -rot set-at ;
|
f -rot set-at ;
|
||||||
|
|
||||||
M: mirror >alist ( mirror -- alist )
|
M: mirror >alist ( mirror -- alist )
|
||||||
>mirror<
|
[ slots>> [ name>> ] map ]
|
||||||
[ [ slot-spec-offset slot ] with map ] keep
|
[ [ object>> ] [ slots>> ] bi [ offset>> slot ] with map ] bi
|
||||||
[ slot-spec-name ] map swap zip ;
|
zip ;
|
||||||
|
|
||||||
M: mirror assoc-size mirror-slots length ;
|
M: mirror assoc-size mirror-slots length ;
|
||||||
|
|
||||||
|
@ -50,7 +49,7 @@ INSTANCE: mirror assoc
|
||||||
|
|
||||||
: sort-assoc ( assoc -- alist )
|
: sort-assoc ( assoc -- alist )
|
||||||
>alist
|
>alist
|
||||||
[ dup first unparse-short swap ] { } map>assoc
|
[ [ first unparse-short ] keep ] { } map>assoc
|
||||||
sort-keys values ;
|
sort-keys values ;
|
||||||
|
|
||||||
GENERIC: make-mirror ( obj -- assoc )
|
GENERIC: make-mirror ( obj -- assoc )
|
||||||
|
|
|
@ -60,7 +60,8 @@ sequences.private combinators ;
|
||||||
[ value-literal sequence? ] [ drop f ] if ;
|
[ value-literal sequence? ] [ drop f ] if ;
|
||||||
|
|
||||||
: member-quot ( seq -- newquot )
|
: member-quot ( seq -- newquot )
|
||||||
[ [ t ] ] { } map>assoc [ drop f ] suffix [ nip case ] curry ;
|
[ literalize [ t ] ] { } map>assoc
|
||||||
|
[ drop f ] suffix [ nip case ] curry ;
|
||||||
|
|
||||||
: expand-member ( #call -- )
|
: expand-member ( #call -- )
|
||||||
dup node-in-d peek value-literal member-quot f splice-quot ;
|
dup node-in-d peek value-literal member-quot f splice-quot ;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
USING: arrays compiler.units generic hashtables inference kernel
|
USING: arrays compiler.units generic hashtables inference kernel
|
||||||
kernel.private math optimizer prettyprint sequences sbufs
|
kernel.private math optimizer generator prettyprint sequences
|
||||||
strings tools.test vectors words sequences.private quotations
|
sbufs strings tools.test vectors words sequences.private
|
||||||
optimizer.backend classes classes.algebra inference.dataflow
|
quotations optimizer.backend classes classes.algebra
|
||||||
classes.tuple.private continuations growable optimizer.inlining
|
inference.dataflow classes.tuple.private continuations growable
|
||||||
namespaces hints ;
|
optimizer.inlining namespaces hints ;
|
||||||
IN: optimizer.tests
|
IN: optimizer.tests
|
||||||
|
|
||||||
[ H{ { 1 5 } { 3 4 } { 2 5 } } ] [
|
[ H{ { 1 5 } { 3 4 } { 2 5 } } ] [
|
||||||
|
@ -349,3 +349,10 @@ USE: sequences.private
|
||||||
1 2 3.0 3 counter-example ;
|
1 2 3.0 3 counter-example ;
|
||||||
|
|
||||||
[ 2 4 6.0 0 ] [ counter-example' ] unit-test
|
[ 2 4 6.0 0 ] [ counter-example' ] unit-test
|
||||||
|
|
||||||
|
: member-test { + - * / /i } member? ;
|
||||||
|
|
||||||
|
\ member-test must-infer
|
||||||
|
[ ] [ \ member-test word-dataflow optimize 2drop ] unit-test
|
||||||
|
[ t ] [ \ + member-test ] unit-test
|
||||||
|
[ f ] [ \ append member-test ] unit-test
|
||||||
|
|
|
@ -51,9 +51,11 @@ ARTICLE: "vocabulary-search-errors" "Word lookup errors"
|
||||||
ARTICLE: "vocabulary-search" "Vocabulary search path"
|
ARTICLE: "vocabulary-search" "Vocabulary search path"
|
||||||
"When the parser reads a token, it attempts to look up a word named by that token. The lookup is performed by searching each vocabulary in the search path, in order."
|
"When the parser reads a token, it attempts to look up a word named by that token. The lookup is performed by searching each vocabulary in the search path, in order."
|
||||||
$nl
|
$nl
|
||||||
"For a source file the vocabulary search path starts off with two vocabularies:"
|
"For a source file the vocabulary search path starts off with one vocabulary:"
|
||||||
{ $code "syntax\nscratchpad" }
|
{ $code "syntax" }
|
||||||
"The " { $vocab-link "syntax" } " vocabulary consists of a set of parsing words for reading Factor data and defining new words. The " { $vocab-link "scratchpad" } " vocabulary is the default vocabulary for new word definitions."
|
"The " { $vocab-link "syntax" } " vocabulary consists of a set of parsing words for reading Factor data and defining new words."
|
||||||
|
$nl
|
||||||
|
"In the listener, the " { $vocab-link "scratchpad" } " is the default vocabulary for new word definitions. However, when loading source files, there is no default vocabulary. Defining words before declaring a vocabulary with " { $link POSTPONE: IN: } " results in an error."
|
||||||
$nl
|
$nl
|
||||||
"At the interactive listener, the default search path contains many more vocabularies. Details on the default search path and parser invocation are found in " { $link "parser" } "."
|
"At the interactive listener, the default search path contains many more vocabularies. Details on the default search path and parser invocation are found in " { $link "parser" } "."
|
||||||
$nl
|
$nl
|
||||||
|
@ -294,6 +296,10 @@ HELP: use
|
||||||
HELP: in
|
HELP: in
|
||||||
{ $var-description "A variable holding the name of the current vocabulary for new definitions." } ;
|
{ $var-description "A variable holding the name of the current vocabulary for new definitions." } ;
|
||||||
|
|
||||||
|
HELP: current-vocab
|
||||||
|
{ $values { "str" "a vocabulary" } }
|
||||||
|
{ $description "Returns the vocabulary stored in the " { $link in } " symbol. Throws an error if the current vocabulary is " { $link f } "." } ;
|
||||||
|
|
||||||
HELP: (use+)
|
HELP: (use+)
|
||||||
{ $values { "vocab" "an assoc mapping strings to words" } }
|
{ $values { "vocab" "an assoc mapping strings to words" } }
|
||||||
{ $description "Adds an assoc at the front of the search path." }
|
{ $description "Adds an assoc at the front of the search path." }
|
||||||
|
@ -323,7 +329,7 @@ HELP: set-in
|
||||||
$parsing-note ;
|
$parsing-note ;
|
||||||
|
|
||||||
HELP: create-in
|
HELP: create-in
|
||||||
{ $values { "string" "a word name" } { "word" "a new word" } }
|
{ $values { "str" "a word name" } { "word" "a new word" } }
|
||||||
{ $description "Creates a word in the current vocabulary. Until re-defined, the word throws an error when invoked." }
|
{ $description "Creates a word in the current vocabulary. Until re-defined, the word throws an error when invoked." }
|
||||||
$parsing-note ;
|
$parsing-note ;
|
||||||
|
|
||||||
|
@ -451,7 +457,7 @@ HELP: bootstrap-syntax
|
||||||
|
|
||||||
HELP: with-file-vocabs
|
HELP: with-file-vocabs
|
||||||
{ $values { "quot" quotation } }
|
{ $values { "quot" quotation } }
|
||||||
{ $description "Calls the quotation in a scope with the initial the vocabulary search path for parsing a file. This consists of the " { $snippet "syntax" } " vocabulary together with the " { $snippet "scratchpad" } " vocabulary." } ;
|
{ $description "Calls the quotation in a scope with the initial the vocabulary search path for parsing a file. This consists of just the " { $snippet "syntax" } " vocabulary." } ;
|
||||||
|
|
||||||
HELP: parse-fresh
|
HELP: parse-fresh
|
||||||
{ $values { "lines" "a sequence of strings" } { "quot" quotation } }
|
{ $values { "lines" "a sequence of strings" } { "quot" quotation } }
|
||||||
|
|
|
@ -3,6 +3,7 @@ io.streams.string namespaces classes effects source-files
|
||||||
assocs sequences strings io.files definitions continuations
|
assocs sequences strings io.files definitions continuations
|
||||||
sorting classes.tuple compiler.units debugger vocabs
|
sorting classes.tuple compiler.units debugger vocabs
|
||||||
vocabs.loader accessors ;
|
vocabs.loader accessors ;
|
||||||
|
|
||||||
IN: parser.tests
|
IN: parser.tests
|
||||||
|
|
||||||
[
|
[
|
||||||
|
@ -429,3 +430,5 @@ must-fail-with
|
||||||
[
|
[
|
||||||
"USE: this-better-not-exist" eval
|
"USE: this-better-not-exist" eval
|
||||||
] must-fail
|
] must-fail
|
||||||
|
|
||||||
|
[ ": foo ;" eval ] [ error>> no-current-vocab? ] must-fail-with
|
||||||
|
|
|
@ -233,8 +233,16 @@ PREDICATE: unexpected-eof < unexpected
|
||||||
: parse-tokens ( end -- seq )
|
: parse-tokens ( end -- seq )
|
||||||
100 <vector> swap (parse-tokens) >array ;
|
100 <vector> swap (parse-tokens) >array ;
|
||||||
|
|
||||||
: create-in ( string -- word )
|
ERROR: no-current-vocab ;
|
||||||
in get create dup set-word dup save-location ;
|
|
||||||
|
M: no-current-vocab summary ( obj -- )
|
||||||
|
drop "Current vocabulary is f, use IN:" ;
|
||||||
|
|
||||||
|
: current-vocab ( -- str )
|
||||||
|
in get [ no-current-vocab ] unless* ;
|
||||||
|
|
||||||
|
: create-in ( str -- word )
|
||||||
|
current-vocab create dup set-word dup save-location ;
|
||||||
|
|
||||||
: CREATE ( -- word ) scan create-in ;
|
: CREATE ( -- word ) scan create-in ;
|
||||||
|
|
||||||
|
@ -243,7 +251,7 @@ PREDICATE: unexpected-eof < unexpected
|
||||||
: CREATE-WORD ( -- word ) CREATE dup reset-generic ;
|
: CREATE-WORD ( -- word ) CREATE dup reset-generic ;
|
||||||
|
|
||||||
: create-class-in ( word -- word )
|
: create-class-in ( word -- word )
|
||||||
in get create
|
current-vocab create
|
||||||
dup save-class-location
|
dup save-class-location
|
||||||
dup predicate-word dup set-word save-location ;
|
dup predicate-word dup set-word save-location ;
|
||||||
|
|
||||||
|
@ -440,8 +448,7 @@ SYMBOL: bootstrap-syntax
|
||||||
|
|
||||||
: with-file-vocabs ( quot -- )
|
: with-file-vocabs ( quot -- )
|
||||||
[
|
[
|
||||||
"scratchpad" in set
|
f in set { "syntax" } set-use
|
||||||
{ "syntax" "scratchpad" } set-use
|
|
||||||
bootstrap-syntax get [ use get push ] when*
|
bootstrap-syntax get [ use get push ] when*
|
||||||
call
|
call
|
||||||
] with-scope ; inline
|
] with-scope ; inline
|
||||||
|
|
|
@ -3,7 +3,7 @@ bit-vectors.private combinators ;
|
||||||
IN: bit-vectors
|
IN: bit-vectors
|
||||||
|
|
||||||
ARTICLE: "bit-vectors" "Bit vectors"
|
ARTICLE: "bit-vectors" "Bit vectors"
|
||||||
"A bit vector is a resizable mutable sequence of bits. The literal syntax is covered in " { $link "syntax-bit-vectors" } ". Bit vector words are found in the " { $vocab-link "bit-vectors" } " vocabulary."
|
"A bit vector is a resizable mutable sequence of bits. Bit vector words are found in the " { $vocab-link "bit-vectors" } " vocabulary."
|
||||||
$nl
|
$nl
|
||||||
"Bit vectors form a class:"
|
"Bit vectors form a class:"
|
||||||
{ $subsection bit-vector }
|
{ $subsection bit-vector }
|
||||||
|
@ -19,7 +19,7 @@ $nl
|
||||||
ABOUT: "bit-vectors"
|
ABOUT: "bit-vectors"
|
||||||
|
|
||||||
HELP: bit-vector
|
HELP: bit-vector
|
||||||
{ $description "The class of resizable bit vectors. See " { $link "syntax-bit-vectors" } " for syntax and " { $link "bit-vectors" } " for general information." } ;
|
{ $description "The class of resizable bit vectors. See " { $link "bit-vectors" } " for information." } ;
|
||||||
|
|
||||||
HELP: <bit-vector>
|
HELP: <bit-vector>
|
||||||
{ $values { "n" "a positive integer specifying initial capacity" } { "bit-vector" bit-vector } }
|
{ $values { "n" "a positive integer specifying initial capacity" } { "bit-vector" bit-vector } }
|
||||||
|
|
|
@ -19,7 +19,7 @@ $nl
|
||||||
ABOUT: "byte-vectors"
|
ABOUT: "byte-vectors"
|
||||||
|
|
||||||
HELP: byte-vector
|
HELP: byte-vector
|
||||||
{ $description "The class of resizable byte vectors. See " { $link "syntax-byte-vectors" } " for syntax and " { $link "byte-vectors" } " for general information." } ;
|
{ $description "The class of resizable byte vectors. See " { $link "byte-vectors" } " for information." } ;
|
||||||
|
|
||||||
HELP: <byte-vector>
|
HELP: <byte-vector>
|
||||||
{ $values { "n" "a positive integer specifying initial capacity" } { "byte-vector" byte-vector } }
|
{ $values { "n" "a positive integer specifying initial capacity" } { "byte-vector" byte-vector } }
|
||||||
|
|
|
@ -2,6 +2,10 @@ USING: arrays calendar kernel math sequences tools.test
|
||||||
continuations system ;
|
continuations system ;
|
||||||
IN: calendar.tests
|
IN: calendar.tests
|
||||||
|
|
||||||
|
\ time+ must-infer
|
||||||
|
\ time* must-infer
|
||||||
|
\ time- must-infer
|
||||||
|
|
||||||
[ f ] [ 2004 12 32 0 0 0 instant <timestamp> valid-timestamp? ] unit-test
|
[ f ] [ 2004 12 32 0 0 0 instant <timestamp> valid-timestamp? ] unit-test
|
||||||
[ f ] [ 2004 2 30 0 0 0 instant <timestamp> valid-timestamp? ] unit-test
|
[ f ] [ 2004 2 30 0 0 0 instant <timestamp> valid-timestamp? ] unit-test
|
||||||
[ f ] [ 2003 2 29 0 0 0 instant <timestamp> valid-timestamp? ] unit-test
|
[ f ] [ 2003 2 29 0 0 0 instant <timestamp> valid-timestamp? ] unit-test
|
||||||
|
|
|
@ -211,12 +211,14 @@ M: duration time+
|
||||||
#! Uses average month/year length since dt loses calendar
|
#! Uses average month/year length since dt loses calendar
|
||||||
#! data
|
#! data
|
||||||
0 swap
|
0 swap
|
||||||
[ year>> + ] keep
|
{
|
||||||
[ month>> months-per-year / + ] keep
|
[ year>> + ]
|
||||||
[ day>> days-per-year / + ] keep
|
[ month>> months-per-year / + ]
|
||||||
[ hour>> hours-per-year / + ] keep
|
[ day>> days-per-year / + ]
|
||||||
[ minute>> minutes-per-year / + ] keep
|
[ hour>> hours-per-year / + ]
|
||||||
second>> seconds-per-year / + ;
|
[ minute>> minutes-per-year / + ]
|
||||||
|
[ second>> seconds-per-year / + ]
|
||||||
|
} cleave ;
|
||||||
|
|
||||||
M: duration <=> [ dt>years ] compare ;
|
M: duration <=> [ dt>years ] compare ;
|
||||||
|
|
||||||
|
@ -252,14 +254,21 @@ M: timestamp time-
|
||||||
#! Exact calendar-time difference
|
#! Exact calendar-time difference
|
||||||
(time-) seconds ;
|
(time-) seconds ;
|
||||||
|
|
||||||
|
: time* ( obj1 obj2 -- obj3 )
|
||||||
|
dup real? [ swap ] when
|
||||||
|
dup real? [ * ] [
|
||||||
|
{
|
||||||
|
[ year>> * ]
|
||||||
|
[ month>> * ]
|
||||||
|
[ day>> * ]
|
||||||
|
[ hour>> * ]
|
||||||
|
[ minute>> * ]
|
||||||
|
[ second>> * ]
|
||||||
|
} 2cleave <duration>
|
||||||
|
] if ;
|
||||||
|
|
||||||
: before ( dt -- -dt )
|
: before ( dt -- -dt )
|
||||||
[ year>> neg ] keep
|
-1 time* ;
|
||||||
[ month>> neg ] keep
|
|
||||||
[ day>> neg ] keep
|
|
||||||
[ hour>> neg ] keep
|
|
||||||
[ minute>> neg ] keep
|
|
||||||
second>> neg
|
|
||||||
<duration> ;
|
|
||||||
|
|
||||||
M: duration time-
|
M: duration time-
|
||||||
before time+ ;
|
before time+ ;
|
||||||
|
|
|
@ -1,26 +1,45 @@
|
||||||
USING: calendar.format calendar kernel tools.test
|
USING: calendar.format calendar kernel math tools.test
|
||||||
io.streams.string ;
|
io.streams.string accessors io ;
|
||||||
IN: calendar.format.tests
|
IN: calendar.format.tests
|
||||||
|
|
||||||
[ 0 ] [
|
[ 0 ] [
|
||||||
"Z" [ read-rfc3339-gmt-offset ] with-string-reader
|
"Z" [ read1 read-rfc3339-gmt-offset ] with-string-reader dt>hours
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
[ 1 ] [
|
[ 1 ] [
|
||||||
"+01" [ read-rfc3339-gmt-offset ] with-string-reader
|
"+01" [ read1 read-rfc3339-gmt-offset ] with-string-reader dt>hours
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
[ -1 ] [
|
[ -1 ] [
|
||||||
"-01" [ read-rfc3339-gmt-offset ] with-string-reader
|
"-01" [ read1 read-rfc3339-gmt-offset ] with-string-reader dt>hours
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
[ -1-1/2 ] [
|
[ -1-1/2 ] [
|
||||||
"-01:30" [ read-rfc3339-gmt-offset ] with-string-reader
|
"-01:30" [ read1 read-rfc3339-gmt-offset ] with-string-reader dt>hours
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
[ 1+1/2 ] [
|
[ 1+1/2 ] [
|
||||||
"+01:30" [ read-rfc3339-gmt-offset ] with-string-reader
|
"+01:30" [ read1 read-rfc3339-gmt-offset ] with-string-reader dt>hours
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
[ ] [ now timestamp>rfc3339 drop ] unit-test
|
[ ] [ now timestamp>rfc3339 drop ] unit-test
|
||||||
[ ] [ now timestamp>rfc822 drop ] unit-test
|
[ ] [ now timestamp>rfc822 drop ] unit-test
|
||||||
|
|
||||||
|
[ 8/1000 -4 ] [
|
||||||
|
"2008-04-19T04:56:00.008-04:00" rfc3339>timestamp
|
||||||
|
[ second>> ] [ gmt-offset>> hour>> ] bi
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
[ T{ duration f 0 0 0 0 0 0 } ] [
|
||||||
|
"GMT" parse-rfc822-gmt-offset
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
[ T{ duration f 0 0 0 -5 0 0 } ] [
|
||||||
|
"-0500" parse-rfc822-gmt-offset
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
[ T{ timestamp f 2008 4 22 14 36 12 T{ duration f 0 0 0 0 0 0 } } ] [
|
||||||
|
"Tue, 22 Apr 2008 14:36:12 GMT" rfc822>timestamp
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
[ t ] [ now dup timestamp>rfc822 rfc822>timestamp time- 1 seconds before? ] unit-test
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
USING: math math.parser kernel sequences io calendar
|
USING: math math.parser kernel sequences io calendar
|
||||||
accessors arrays io.streams.string combinators accessors ;
|
accessors arrays io.streams.string splitting
|
||||||
|
combinators accessors debugger ;
|
||||||
IN: calendar.format
|
IN: calendar.format
|
||||||
|
|
||||||
GENERIC: day. ( obj -- )
|
GENERIC: day. ( obj -- )
|
||||||
|
@ -58,11 +59,11 @@ M: timestamp year. ( timestamp -- )
|
||||||
[ hour>> write-00 ] [ minute>> write-00 ] bi ;
|
[ hour>> write-00 ] [ minute>> write-00 ] bi ;
|
||||||
|
|
||||||
: write-gmt-offset ( gmt-offset -- )
|
: write-gmt-offset ( gmt-offset -- )
|
||||||
dup instant <=> {
|
dup instant <=> sgn {
|
||||||
{ [ dup 0 = ] [ 2drop "GMT" write ] }
|
{ 0 [ drop "GMT" write ] }
|
||||||
{ [ dup 0 < ] [ drop "-" write before (write-gmt-offset) ] }
|
{ -1 [ "-" write before (write-gmt-offset) ] }
|
||||||
{ [ dup 0 > ] [ drop "+" write (write-gmt-offset) ] }
|
{ 1 [ "+" write (write-gmt-offset) ] }
|
||||||
} cond ;
|
} case ;
|
||||||
|
|
||||||
: timestamp>rfc822 ( timestamp -- str )
|
: timestamp>rfc822 ( timestamp -- str )
|
||||||
#! RFC822 timestamp format
|
#! RFC822 timestamp format
|
||||||
|
@ -83,20 +84,22 @@ M: timestamp year. ( timestamp -- )
|
||||||
[ minute>> write-00 ] bi ;
|
[ minute>> write-00 ] bi ;
|
||||||
|
|
||||||
: write-rfc3339-gmt-offset ( duration -- )
|
: write-rfc3339-gmt-offset ( duration -- )
|
||||||
dup instant <=> {
|
dup instant <=> sgn {
|
||||||
{ [ dup 0 = ] [ 2drop "Z" write ] }
|
{ 0 [ drop "Z" write ] }
|
||||||
{ [ dup 0 < ] [ drop CHAR: - write1 before (write-rfc3339-gmt-offset) ] }
|
{ -1 [ CHAR: - write1 before (write-rfc3339-gmt-offset) ] }
|
||||||
{ [ dup 0 > ] [ drop CHAR: + write1 (write-rfc3339-gmt-offset) ] }
|
{ 1 [ CHAR: + write1 (write-rfc3339-gmt-offset) ] }
|
||||||
} cond ;
|
} case ;
|
||||||
|
|
||||||
: (timestamp>rfc3339) ( timestamp -- )
|
: (timestamp>rfc3339) ( timestamp -- )
|
||||||
dup year>> number>string write CHAR: - write1
|
{
|
||||||
dup month>> write-00 CHAR: - write1
|
[ year>> number>string write CHAR: - write1 ]
|
||||||
dup day>> write-00 CHAR: T write1
|
[ month>> write-00 CHAR: - write1 ]
|
||||||
dup hour>> write-00 CHAR: : write1
|
[ day>> write-00 CHAR: T write1 ]
|
||||||
dup minute>> write-00 CHAR: : write1
|
[ hour>> write-00 CHAR: : write1 ]
|
||||||
dup second>> >fixnum write-00
|
[ minute>> write-00 CHAR: : write1 ]
|
||||||
gmt-offset>> write-rfc3339-gmt-offset ;
|
[ second>> >fixnum write-00 ]
|
||||||
|
[ gmt-offset>> write-rfc3339-gmt-offset ]
|
||||||
|
} cleave ;
|
||||||
|
|
||||||
: timestamp>rfc3339 ( timestamp -- str )
|
: timestamp>rfc3339 ( timestamp -- str )
|
||||||
[ (timestamp>rfc3339) ] with-string-writer ;
|
[ (timestamp>rfc3339) ] with-string-writer ;
|
||||||
|
@ -106,14 +109,20 @@ M: timestamp year. ( timestamp -- )
|
||||||
|
|
||||||
: read-00 2 read string>number ;
|
: read-00 2 read string>number ;
|
||||||
|
|
||||||
|
: read-000 3 read string>number ;
|
||||||
|
|
||||||
: read-0000 4 read string>number ;
|
: read-0000 4 read string>number ;
|
||||||
|
|
||||||
: read-rfc3339-gmt-offset ( -- n )
|
: signed-gmt-offset ( dt ch -- dt' )
|
||||||
read1 dup CHAR: Z = [ drop 0 ] [
|
{ { CHAR: + [ 1 ] } { CHAR: - [ -1 ] } } case time* ;
|
||||||
{ { CHAR: + [ 1 ] } { CHAR: - [ -1 ] } } case
|
|
||||||
read-00
|
: read-rfc3339-gmt-offset ( ch -- dt )
|
||||||
read1 { { CHAR: : [ read-00 ] } { f [ 0 ] } } case
|
dup CHAR: Z = [ drop instant ] [
|
||||||
60 / + *
|
>r
|
||||||
|
read-00 hours
|
||||||
|
read1 { { CHAR: : [ read-00 ] } { f [ 0 ] } } case minutes
|
||||||
|
time+
|
||||||
|
r> signed-gmt-offset
|
||||||
] if ;
|
] if ;
|
||||||
|
|
||||||
: read-ymd ( -- y m d )
|
: read-ymd ( -- y m d )
|
||||||
|
@ -126,26 +135,61 @@ M: timestamp year. ( timestamp -- )
|
||||||
read-ymd
|
read-ymd
|
||||||
"Tt" expect
|
"Tt" expect
|
||||||
read-hms
|
read-hms
|
||||||
read-rfc3339-gmt-offset ! timezone
|
read1 { { CHAR: . [ read-000 1000 / + read1 ] } [ ] } case
|
||||||
|
read-rfc3339-gmt-offset
|
||||||
<timestamp> ;
|
<timestamp> ;
|
||||||
|
|
||||||
: rfc3339>timestamp ( str -- timestamp )
|
: rfc3339>timestamp ( str -- timestamp )
|
||||||
[ (rfc3339>timestamp) ] with-string-reader ;
|
[ (rfc3339>timestamp) ] with-string-reader ;
|
||||||
|
|
||||||
|
ERROR: invalid-rfc822-date ;
|
||||||
|
|
||||||
|
: check-rfc822-date ( obj/f -- obj ) [ invalid-rfc822-date ] unless* ;
|
||||||
|
|
||||||
|
: read-token ( seps -- token )
|
||||||
|
[ read-until ] keep member? check-rfc822-date drop ;
|
||||||
|
|
||||||
|
: read-sp ( -- token ) " " read-token ;
|
||||||
|
|
||||||
|
: checked-number ( str -- n )
|
||||||
|
string>number check-rfc822-date ;
|
||||||
|
|
||||||
|
: parse-rfc822-gmt-offset ( string -- dt )
|
||||||
|
dup "GMT" = [ drop instant ] [
|
||||||
|
unclip >r
|
||||||
|
2 cut [ string>number ] bi@ [ hours ] [ minutes ] bi* time+
|
||||||
|
r> signed-gmt-offset
|
||||||
|
] if ;
|
||||||
|
|
||||||
|
: (rfc822>timestamp) ( -- timestamp )
|
||||||
|
timestamp new
|
||||||
|
"," read-token day-abbreviations3 member? check-rfc822-date drop
|
||||||
|
read1 CHAR: \s assert=
|
||||||
|
read-sp checked-number >>day
|
||||||
|
read-sp month-abbreviations index check-rfc822-date >>month
|
||||||
|
read-sp checked-number >>year
|
||||||
|
":" read-token checked-number >>hour
|
||||||
|
":" read-token checked-number >>minute
|
||||||
|
" " read-token checked-number >>second
|
||||||
|
readln parse-rfc822-gmt-offset >>gmt-offset ;
|
||||||
|
|
||||||
|
: rfc822>timestamp ( str -- timestamp )
|
||||||
|
[ (rfc822>timestamp) ] with-string-reader ;
|
||||||
|
|
||||||
: (ymdhms>timestamp) ( -- timestamp )
|
: (ymdhms>timestamp) ( -- timestamp )
|
||||||
read-ymd " " expect read-hms 0 <timestamp> ;
|
read-ymd " " expect read-hms instant <timestamp> ;
|
||||||
|
|
||||||
: ymdhms>timestamp ( str -- timestamp )
|
: ymdhms>timestamp ( str -- timestamp )
|
||||||
[ (ymdhms>timestamp) ] with-string-reader ;
|
[ (ymdhms>timestamp) ] with-string-reader ;
|
||||||
|
|
||||||
: (hms>timestamp) ( -- timestamp )
|
: (hms>timestamp) ( -- timestamp )
|
||||||
f f f read-hms f <timestamp> ;
|
f f f read-hms instant <timestamp> ;
|
||||||
|
|
||||||
: hms>timestamp ( str -- timestamp )
|
: hms>timestamp ( str -- timestamp )
|
||||||
[ (hms>timestamp) ] with-string-reader ;
|
[ (hms>timestamp) ] with-string-reader ;
|
||||||
|
|
||||||
: (ymd>timestamp) ( -- timestamp )
|
: (ymd>timestamp) ( -- timestamp )
|
||||||
read-ymd f f f f <timestamp> ;
|
read-ymd f f f instant <timestamp> ;
|
||||||
|
|
||||||
: ymd>timestamp ( str -- timestamp )
|
: ymd>timestamp ( str -- timestamp )
|
||||||
[ (ymd>timestamp) ] with-string-reader ;
|
[ (ymd>timestamp) ] with-string-reader ;
|
||||||
|
|
|
@ -14,7 +14,7 @@ HELP: <column> ( seq n -- column )
|
||||||
{ $description "Outputs a new virtual sequence which presents a fixed column of a matrix represented as a sequence of rows." "The " { $snippet "i" } "th element of a column is the " { $snippet "n" } "th element of the " { $snippet "i" } "th element of" { $snippet "seq" } ". Every element of " { $snippet "seq" } " must be a sequence, and all sequences must have equal length." }
|
{ $description "Outputs a new virtual sequence which presents a fixed column of a matrix represented as a sequence of rows." "The " { $snippet "i" } "th element of a column is the " { $snippet "n" } "th element of the " { $snippet "i" } "th element of" { $snippet "seq" } ". Every element of " { $snippet "seq" } " must be a sequence, and all sequences must have equal length." }
|
||||||
{ $examples
|
{ $examples
|
||||||
{ $example
|
{ $example
|
||||||
"USING: arrays prettyprint sequences ;"
|
"USING: arrays prettyprint columns ;"
|
||||||
"{ { 1 2 3 } { 4 5 6 } { 7 8 9 } } 0 <column> >array ."
|
"{ { 1 2 3 } { 4 5 6 } { 7 8 9 } } 0 <column> >array ."
|
||||||
"{ 1 4 7 }"
|
"{ 1 4 7 }"
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,3 +3,4 @@ USING: tools.test db kernel ;
|
||||||
|
|
||||||
{ 1 0 } [ [ drop ] query-each ] must-infer-as
|
{ 1 0 } [ [ drop ] query-each ] must-infer-as
|
||||||
{ 1 1 } [ [ ] query-map ] must-infer-as
|
{ 1 1 } [ [ ] query-map ] must-infer-as
|
||||||
|
{ 2 0 } [ [ ] with-db ] must-infer-as
|
||||||
|
|
|
@ -131,6 +131,7 @@ M: nonthrowable execute-statement* ( statement type -- )
|
||||||
: with-db ( db seq quot -- )
|
: with-db ( db seq quot -- )
|
||||||
>r make-db db-open db r>
|
>r make-db db-open db r>
|
||||||
[ db get swap [ drop ] swap compose with-disposal ] curry with-variable ;
|
[ db get swap [ drop ] swap compose with-disposal ] curry with-variable ;
|
||||||
|
inline
|
||||||
|
|
||||||
: default-query ( query -- result-set )
|
: default-query ( query -- result-set )
|
||||||
query-results [ [ sql-row ] query-map ] with-disposal ;
|
query-results [ [ sql-row ] query-map ] with-disposal ;
|
||||||
|
|
|
@ -69,6 +69,11 @@ M: postgresql-result-null summary ( obj -- str )
|
||||||
: malloc-byte-array/length
|
: malloc-byte-array/length
|
||||||
[ malloc-byte-array dup free-always ] [ length ] bi ;
|
[ malloc-byte-array dup free-always ] [ length ] bi ;
|
||||||
|
|
||||||
|
: default-param-value
|
||||||
|
number>string* dup [
|
||||||
|
utf8 malloc-string dup free-always
|
||||||
|
] when 0 ;
|
||||||
|
|
||||||
: param-values ( statement -- seq seq2 )
|
: param-values ( statement -- seq seq2 )
|
||||||
[ bind-params>> ] [ in-params>> ] bi
|
[ bind-params>> ] [ in-params>> ] bi
|
||||||
[
|
[
|
||||||
|
@ -77,11 +82,11 @@ M: postgresql-result-null summary ( obj -- str )
|
||||||
dup [ object>bytes malloc-byte-array/length ] [ 0 ] if
|
dup [ object>bytes malloc-byte-array/length ] [ 0 ] if
|
||||||
] }
|
] }
|
||||||
{ BLOB [ dup [ malloc-byte-array/length ] [ 0 ] if ] }
|
{ BLOB [ dup [ malloc-byte-array/length ] [ 0 ] if ] }
|
||||||
[
|
{ DATE [ dup [ timestamp>ymd ] when default-param-value ] }
|
||||||
drop number>string* dup [
|
{ TIME [ dup [ timestamp>hms ] when default-param-value ] }
|
||||||
utf8 malloc-string dup free-always
|
{ DATETIME [ dup [ timestamp>ymdhms ] when default-param-value ] }
|
||||||
] when 0
|
{ TIMESTAMP [ dup [ timestamp>ymdhms ] when default-param-value ] }
|
||||||
]
|
[ drop default-param-value ]
|
||||||
} case 2array
|
} case 2array
|
||||||
] 2map flip dup empty? [
|
] 2map flip dup empty? [
|
||||||
drop f f
|
drop f f
|
||||||
|
|
|
@ -108,6 +108,7 @@ LIBRARY: sqlite
|
||||||
FUNCTION: int sqlite3_open ( char* filename, void* ppDb ) ;
|
FUNCTION: int sqlite3_open ( char* filename, void* ppDb ) ;
|
||||||
FUNCTION: int sqlite3_close ( sqlite3* pDb ) ;
|
FUNCTION: int sqlite3_close ( sqlite3* pDb ) ;
|
||||||
FUNCTION: char* sqlite3_errmsg ( sqlite3* pDb ) ;
|
FUNCTION: char* sqlite3_errmsg ( sqlite3* pDb ) ;
|
||||||
|
FUNCTION: int sqlite3_prepare ( sqlite3* pDb, char* zSql, int nBytes, void* ppStmt, void* pzTail ) ;
|
||||||
FUNCTION: int sqlite3_prepare_v2 ( sqlite3* pDb, char* zSql, int nBytes, void* ppStmt, void* pzTail ) ;
|
FUNCTION: int sqlite3_prepare_v2 ( sqlite3* pDb, char* zSql, int nBytes, void* ppStmt, void* pzTail ) ;
|
||||||
FUNCTION: int sqlite3_finalize ( sqlite3_stmt* pStmt ) ;
|
FUNCTION: int sqlite3_finalize ( sqlite3_stmt* pStmt ) ;
|
||||||
FUNCTION: int sqlite3_reset ( sqlite3_stmt* pStmt ) ;
|
FUNCTION: int sqlite3_reset ( sqlite3_stmt* pStmt ) ;
|
||||||
|
|
|
@ -97,10 +97,10 @@ IN: db.sqlite.lib
|
||||||
{ TEXT [ sqlite-bind-text-by-name ] }
|
{ TEXT [ sqlite-bind-text-by-name ] }
|
||||||
{ VARCHAR [ sqlite-bind-text-by-name ] }
|
{ VARCHAR [ sqlite-bind-text-by-name ] }
|
||||||
{ DOUBLE [ sqlite-bind-double-by-name ] }
|
{ DOUBLE [ sqlite-bind-double-by-name ] }
|
||||||
{ DATE [ sqlite-bind-text-by-name ] }
|
{ DATE [ timestamp>ymd sqlite-bind-text-by-name ] }
|
||||||
{ TIME [ sqlite-bind-text-by-name ] }
|
{ TIME [ timestamp>hms sqlite-bind-text-by-name ] }
|
||||||
{ DATETIME [ sqlite-bind-text-by-name ] }
|
{ DATETIME [ timestamp>ymdhms sqlite-bind-text-by-name ] }
|
||||||
{ TIMESTAMP [ sqlite-bind-text-by-name ] }
|
{ TIMESTAMP [ timestamp>ymdhms sqlite-bind-text-by-name ] }
|
||||||
{ BLOB [ sqlite-bind-blob-by-name ] }
|
{ BLOB [ sqlite-bind-blob-by-name ] }
|
||||||
{ FACTOR-BLOB [
|
{ FACTOR-BLOB [
|
||||||
object>bytes
|
object>bytes
|
||||||
|
|
|
@ -124,7 +124,7 @@ M: sqlite-db create-sql-statement ( class -- statement )
|
||||||
dup type>> lookup-create-type 0%
|
dup type>> lookup-create-type 0%
|
||||||
modifiers 0%
|
modifiers 0%
|
||||||
] interleave ");" 0%
|
] interleave ");" 0%
|
||||||
] query-make dup sql>> . ;
|
] query-make ;
|
||||||
|
|
||||||
M: sqlite-db drop-sql-statement ( class -- statement )
|
M: sqlite-db drop-sql-statement ( class -- statement )
|
||||||
[ "drop table " 0% 0% ";" 0% drop ] query-make ;
|
[ "drop table " 0% 0% ";" 0% drop ] query-make ;
|
||||||
|
|
|
@ -40,7 +40,7 @@ SYMBOL: person4
|
||||||
|
|
||||||
[ 1 ] [ person1 get person-the-id ] unit-test
|
[ 1 ] [ person1 get person-the-id ] unit-test
|
||||||
|
|
||||||
200 person1 get set-person-the-number
|
[ ] [ 200 person1 get set-person-the-number ] unit-test
|
||||||
|
|
||||||
[ ] [ person1 get update-tuple ] unit-test
|
[ ] [ person1 get update-tuple ] unit-test
|
||||||
|
|
||||||
|
@ -80,9 +80,9 @@ SYMBOL: person4
|
||||||
"teddy"
|
"teddy"
|
||||||
10
|
10
|
||||||
3.14
|
3.14
|
||||||
T{ timestamp f 2008 3 5 16 24 11 0 }
|
T{ timestamp f 2008 3 5 16 24 11 T{ duration f 0 0 0 0 0 0 } }
|
||||||
T{ timestamp f 2008 11 22 f f f f }
|
T{ timestamp f 2008 11 22 f f f T{ duration f 0 0 0 0 0 0 } }
|
||||||
T{ timestamp f f f f 12 34 56 f }
|
T{ timestamp f f f f 12 34 56 T{ duration f 0 0 0 0 0 0 } }
|
||||||
B{ 115 116 111 114 101 105 110 97 98 108 111 98 }
|
B{ 115 116 111 114 101 105 110 97 98 108 111 98 }
|
||||||
}
|
}
|
||||||
] [ T{ person f 3 } select-tuple ] unit-test
|
] [ T{ person f 3 } select-tuple ] unit-test
|
||||||
|
@ -96,9 +96,9 @@ SYMBOL: person4
|
||||||
"eddie"
|
"eddie"
|
||||||
10
|
10
|
||||||
3.14
|
3.14
|
||||||
T{ timestamp f 2008 3 5 16 24 11 0 }
|
T{ timestamp f 2008 3 5 16 24 11 T{ duration f 0 0 0 0 0 0 } }
|
||||||
T{ timestamp f 2008 11 22 f f f f }
|
T{ timestamp f 2008 11 22 f f f T{ duration f 0 0 0 0 0 0 } }
|
||||||
T{ timestamp f f f f 12 34 56 f }
|
T{ timestamp f f f f 12 34 56 T{ duration f 0 0 0 0 0 0 } }
|
||||||
f
|
f
|
||||||
H{ { 1 2 } { 3 4 } { 5 "lol" } }
|
H{ { 1 2 } { 3 4 } { 5 "lol" } }
|
||||||
}
|
}
|
||||||
|
@ -121,8 +121,16 @@ SYMBOL: person4
|
||||||
} define-persistent
|
} define-persistent
|
||||||
"billy" 10 3.14 f f f f f <person> person1 set
|
"billy" 10 3.14 f f f f f <person> person1 set
|
||||||
"johnny" 10 3.14 f f f f f <person> person2 set
|
"johnny" 10 3.14 f f f f f <person> person2 set
|
||||||
"teddy" 10 3.14 "2008-03-05 16:24:11" "2008-11-22" "12:34:56" B{ 115 116 111 114 101 105 110 97 98 108 111 98 } f <person> person3 set
|
"teddy" 10 3.14
|
||||||
"eddie" 10 3.14 "2008-03-05 16:24:11" "2008-11-22" "12:34:56" f H{ { 1 2 } { 3 4 } { 5 "lol" } } <person> person4 set ;
|
T{ timestamp f 2008 3 5 16 24 11 T{ duration f 0 0 0 0 0 0 } }
|
||||||
|
T{ timestamp f 2008 11 22 0 0 0 T{ duration f 0 0 0 0 0 0 } }
|
||||||
|
T{ timestamp f f f f 12 34 56 T{ duration f 0 0 0 0 0 0 } }
|
||||||
|
B{ 115 116 111 114 101 105 110 97 98 108 111 98 } f <person> person3 set
|
||||||
|
"eddie" 10 3.14
|
||||||
|
T{ timestamp f 2008 3 5 16 24 11 T{ duration f 0 0 0 0 0 0 } }
|
||||||
|
T{ timestamp f 2008 11 22 0 0 0 T{ duration f 0 0 0 0 0 0 } }
|
||||||
|
T{ timestamp f f f f 12 34 56 T{ duration f 0 0 0 0 0 0 } }
|
||||||
|
f H{ { 1 2 } { 3 4 } { 5 "lol" } } <person> person4 set ;
|
||||||
|
|
||||||
: assigned-person-schema ( -- )
|
: assigned-person-schema ( -- )
|
||||||
person "PERSON"
|
person "PERSON"
|
||||||
|
@ -139,8 +147,17 @@ SYMBOL: person4
|
||||||
} define-persistent
|
} define-persistent
|
||||||
1 "billy" 10 3.14 f f f f f <assigned-person> person1 set
|
1 "billy" 10 3.14 f f f f f <assigned-person> person1 set
|
||||||
2 "johnny" 10 3.14 f f f f f <assigned-person> person2 set
|
2 "johnny" 10 3.14 f f f f f <assigned-person> person2 set
|
||||||
3 "teddy" 10 3.14 "2008-03-05 16:24:11" "2008-11-22" "12:34:56" B{ 115 116 111 114 101 105 110 97 98 108 111 98 } f <assigned-person> person3 set
|
3 "teddy" 10 3.14
|
||||||
4 "eddie" 10 3.14 "2008-03-05 16:24:11" "2008-11-22" "12:34:56" f H{ { 1 2 } { 3 4 } { 5 "lol" } } <assigned-person> person4 set ;
|
T{ timestamp f 2008 3 5 16 24 11 T{ duration f 0 0 0 0 0 0 } }
|
||||||
|
T{ timestamp f 2008 11 22 0 0 0 T{ duration f 0 0 0 0 0 0 } }
|
||||||
|
T{ timestamp f f f f 12 34 56 T{ duration f 0 0 0 0 0 0 } }
|
||||||
|
B{ 115 116 111 114 101 105 110 97 98 108 111 98 }
|
||||||
|
f <assigned-person> person3 set
|
||||||
|
4 "eddie" 10 3.14
|
||||||
|
T{ timestamp f 2008 3 5 16 24 11 T{ duration f 0 0 0 0 0 0 } }
|
||||||
|
T{ timestamp f 2008 11 22 0 0 0 T{ duration f 0 0 0 0 0 0 } }
|
||||||
|
T{ timestamp f f f f 12 34 56 T{ duration f 0 0 0 0 0 0 } }
|
||||||
|
f H{ { 1 2 } { 3 4 } { 5 "lol" } } <assigned-person> person4 set ;
|
||||||
|
|
||||||
TUPLE: paste n summary author channel mode contents timestamp annotations ;
|
TUPLE: paste n summary author channel mode contents timestamp annotations ;
|
||||||
TUPLE: annotation n paste-id summary author mode contents ;
|
TUPLE: annotation n paste-id summary author mode contents ;
|
||||||
|
@ -363,3 +380,6 @@ TUPLE: does-not-persist ;
|
||||||
\ delete-tuple must-infer
|
\ delete-tuple must-infer
|
||||||
\ select-tuple must-infer
|
\ select-tuple must-infer
|
||||||
\ define-persistent must-infer
|
\ define-persistent must-infer
|
||||||
|
\ ensure-table must-infer
|
||||||
|
\ create-table must-infer
|
||||||
|
\ drop-table must-infer
|
||||||
|
|
|
@ -105,7 +105,7 @@ M: retryable execute-statement* ( statement type -- )
|
||||||
[ with-disposal ] curry each
|
[ with-disposal ] curry each
|
||||||
] [
|
] [
|
||||||
with-disposal
|
with-disposal
|
||||||
] if ;
|
] if ; inline
|
||||||
|
|
||||||
: create-table ( class -- )
|
: create-table ( class -- )
|
||||||
create-sql-statement [ execute-statement ] with-disposals ;
|
create-sql-statement [ execute-statement ] with-disposals ;
|
||||||
|
|
|
@ -3,7 +3,7 @@ float-vectors.private combinators ;
|
||||||
IN: float-vectors
|
IN: float-vectors
|
||||||
|
|
||||||
ARTICLE: "float-vectors" "Float vectors"
|
ARTICLE: "float-vectors" "Float vectors"
|
||||||
"A float vector is a resizable mutable sequence of unsigned floats. The literal syntax is covered in " { $link "syntax-float-vectors" } ". Float vector words are found in the " { $vocab-link "float-vectors" } " vocabulary."
|
"A float vector is a resizable mutable sequence of unsigned floats. Float vector words are found in the " { $vocab-link "float-vectors" } " vocabulary."
|
||||||
$nl
|
$nl
|
||||||
"Float vectors form a class:"
|
"Float vectors form a class:"
|
||||||
{ $subsection float-vector }
|
{ $subsection float-vector }
|
||||||
|
@ -19,7 +19,7 @@ $nl
|
||||||
ABOUT: "float-vectors"
|
ABOUT: "float-vectors"
|
||||||
|
|
||||||
HELP: float-vector
|
HELP: float-vector
|
||||||
{ $description "The class of resizable float vectors. See " { $link "syntax-float-vectors" } " for syntax and " { $link "float-vectors" } " for general information." } ;
|
{ $description "The class of resizable float vectors. See " { $link "float-vectors" } " for information." } ;
|
||||||
|
|
||||||
HELP: <float-vector>
|
HELP: <float-vector>
|
||||||
{ $values { "n" "a positive integer specifying initial capacity" } { "float-vector" float-vector } }
|
{ $values { "n" "a positive integer specifying initial capacity" } { "float-vector" float-vector } }
|
||||||
|
|
|
@ -44,3 +44,7 @@ sequences ;
|
||||||
: funny-dip '[ @ _ ] call ; inline
|
: funny-dip '[ @ _ ] call ; inline
|
||||||
|
|
||||||
[ "hi" 3 ] [ "h" "i" 3 [ append ] funny-dip ] unit-test
|
[ "hi" 3 ] [ "h" "i" 3 [ append ] funny-dip ] unit-test
|
||||||
|
|
||||||
|
[ { 1 2 3 } ] [
|
||||||
|
3 1 '[ , [ , + ] map ] call
|
||||||
|
] unit-test
|
||||||
|
|
|
@ -9,41 +9,54 @@ IN: fry
|
||||||
: @ "Only valid inside a fry" throw ;
|
: @ "Only valid inside a fry" throw ;
|
||||||
: _ "Only valid inside a fry" throw ;
|
: _ "Only valid inside a fry" throw ;
|
||||||
|
|
||||||
DEFER: (fry)
|
DEFER: (shallow-fry)
|
||||||
|
|
||||||
: ((fry)) ( accum quot adder -- result )
|
: ((shallow-fry)) ( accum quot adder -- result )
|
||||||
>r [ ] swap (fry) r>
|
>r [ ] swap (shallow-fry) r>
|
||||||
append swap dup empty? [ drop ] [
|
append swap dup empty? [ drop ] [
|
||||||
[ swap compose ] curry append
|
[ swap compose ] curry append
|
||||||
] if ; inline
|
] if ; inline
|
||||||
|
|
||||||
: (fry) ( accum quot -- result )
|
: (shallow-fry) ( accum quot -- result )
|
||||||
dup empty? [
|
dup empty? [
|
||||||
drop 1quotation
|
drop 1quotation
|
||||||
] [
|
] [
|
||||||
unclip {
|
unclip {
|
||||||
{ \ , [ [ curry ] ((fry)) ] }
|
{ \ , [ [ curry ] ((shallow-fry)) ] }
|
||||||
{ \ @ [ [ compose ] ((fry)) ] }
|
{ \ @ [ [ compose ] ((shallow-fry)) ] }
|
||||||
|
|
||||||
! to avoid confusion, remove if fry goes core
|
! to avoid confusion, remove if fry goes core
|
||||||
{ \ namespaces:, [ [ curry ] ((fry)) ] }
|
{ \ namespaces:, [ [ curry ] ((shallow-fry)) ] }
|
||||||
|
|
||||||
[ swap >r suffix r> (fry) ]
|
[ swap >r suffix r> (shallow-fry) ]
|
||||||
} case
|
} case
|
||||||
] if ;
|
] if ;
|
||||||
|
|
||||||
: trivial-fry ( quot -- quot' ) [ ] swap (fry) ;
|
: shallow-fry ( quot -- quot' ) [ ] swap (shallow-fry) ;
|
||||||
|
|
||||||
: fry ( quot -- quot' )
|
: deep-fry ( quot -- quot' )
|
||||||
{ _ } last-split1 [
|
{ _ } last-split1 [
|
||||||
[
|
[
|
||||||
trivial-fry %
|
shallow-fry %
|
||||||
[ >r ] %
|
[ >r ] %
|
||||||
fry %
|
deep-fry %
|
||||||
[ [ dip ] curry r> compose ] %
|
[ [ dip ] curry r> compose ] %
|
||||||
] [ ] make
|
] [ ] make
|
||||||
] [
|
] [
|
||||||
trivial-fry
|
shallow-fry
|
||||||
] if* ;
|
] if* ;
|
||||||
|
|
||||||
|
: fry ( quot -- quot' )
|
||||||
|
[
|
||||||
|
[
|
||||||
|
dup callable? [
|
||||||
|
[
|
||||||
|
[ { , namespaces:, @ } member? ] subset length
|
||||||
|
\ , <repetition> %
|
||||||
|
]
|
||||||
|
[ deep-fry % ] bi
|
||||||
|
] [ namespaces:, ] if
|
||||||
|
] each
|
||||||
|
] [ ] make deep-fry ;
|
||||||
|
|
||||||
: '[ \ ] parse-until fry over push-all ; parsing
|
: '[ \ ] parse-until fry over push-all ; parsing
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
USING: alien alien.c-types kernel libc math namespaces
|
USING: alien alien.c-types kernel libc math namespaces
|
||||||
windows windows.kernel32 windows.advapi32
|
windows windows.kernel32 windows.advapi32
|
||||||
words combinators vocabs.loader hardware-info.backend
|
words combinators vocabs.loader hardware-info.backend
|
||||||
system ;
|
system alien.strings ;
|
||||||
IN: hardware-info.windows
|
IN: hardware-info.windows
|
||||||
|
|
||||||
: system-info ( -- SYSTEM_INFO )
|
: system-info ( -- SYSTEM_INFO )
|
||||||
|
|
|
@ -6,9 +6,9 @@ tuple-syntax namespaces ;
|
||||||
[ "/" "localhost" 8888 ] [ "http://localhost:8888" parse-url ] unit-test
|
[ "/" "localhost" 8888 ] [ "http://localhost:8888" parse-url ] unit-test
|
||||||
|
|
||||||
[ "foo.txt" ] [ "http://www.paulgraham.com/foo.txt" download-name ] unit-test
|
[ "foo.txt" ] [ "http://www.paulgraham.com/foo.txt" download-name ] unit-test
|
||||||
[ "foo.txt" ] [ "http://www.arcsucks.com/foo.txt?xxx" download-name ] unit-test
|
[ "foo.txt" ] [ "http://www.arc.com/foo.txt?xxx" download-name ] unit-test
|
||||||
[ "foo.txt" ] [ "http://www.arcsucks.com/foo.txt/" download-name ] unit-test
|
[ "foo.txt" ] [ "http://www.arc.com/foo.txt/" download-name ] unit-test
|
||||||
[ "www.arcsucks.com" ] [ "http://www.arcsucks.com////" download-name ] unit-test
|
[ "www.arc.com" ] [ "http://www.arc.com////" download-name ] unit-test
|
||||||
|
|
||||||
[
|
[
|
||||||
TUPLE{ request
|
TUPLE{ request
|
||||||
|
@ -18,7 +18,7 @@ tuple-syntax namespaces ;
|
||||||
port: 80
|
port: 80
|
||||||
version: "1.1"
|
version: "1.1"
|
||||||
cookies: V{ }
|
cookies: V{ }
|
||||||
header: H{ }
|
header: H{ { "connection" "close" } }
|
||||||
}
|
}
|
||||||
] [
|
] [
|
||||||
[
|
[
|
||||||
|
|
|
@ -3,9 +3,17 @@
|
||||||
USING: assocs http kernel math math.parser namespaces sequences
|
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
|
splitting calendar continuations accessors vectors
|
||||||
io.encodings.8-bit io.encodings.binary fry ;
|
io.encodings.8-bit io.encodings.binary fry debugger inspector ;
|
||||||
IN: http.client
|
IN: http.client
|
||||||
|
|
||||||
|
: max-redirects 10 ;
|
||||||
|
|
||||||
|
ERROR: too-many-redirects ;
|
||||||
|
|
||||||
|
M: too-many-redirects summary
|
||||||
|
drop
|
||||||
|
[ "Redirection limit of " % max-redirects # " exceeded" % ] "" make ;
|
||||||
|
|
||||||
DEFER: http-request
|
DEFER: http-request
|
||||||
|
|
||||||
<PRIVATE
|
<PRIVATE
|
||||||
|
@ -29,22 +37,29 @@ DEFER: http-request
|
||||||
: relative-redirect ( path -- request )
|
: relative-redirect ( path -- request )
|
||||||
request get swap store-path ;
|
request get swap store-path ;
|
||||||
|
|
||||||
|
SYMBOL: redirects
|
||||||
|
|
||||||
|
: absolute-url? ( url -- ? )
|
||||||
|
[ "http://" head? ] [ "https://" head? ] bi or ;
|
||||||
|
|
||||||
: do-redirect ( response -- response stream )
|
: do-redirect ( response -- response stream )
|
||||||
dup response-code 300 399 between? [
|
dup response-code 300 399 between? [
|
||||||
stdio get dispose
|
stdio get dispose
|
||||||
|
redirects inc
|
||||||
|
redirects get max-redirects < [
|
||||||
header>> "location" swap at
|
header>> "location" swap at
|
||||||
dup "http://" head? [
|
dup absolute-url? [
|
||||||
absolute-redirect
|
absolute-redirect
|
||||||
] [
|
] [
|
||||||
relative-redirect
|
relative-redirect
|
||||||
] if "GET" >>method http-request
|
] if "GET" >>method http-request
|
||||||
|
] [
|
||||||
|
too-many-redirects
|
||||||
|
] if
|
||||||
] [
|
] [
|
||||||
stdio get
|
stdio get
|
||||||
] if ;
|
] if ;
|
||||||
|
|
||||||
: request-addr ( request -- addr )
|
|
||||||
dup host>> swap port>> <inet> ;
|
|
||||||
|
|
||||||
: close-on-error ( stream quot -- )
|
: close-on-error ( stream quot -- )
|
||||||
'[ , with-stream* ] [ ] pick '[ , dispose ] cleanup ; inline
|
'[ , with-stream* ] [ ] pick '[ , dispose ] cleanup ; inline
|
||||||
|
|
||||||
|
@ -61,28 +76,55 @@ PRIVATE>
|
||||||
] close-on-error
|
] close-on-error
|
||||||
] with-variable ;
|
] with-variable ;
|
||||||
|
|
||||||
|
: read-chunks ( -- )
|
||||||
|
read-crlf ";" split1 drop hex> dup { f 0 } member?
|
||||||
|
[ drop ] [ read % read-crlf "" assert= read-chunks ] if ;
|
||||||
|
|
||||||
|
: do-chunked-encoding ( response stream -- response stream/string )
|
||||||
|
over "transfer-encoding" header "chunked" = [
|
||||||
|
[ [ read-chunks ] "" make ] with-stream
|
||||||
|
] when ;
|
||||||
|
|
||||||
: <get-request> ( url -- request )
|
: <get-request> ( url -- request )
|
||||||
<request> request-with-url "GET" >>method ;
|
<request> request-with-url "GET" >>method ;
|
||||||
|
|
||||||
: http-get-stream ( url -- response stream )
|
: string-or-contents ( stream/string -- string )
|
||||||
<get-request> http-request ;
|
dup string? [ contents ] unless ;
|
||||||
|
|
||||||
|
: http-get-stream ( url -- response stream/string )
|
||||||
|
<get-request> http-request do-chunked-encoding ;
|
||||||
|
|
||||||
: success? ( code -- ? ) 200 = ;
|
: success? ( code -- ? ) 200 = ;
|
||||||
|
|
||||||
: check-response ( response -- )
|
ERROR: download-failed response body ;
|
||||||
code>> success?
|
|
||||||
[ "HTTP download failed" throw ] unless ;
|
M: download-failed error.
|
||||||
|
"HTTP download failed:" print nl
|
||||||
|
[
|
||||||
|
response>>
|
||||||
|
write-response-code
|
||||||
|
write-response-message nl
|
||||||
|
drop
|
||||||
|
]
|
||||||
|
[ body>> write ] bi ;
|
||||||
|
|
||||||
|
: check-response ( response string -- string )
|
||||||
|
over code>> success? [ nip ] [ download-failed ] if ;
|
||||||
|
|
||||||
: http-get ( url -- string )
|
: http-get ( url -- string )
|
||||||
http-get-stream contents swap check-response ;
|
http-get-stream string-or-contents check-response ;
|
||||||
|
|
||||||
: download-name ( url -- name )
|
: download-name ( url -- name )
|
||||||
file-name "?" split1 drop "/" ?tail drop ;
|
file-name "?" split1 drop "/" ?tail drop ;
|
||||||
|
|
||||||
: download-to ( url file -- )
|
: download-to ( url file -- )
|
||||||
#! Downloads the contents of a URL to a file.
|
#! Downloads the contents of a URL to a file.
|
||||||
swap http-get-stream swap check-response
|
swap http-get-stream check-response
|
||||||
[ swap latin1 <file-writer> stream-copy ] with-disposal ;
|
dup string? [
|
||||||
|
latin1 [ write ] with-file-writer
|
||||||
|
] [
|
||||||
|
[ swap latin1 <file-writer> stream-copy ] with-disposal
|
||||||
|
] if ;
|
||||||
|
|
||||||
: download ( url -- )
|
: download ( url -- )
|
||||||
dup download-name download-to ;
|
dup download-name download-to ;
|
||||||
|
@ -95,4 +137,4 @@ PRIVATE>
|
||||||
swap >>post-data-type ;
|
swap >>post-data-type ;
|
||||||
|
|
||||||
: http-post ( content-type content url -- response string )
|
: http-post ( content-type content url -- response string )
|
||||||
<post-request> http-request contents ;
|
<post-request> http-request do-chunked-encoding string-or-contents ;
|
||||||
|
|
|
@ -24,6 +24,8 @@ IN: http.tests
|
||||||
[ "/bar" ] [ "http://foo.com/bar" url>path ] unit-test
|
[ "/bar" ] [ "http://foo.com/bar" url>path ] unit-test
|
||||||
[ "/bar" ] [ "/bar" url>path ] unit-test
|
[ "/bar" ] [ "/bar" url>path ] unit-test
|
||||||
|
|
||||||
|
: lf>crlf "\n" split "\r\n" join ;
|
||||||
|
|
||||||
STRING: read-request-test-1
|
STRING: read-request-test-1
|
||||||
GET http://foo/bar HTTP/1.1
|
GET http://foo/bar HTTP/1.1
|
||||||
Some-Header: 1
|
Some-Header: 1
|
||||||
|
@ -45,7 +47,7 @@ blah
|
||||||
cookies: V{ }
|
cookies: V{ }
|
||||||
}
|
}
|
||||||
] [
|
] [
|
||||||
read-request-test-1 [
|
read-request-test-1 lf>crlf [
|
||||||
read-request
|
read-request
|
||||||
] with-string-reader
|
] with-string-reader
|
||||||
] unit-test
|
] unit-test
|
||||||
|
@ -59,7 +61,7 @@ blah
|
||||||
;
|
;
|
||||||
|
|
||||||
read-request-test-1' 1array [
|
read-request-test-1' 1array [
|
||||||
read-request-test-1
|
read-request-test-1 lf>crlf
|
||||||
[ read-request ] with-string-reader
|
[ read-request ] with-string-reader
|
||||||
[ write-request ] with-string-writer
|
[ write-request ] with-string-writer
|
||||||
! normalize crlf
|
! normalize crlf
|
||||||
|
@ -69,6 +71,7 @@ read-request-test-1' 1array [
|
||||||
STRING: read-request-test-2
|
STRING: read-request-test-2
|
||||||
HEAD http://foo/bar HTTP/1.1
|
HEAD http://foo/bar HTTP/1.1
|
||||||
Host: www.sex.com
|
Host: www.sex.com
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|
||||||
[
|
[
|
||||||
|
@ -83,7 +86,7 @@ Host: www.sex.com
|
||||||
cookies: V{ }
|
cookies: V{ }
|
||||||
}
|
}
|
||||||
] [
|
] [
|
||||||
read-request-test-2 [
|
read-request-test-2 lf>crlf [
|
||||||
read-request
|
read-request
|
||||||
] with-string-reader
|
] with-string-reader
|
||||||
] unit-test
|
] unit-test
|
||||||
|
@ -104,7 +107,7 @@ blah
|
||||||
cookies: V{ }
|
cookies: V{ }
|
||||||
}
|
}
|
||||||
] [
|
] [
|
||||||
read-response-test-1
|
read-response-test-1 lf>crlf
|
||||||
[ read-response ] with-string-reader
|
[ read-response ] with-string-reader
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
|
@ -117,7 +120,7 @@ content-type: text/html
|
||||||
;
|
;
|
||||||
|
|
||||||
read-response-test-1' 1array [
|
read-response-test-1' 1array [
|
||||||
read-response-test-1
|
read-response-test-1 lf>crlf
|
||||||
[ read-response ] with-string-reader
|
[ read-response ] with-string-reader
|
||||||
[ write-response ] with-string-writer
|
[ write-response ] with-string-writer
|
||||||
! normalize crlf
|
! normalize crlf
|
||||||
|
@ -130,19 +133,26 @@ read-response-test-1' 1array [
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
! Live-fire exercise
|
! Live-fire exercise
|
||||||
USING: http.server http.server.static http.server.actions
|
USING: http.server http.server.static http.server.sessions
|
||||||
http.client io.server io.files io accessors namespaces threads
|
http.server.actions http.server.auth.login http.client
|
||||||
|
io.server io.files io accessors namespaces threads
|
||||||
io.encodings.ascii ;
|
io.encodings.ascii ;
|
||||||
|
|
||||||
|
: add-quit-action
|
||||||
|
<action>
|
||||||
|
[ stop-server "text/html" <content> [ "Goodbye" write ] >>body ] >>display
|
||||||
|
"quit" add-responder ;
|
||||||
|
|
||||||
[ ] [
|
[ ] [
|
||||||
[
|
[
|
||||||
<dispatcher>
|
<dispatcher>
|
||||||
<action>
|
add-quit-action
|
||||||
[ stop-server "text/html" <content> [ "Goodbye" write ] >>body ] >>display
|
|
||||||
"quit" add-responder
|
|
||||||
<dispatcher>
|
<dispatcher>
|
||||||
"extra/http/test" resource-path <static> >>default
|
"extra/http/test" resource-path <static> >>default
|
||||||
"nested" add-responder
|
"nested" add-responder
|
||||||
|
<action>
|
||||||
|
[ "redirect-loop" f <permanent-redirect> ] >>display
|
||||||
|
"redirect-loop" add-responder
|
||||||
main-responder set
|
main-responder set
|
||||||
|
|
||||||
[ 1237 httpd ] "HTTPD test" spawn drop
|
[ 1237 httpd ] "HTTPD test" spawn drop
|
||||||
|
@ -159,11 +169,62 @@ io.encodings.ascii ;
|
||||||
"localhost" 1237 <inet> ascii <client> [
|
"localhost" 1237 <inet> ascii <client> [
|
||||||
"GET nested HTTP/1.0\r\n" write flush
|
"GET nested HTTP/1.0\r\n" write flush
|
||||||
"\r\n" write flush
|
"\r\n" write flush
|
||||||
readln drop
|
read-crlf drop
|
||||||
read-header USE: prettyprint
|
read-header
|
||||||
] with-stream dup . "location" swap at "/" head?
|
] with-stream "location" swap at "/" head?
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
|
[ "http://localhost:1237/redirect-loop" http-get ]
|
||||||
|
[ too-many-redirects? ] must-fail-with
|
||||||
|
|
||||||
[ "Goodbye" ] [
|
[ "Goodbye" ] [
|
||||||
"http://localhost:1237/quit" http-get
|
"http://localhost:1237/quit" http-get
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
|
! Dispatcher bugs
|
||||||
|
[ ] [
|
||||||
|
[
|
||||||
|
<dispatcher>
|
||||||
|
<action> <protected>
|
||||||
|
<login>
|
||||||
|
<url-sessions> "" add-responder
|
||||||
|
add-quit-action
|
||||||
|
<dispatcher>
|
||||||
|
<action> "a" add-main-responder
|
||||||
|
"d" add-responder
|
||||||
|
main-responder set
|
||||||
|
|
||||||
|
[ 1237 httpd ] "HTTPD test" spawn drop
|
||||||
|
] with-scope
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
[ ] [ 1000 sleep ] unit-test
|
||||||
|
|
||||||
|
: 404? [ download-failed? ] [ response>> code>> 404 = ] bi and ;
|
||||||
|
|
||||||
|
! This should give a 404 not an infinite redirect loop
|
||||||
|
[ "http://localhost:1237/d/blah" http-get ] [ 404? ] must-fail-with
|
||||||
|
|
||||||
|
! This should give a 404 not an infinite redirect loop
|
||||||
|
[ "http://localhost:1237/blah/" http-get ] [ 404? ] must-fail-with
|
||||||
|
|
||||||
|
[ "Goodbye" ] [ "http://localhost:1237/quit" http-get ] unit-test
|
||||||
|
|
||||||
|
[ ] [
|
||||||
|
[
|
||||||
|
<dispatcher>
|
||||||
|
<action> [ "text/plain" <content> [ "Hi" write ] >>body ] >>display
|
||||||
|
<login> <url-sessions>
|
||||||
|
"" add-responder
|
||||||
|
add-quit-action
|
||||||
|
main-responder set
|
||||||
|
|
||||||
|
[ 1237 httpd ] "HTTPD test" spawn drop
|
||||||
|
] with-scope
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
[ ] [ 1000 sleep ] unit-test
|
||||||
|
|
||||||
|
[ "Hi" ] [ "http://localhost:1237/" http-get ] unit-test
|
||||||
|
|
||||||
|
[ "Goodbye" ] [ "http://localhost:1237/quit" http-get ] unit-test
|
||||||
|
|
|
@ -1,10 +1,18 @@
|
||||||
! 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: fry hashtables io io.streams.string kernel math sets
|
USING: accessors kernel combinators math namespaces
|
||||||
namespaces math.parser assocs sequences strings splitting ascii
|
|
||||||
io.encodings.utf8 io.encodings.string namespaces unicode.case
|
assocs sequences splitting sorting sets debugger
|
||||||
combinators vectors sorting accessors calendar
|
strings vectors hashtables quotations arrays byte-arrays
|
||||||
calendar.format quotations arrays combinators.lib byte-arrays ;
|
math.parser calendar calendar.format
|
||||||
|
|
||||||
|
io io.streams.string io.encodings.utf8 io.encodings.string
|
||||||
|
io.sockets
|
||||||
|
|
||||||
|
unicode.case unicode.categories qualified ;
|
||||||
|
|
||||||
|
EXCLUDE: fry => , ;
|
||||||
|
|
||||||
IN: http
|
IN: http
|
||||||
|
|
||||||
: http-port 80 ; inline
|
: http-port 80 ; inline
|
||||||
|
@ -13,11 +21,12 @@ IN: http
|
||||||
#! In a URL, can this character be used without
|
#! In a URL, can this character be used without
|
||||||
#! URL-encoding?
|
#! URL-encoding?
|
||||||
{
|
{
|
||||||
[ dup letter? ]
|
{ [ dup letter? ] [ t ] }
|
||||||
[ dup LETTER? ]
|
{ [ dup LETTER? ] [ t ] }
|
||||||
[ dup digit? ]
|
{ [ dup digit? ] [ t ] }
|
||||||
[ dup "/_-.:" member? ]
|
{ [ dup "/_-.:" member? ] [ t ] }
|
||||||
} || nip ; foldable
|
[ f ]
|
||||||
|
} cond nip ; foldable
|
||||||
|
|
||||||
: push-utf8 ( ch -- )
|
: push-utf8 ( ch -- )
|
||||||
1string utf8 encode
|
1string utf8 encode
|
||||||
|
@ -75,8 +84,15 @@ IN: http
|
||||||
] if
|
] if
|
||||||
] if ;
|
] if ;
|
||||||
|
|
||||||
|
: read-lf ( -- string )
|
||||||
|
"\n" read-until CHAR: \n assert= ;
|
||||||
|
|
||||||
|
: read-crlf ( -- string )
|
||||||
|
"\r" read-until
|
||||||
|
[ CHAR: \r assert= read1 CHAR: \n assert= ] when* ;
|
||||||
|
|
||||||
: read-header-line ( -- )
|
: read-header-line ( -- )
|
||||||
readln dup
|
read-crlf dup
|
||||||
empty? [ drop ] [ header-line read-header-line ] if ;
|
empty? [ drop ] [ header-line read-header-line ] if ;
|
||||||
|
|
||||||
: read-header ( -- assoc )
|
: read-header ( -- assoc )
|
||||||
|
@ -175,13 +191,17 @@ post-data
|
||||||
post-data-type
|
post-data-type
|
||||||
cookies ;
|
cookies ;
|
||||||
|
|
||||||
|
: set-header ( request/response value key -- request/response )
|
||||||
|
pick header>> set-at ;
|
||||||
|
|
||||||
: <request>
|
: <request>
|
||||||
request new
|
request new
|
||||||
"1.1" >>version
|
"1.1" >>version
|
||||||
http-port >>port
|
http-port >>port
|
||||||
H{ } clone >>header
|
H{ } clone >>header
|
||||||
H{ } clone >>query
|
H{ } clone >>query
|
||||||
V{ } clone >>cookies ;
|
V{ } clone >>cookies
|
||||||
|
"close" "connection" set-header ;
|
||||||
|
|
||||||
: query-param ( request key -- value )
|
: query-param ( request key -- value )
|
||||||
swap query>> at ;
|
swap query>> at ;
|
||||||
|
@ -220,7 +240,7 @@ cookies ;
|
||||||
dup { "1.0" "1.1" } member? [ "Bad version" throw ] unless ;
|
dup { "1.0" "1.1" } member? [ "Bad version" throw ] unless ;
|
||||||
|
|
||||||
: read-request-version ( request -- request )
|
: read-request-version ( request -- request )
|
||||||
readln [ CHAR: \s = ] left-trim
|
read-crlf [ CHAR: \s = ] left-trim
|
||||||
parse-version
|
parse-version
|
||||||
>>version ;
|
>>version ;
|
||||||
|
|
||||||
|
@ -295,9 +315,15 @@ SYMBOL: max-post-request
|
||||||
"application/x-www-form-urlencoded" >>post-data-type
|
"application/x-www-form-urlencoded" >>post-data-type
|
||||||
] if ;
|
] if ;
|
||||||
|
|
||||||
|
: request-addr ( request -- addr )
|
||||||
|
[ host>> ] [ port>> ] bi <inet> ;
|
||||||
|
|
||||||
|
: request-host ( request -- string )
|
||||||
|
[ host>> ] [ drop ":" ] [ port>> number>string ] tri 3append ;
|
||||||
|
|
||||||
: write-request-header ( request -- request )
|
: write-request-header ( request -- request )
|
||||||
dup header>> >hashtable
|
dup header>> >hashtable
|
||||||
over host>> [ "host" pick set-at ] when*
|
over host>> [ over request-host "host" pick set-at ] when
|
||||||
over post-data>> [ length "content-length" pick set-at ] when*
|
over post-data>> [ length "content-length" pick set-at ] when*
|
||||||
over post-data-type>> [ "content-type" pick set-at ] when*
|
over post-data-type>> [ "content-type" pick set-at ] when*
|
||||||
over cookies>> f like [ unparse-cookies "cookie" pick set-at ] when*
|
over cookies>> f like [ unparse-cookies "cookie" pick set-at ] when*
|
||||||
|
@ -330,9 +356,6 @@ SYMBOL: max-post-request
|
||||||
tri
|
tri
|
||||||
] with-string-writer ;
|
] with-string-writer ;
|
||||||
|
|
||||||
: set-header ( request/response value key -- request/response )
|
|
||||||
pick header>> set-at ;
|
|
||||||
|
|
||||||
GENERIC: write-response ( response -- )
|
GENERIC: write-response ( response -- )
|
||||||
|
|
||||||
GENERIC: write-full-response ( request response -- )
|
GENERIC: write-full-response ( request response -- )
|
||||||
|
@ -365,7 +388,7 @@ body ;
|
||||||
>>code ;
|
>>code ;
|
||||||
|
|
||||||
: read-response-message
|
: read-response-message
|
||||||
readln >>message ;
|
read-crlf >>message ;
|
||||||
|
|
||||||
: read-response-header
|
: read-response-header
|
||||||
read-header >>header
|
read-header >>header
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
IN: http.server.actions.tests
|
|
||||||
USING: http.server.actions http.server.validators
|
USING: http.server.actions http.server.validators
|
||||||
tools.test math math.parser multiline namespaces http
|
tools.test math math.parser multiline namespaces http
|
||||||
io.streams.string http.server sequences accessors ;
|
io.streams.string http.server sequences splitting accessors ;
|
||||||
|
IN: http.server.actions.tests
|
||||||
|
|
||||||
[
|
[
|
||||||
"a" [ v-number ] { { "a" "123" } } validate-param
|
"a" [ v-number ] { { "a" "123" } } validate-param
|
||||||
|
@ -13,6 +13,8 @@ io.streams.string http.server sequences accessors ;
|
||||||
{ { "a" [ v-number ] } { "b" [ v-number ] } } >>get-params
|
{ { "a" [ v-number ] } { "b" [ v-number ] } } >>get-params
|
||||||
"action-1" set
|
"action-1" set
|
||||||
|
|
||||||
|
: lf>crlf "\n" split "\r\n" join ;
|
||||||
|
|
||||||
STRING: action-request-test-1
|
STRING: action-request-test-1
|
||||||
GET http://foo/bar?a=12&b=13 HTTP/1.1
|
GET http://foo/bar?a=12&b=13 HTTP/1.1
|
||||||
|
|
||||||
|
@ -20,28 +22,8 @@ blah
|
||||||
;
|
;
|
||||||
|
|
||||||
[ 25 ] [
|
[ 25 ] [
|
||||||
action-request-test-1 [ read-request ] with-string-reader
|
action-request-test-1 lf>crlf
|
||||||
|
[ read-request ] with-string-reader
|
||||||
request set
|
request set
|
||||||
"/blah"
|
{ } "action-1" get call-responder
|
||||||
"action-1" get call-responder
|
|
||||||
] unit-test
|
|
||||||
|
|
||||||
<action>
|
|
||||||
[ +append-path get "xxx" get "X" <repetition> concat append ] >>submit
|
|
||||||
{ { +append-path [ ] } { "xxx" [ v-number ] } } >>post-params
|
|
||||||
"action-2" set
|
|
||||||
|
|
||||||
STRING: action-request-test-2
|
|
||||||
POST http://foo/bar/baz HTTP/1.1
|
|
||||||
content-length: 5
|
|
||||||
content-type: application/x-www-form-urlencoded
|
|
||||||
|
|
||||||
xxx=4
|
|
||||||
;
|
|
||||||
|
|
||||||
[ "/blahXXXX" ] [
|
|
||||||
action-request-test-2 [ read-request ] with-string-reader
|
|
||||||
request set
|
|
||||||
"/blah"
|
|
||||||
"action-2" get call-responder
|
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
|
@ -5,7 +5,7 @@ http.server http.server.validators http hashtables namespaces
|
||||||
fry continuations locals ;
|
fry continuations locals ;
|
||||||
IN: http.server.actions
|
IN: http.server.actions
|
||||||
|
|
||||||
SYMBOL: +append-path
|
SYMBOL: +path+
|
||||||
|
|
||||||
SYMBOL: params
|
SYMBOL: params
|
||||||
|
|
||||||
|
@ -39,12 +39,15 @@ TUPLE: action init display submit get-params post-params ;
|
||||||
|
|
||||||
M: action call-responder ( path action -- response )
|
M: action call-responder ( path action -- response )
|
||||||
'[
|
'[
|
||||||
, ,
|
, [ CHAR: / = ] right-trim empty? [
|
||||||
[ +append-path associate request-params assoc-union params set ]
|
, action set
|
||||||
[ action set ] bi*
|
request-params params set
|
||||||
request get method>> {
|
request get method>> {
|
||||||
{ "GET" [ handle-get ] }
|
{ "GET" [ handle-get ] }
|
||||||
{ "HEAD" [ handle-get ] }
|
{ "HEAD" [ handle-get ] }
|
||||||
{ "POST" [ handle-post ] }
|
{ "POST" [ handle-post ] }
|
||||||
} case
|
} case
|
||||||
|
] [
|
||||||
|
<404>
|
||||||
|
] if
|
||||||
] with-exit-continuation ;
|
] with-exit-continuation ;
|
||||||
|
|
|
@ -60,7 +60,7 @@ M: user-saver dispose
|
||||||
|
|
||||||
: successful-login ( user -- response )
|
: successful-login ( user -- response )
|
||||||
logged-in-user sset
|
logged-in-user sset
|
||||||
post-login-url sget "" or f <permanent-redirect>
|
post-login-url sget "$login" or f <permanent-redirect>
|
||||||
f post-login-url sset ;
|
f post-login-url sset ;
|
||||||
|
|
||||||
:: <login-action> ( -- action )
|
:: <login-action> ( -- action )
|
||||||
|
@ -162,10 +162,12 @@ SYMBOL: previous-page
|
||||||
<action>
|
<action>
|
||||||
[
|
[
|
||||||
blank-values
|
blank-values
|
||||||
|
|
||||||
logged-in-user sget
|
logged-in-user sget
|
||||||
dup username>> "username" set-value
|
[ username>> "username" set-value ]
|
||||||
dup realname>> "realname" set-value
|
[ realname>> "realname" set-value ]
|
||||||
dup email>> "email" set-value
|
[ email>> "email" set-value ]
|
||||||
|
tri
|
||||||
] >>init
|
] >>init
|
||||||
|
|
||||||
[ form edit-form ] >>display
|
[ form edit-form ] >>display
|
||||||
|
@ -190,6 +192,8 @@ SYMBOL: previous-page
|
||||||
"realname" value >>realname
|
"realname" value >>realname
|
||||||
"email" value >>email
|
"email" value >>email
|
||||||
|
|
||||||
|
drop
|
||||||
|
|
||||||
user-profile-changed? on
|
user-profile-changed? on
|
||||||
|
|
||||||
previous-page sget f <permanent-redirect>
|
previous-page sget f <permanent-redirect>
|
||||||
|
@ -329,7 +333,7 @@ SYMBOL: lost-password-from
|
||||||
<action>
|
<action>
|
||||||
[
|
[
|
||||||
f logged-in-user sset
|
f logged-in-user sset
|
||||||
"login" f <permanent-redirect>
|
"$login/login" f <permanent-redirect>
|
||||||
] >>submit ;
|
] >>submit ;
|
||||||
|
|
||||||
! ! ! Authentication logic
|
! ! ! Authentication logic
|
||||||
|
@ -340,7 +344,7 @@ C: <protected> protected
|
||||||
|
|
||||||
: show-login-page ( -- response )
|
: show-login-page ( -- response )
|
||||||
request get request-url post-login-url sset
|
request get request-url post-login-url sset
|
||||||
"login" f <permanent-redirect> ;
|
"$login/login" f <temporary-redirect> ;
|
||||||
|
|
||||||
M: protected call-responder ( path responder -- response )
|
M: protected call-responder ( path responder -- response )
|
||||||
logged-in-user sget dup [
|
logged-in-user sget dup [
|
||||||
|
@ -363,7 +367,7 @@ M: login call-responder ( path responder -- response )
|
||||||
|
|
||||||
: <login> ( responder -- auth )
|
: <login> ( responder -- auth )
|
||||||
login new-dispatcher
|
login new-dispatcher
|
||||||
swap <protected> >>default
|
swap >>default
|
||||||
<login-action> <login-boilerplate> "login" add-responder
|
<login-action> <login-boilerplate> "login" add-responder
|
||||||
<logout-action> <login-boilerplate> "logout" add-responder
|
<logout-action> <login-boilerplate> "logout" add-responder
|
||||||
no-users >>users ;
|
no-users >>users ;
|
||||||
|
|
|
@ -48,7 +48,7 @@ SYMBOL: next-template
|
||||||
: call-next-template ( -- )
|
: call-next-template ( -- )
|
||||||
next-template get write ;
|
next-template get write ;
|
||||||
|
|
||||||
M: f call-template drop call-next-template ;
|
M: f call-template* drop call-next-template ;
|
||||||
|
|
||||||
: with-boilerplate ( body template -- )
|
: with-boilerplate ( body template -- )
|
||||||
[
|
[
|
||||||
|
|
|
@ -8,7 +8,7 @@ splitting kernel hashtables continuations ;
|
||||||
<request> "GET" >>method request set
|
<request> "GET" >>method request set
|
||||||
[
|
[
|
||||||
exit-continuation set
|
exit-continuation set
|
||||||
"xxx"
|
{ }
|
||||||
<action> [ [ "hello" print 123 ] show-final ] >>display
|
<action> [ [ "hello" print 123 ] show-final ] >>display
|
||||||
<callback-responder>
|
<callback-responder>
|
||||||
call-responder
|
call-responder
|
||||||
|
@ -31,7 +31,7 @@ splitting kernel hashtables continuations ;
|
||||||
[
|
[
|
||||||
exit-continuation set
|
exit-continuation set
|
||||||
<request> "GET" >>method request set
|
<request> "GET" >>method request set
|
||||||
"" "r" get call-responder
|
{ } "r" get call-responder
|
||||||
] callcc1
|
] callcc1
|
||||||
|
|
||||||
body>> first
|
body>> first
|
||||||
|
@ -44,7 +44,7 @@ splitting kernel hashtables continuations ;
|
||||||
|
|
||||||
[
|
[
|
||||||
exit-continuation set
|
exit-continuation set
|
||||||
"/"
|
{ }
|
||||||
"r" get call-responder
|
"r" get call-responder
|
||||||
] callcc1
|
] callcc1
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ splitting kernel hashtables continuations ;
|
||||||
|
|
||||||
[
|
[
|
||||||
exit-continuation set
|
exit-continuation set
|
||||||
"/"
|
{ }
|
||||||
"r" get call-responder
|
"r" get call-responder
|
||||||
] callcc1
|
] callcc1
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
! Copyright (C) 2008 Slava Pestov
|
||||||
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
|
USING: splitting kernel io sequences xmode.code2html accessors
|
||||||
|
http.server.components ;
|
||||||
|
IN: http.server.components.code
|
||||||
|
|
||||||
|
TUPLE: code-renderer < text-renderer mode ;
|
||||||
|
|
||||||
|
: <code-renderer> ( mode -- renderer )
|
||||||
|
code-renderer new-text-renderer
|
||||||
|
swap >>mode ;
|
||||||
|
|
||||||
|
M: code-renderer render-view*
|
||||||
|
[ string-lines ] [ mode>> value ] bi* htmlize-lines ;
|
||||||
|
|
||||||
|
: <code> ( id mode -- component )
|
||||||
|
swap <text>
|
||||||
|
swap <code-renderer> >>renderer ;
|
|
@ -280,6 +280,22 @@ TUPLE: date < string ;
|
||||||
M: date component-string
|
M: date component-string
|
||||||
drop timestamp>string ;
|
drop timestamp>string ;
|
||||||
|
|
||||||
|
! Link components
|
||||||
|
|
||||||
|
GENERIC: link-title ( obj -- string )
|
||||||
|
GENERIC: link-href ( obj -- url )
|
||||||
|
|
||||||
|
SINGLETON: link-renderer
|
||||||
|
|
||||||
|
M: link-renderer render-view*
|
||||||
|
drop <a dup link-href =href a> link-title write </a> ;
|
||||||
|
|
||||||
|
TUPLE: link < string ;
|
||||||
|
|
||||||
|
: <link> ( id -- component )
|
||||||
|
link new-string
|
||||||
|
link-renderer >>renderer ;
|
||||||
|
|
||||||
! List components
|
! List components
|
||||||
SYMBOL: +plain+
|
SYMBOL: +plain+
|
||||||
SYMBOL: +ordered+
|
SYMBOL: +ordered+
|
||||||
|
@ -289,17 +305,20 @@ TUPLE: list-renderer component type ;
|
||||||
|
|
||||||
C: <list-renderer> list-renderer
|
C: <list-renderer> list-renderer
|
||||||
|
|
||||||
: render-plain-list ( seq quot component -- )
|
: render-plain-list ( seq component quot -- )
|
||||||
swap '[ , @ ] each ; inline
|
'[ , component>> renderer>> @ ] each ; inline
|
||||||
|
|
||||||
|
: render-li-list ( seq component quot -- )
|
||||||
|
'[ <li> @ </li> ] render-plain-list ; inline
|
||||||
|
|
||||||
: render-ordered-list ( seq quot component -- )
|
: render-ordered-list ( seq quot component -- )
|
||||||
swap <ol> '[ <li> , @ </li> ] each </ol> ; inline
|
<ol> render-li-list </ol> ; inline
|
||||||
|
|
||||||
: render-unordered-list ( seq quot component -- )
|
: render-unordered-list ( seq quot component -- )
|
||||||
swap <ul> '[ <li> , @ </li> ] each </ul> ; inline
|
<ul> render-li-list </ul> ; inline
|
||||||
|
|
||||||
: render-list ( value renderer quot -- )
|
: render-list ( value renderer quot -- )
|
||||||
swap [ component>> ] [ type>> ] bi {
|
over type>> {
|
||||||
{ +plain+ [ render-plain-list ] }
|
{ +plain+ [ render-plain-list ] }
|
||||||
{ +ordered+ [ render-ordered-list ] }
|
{ +ordered+ [ render-ordered-list ] }
|
||||||
{ +unordered+ [ render-unordered-list ] }
|
{ +unordered+ [ render-unordered-list ] }
|
||||||
|
@ -317,3 +336,26 @@ TUPLE: list < component ;
|
||||||
<list-renderer> list swap new-component ;
|
<list-renderer> list swap new-component ;
|
||||||
|
|
||||||
M: list component-string drop ;
|
M: list component-string drop ;
|
||||||
|
|
||||||
|
! Choice
|
||||||
|
TUPLE: choice-renderer choices ;
|
||||||
|
|
||||||
|
C: <choice-renderer> choice-renderer
|
||||||
|
|
||||||
|
M: choice-renderer render-view*
|
||||||
|
drop write ;
|
||||||
|
|
||||||
|
M: choice-renderer render-edit*
|
||||||
|
<select swap =name select>
|
||||||
|
choices>> [
|
||||||
|
<option [ = [ "true" =selected ] when ] keep option>
|
||||||
|
write
|
||||||
|
</option>
|
||||||
|
] with each
|
||||||
|
</select> ;
|
||||||
|
|
||||||
|
TUPLE: choice < string ;
|
||||||
|
|
||||||
|
: <choice> ( id choices -- component )
|
||||||
|
swap choice new-string
|
||||||
|
swap <choice-renderer> >>renderer ;
|
||||||
|
|
|
@ -78,4 +78,4 @@ M: form render-view*
|
||||||
dup view-template>> render-form ;
|
dup view-template>> render-form ;
|
||||||
|
|
||||||
M: form render-edit*
|
M: form render-edit*
|
||||||
dup edit-template>> render-form ;
|
nip dup edit-template>> render-form ;
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
USING: http.server tools.test kernel namespaces accessors
|
USING: http.server tools.test kernel namespaces accessors
|
||||||
io http math sequences assocs ;
|
io http math sequences assocs arrays classes words ;
|
||||||
IN: http.server.tests
|
IN: http.server.tests
|
||||||
|
|
||||||
|
\ find-responder must-infer
|
||||||
|
|
||||||
[
|
[
|
||||||
<request>
|
<request>
|
||||||
"www.apple.com" >>host
|
"www.apple.com" >>host
|
||||||
|
@ -29,7 +31,9 @@ M: mock-responder call-responder
|
||||||
"text/plain" <content> ;
|
"text/plain" <content> ;
|
||||||
|
|
||||||
: check-dispatch ( tag path -- ? )
|
: check-dispatch ( tag path -- ? )
|
||||||
|
H{ } clone base-paths set
|
||||||
over off
|
over off
|
||||||
|
split-path
|
||||||
main-responder get call-responder
|
main-responder get call-responder
|
||||||
write-response get ;
|
write-response get ;
|
||||||
|
|
||||||
|
@ -44,11 +48,11 @@ M: mock-responder call-responder
|
||||||
main-responder set
|
main-responder set
|
||||||
|
|
||||||
[ "foo" ] [
|
[ "foo" ] [
|
||||||
"foo" main-responder get find-responder path>> nip
|
{ "foo" } main-responder get find-responder path>> nip
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
[ "bar" ] [
|
[ "bar" ] [
|
||||||
"bar" main-responder get find-responder path>> nip
|
{ "bar" } main-responder get find-responder path>> nip
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
[ t ] [ "foo" "foo" check-dispatch ] unit-test
|
[ t ] [ "foo" "foo" check-dispatch ] unit-test
|
||||||
|
@ -60,14 +64,6 @@ M: mock-responder call-responder
|
||||||
[ t ] [ "123" "baz/123" check-dispatch ] unit-test
|
[ t ] [ "123" "baz/123" check-dispatch ] unit-test
|
||||||
[ t ] [ "123" "baz///123" check-dispatch ] unit-test
|
[ t ] [ "123" "baz///123" check-dispatch ] unit-test
|
||||||
|
|
||||||
[ t ] [
|
|
||||||
<request>
|
|
||||||
"baz" >>path
|
|
||||||
request set
|
|
||||||
"baz" main-responder get call-responder
|
|
||||||
dup code>> 300 399 between? >r
|
|
||||||
header>> "location" swap at "baz/" tail? r> and
|
|
||||||
] unit-test
|
|
||||||
] with-scope
|
] with-scope
|
||||||
|
|
||||||
[
|
[
|
||||||
|
@ -77,3 +73,67 @@ M: mock-responder call-responder
|
||||||
|
|
||||||
[ "/default" ] [ "/default" main-responder get find-responder drop ] unit-test
|
[ "/default" ] [ "/default" main-responder get find-responder drop ] unit-test
|
||||||
] with-scope
|
] 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
|
||||||
|
"text/plain" <content> swap >array >>body ;
|
||||||
|
|
||||||
|
[ { "c" } ] [
|
||||||
|
H{ } clone base-paths 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
|
||||||
|
|
||||||
|
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> swap >>body ;
|
||||||
|
|
||||||
|
[ ] [
|
||||||
|
<dispatcher>
|
||||||
|
<dispatcher>
|
||||||
|
<funny-dispatcher>
|
||||||
|
<base-path-check-responder> "c" add-responder
|
||||||
|
"b" add-responder
|
||||||
|
"a" add-responder
|
||||||
|
main-responder set
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
[ "/a/b/" ] [
|
||||||
|
"a/b/c" split-path main-responder get call-responder body>>
|
||||||
|
] unit-test
|
||||||
|
|
|
@ -4,9 +4,11 @@ USING: assocs kernel namespaces io io.timeouts strings splitting
|
||||||
threads http sequences prettyprint io.server logging calendar
|
threads http sequences prettyprint io.server logging calendar
|
||||||
html.elements accessors math.parser combinators.lib
|
html.elements accessors math.parser combinators.lib
|
||||||
tools.vocabs debugger html continuations random combinators
|
tools.vocabs debugger html continuations random combinators
|
||||||
destructors io.encodings.8-bit fry ;
|
destructors io.encodings.8-bit fry classes words ;
|
||||||
IN: http.server
|
IN: http.server
|
||||||
|
|
||||||
|
! path is a sequence of path component strings
|
||||||
|
|
||||||
GENERIC: call-responder ( path responder -- response )
|
GENERIC: call-responder ( path responder -- response )
|
||||||
|
|
||||||
: request-params ( -- assoc )
|
: request-params ( -- assoc )
|
||||||
|
@ -52,13 +54,39 @@ SYMBOL: 404-responder
|
||||||
|
|
||||||
[ <404> ] <trivial-responder> 404-responder set-global
|
[ <404> ] <trivial-responder> 404-responder set-global
|
||||||
|
|
||||||
|
SYMBOL: base-paths
|
||||||
|
|
||||||
|
: invert-slice ( slice -- slice' )
|
||||||
|
dup slice? [
|
||||||
|
[ seq>> ] [ from>> ] bi head-slice
|
||||||
|
] [
|
||||||
|
drop { }
|
||||||
|
] if ;
|
||||||
|
|
||||||
|
: add-base-path ( path dispatcher -- )
|
||||||
|
[ invert-slice ] [ class word-name ] bi*
|
||||||
|
base-paths get set-at ;
|
||||||
|
|
||||||
SYMBOL: link-hook
|
SYMBOL: link-hook
|
||||||
|
|
||||||
: modify-query ( query -- query )
|
: modify-query ( query -- query )
|
||||||
link-hook get [ ] or call ;
|
link-hook get [ ] or call ;
|
||||||
|
|
||||||
|
: base-path ( string -- path )
|
||||||
|
dup base-paths get at
|
||||||
|
[ ] [ "No such responder: " swap append throw ] ?if ;
|
||||||
|
|
||||||
|
: resolve-base-path ( string -- string' )
|
||||||
|
"$" ?head [
|
||||||
|
[
|
||||||
|
"/" split1 >r
|
||||||
|
base-path [ "/" % % ] each "/" %
|
||||||
|
r> %
|
||||||
|
] "" make
|
||||||
|
] when ;
|
||||||
|
|
||||||
: link>string ( url query -- url' )
|
: link>string ( url query -- url' )
|
||||||
modify-query (link>string) ;
|
[ resolve-base-path ] [ modify-query ] bi* (link>string) ;
|
||||||
|
|
||||||
: write-link ( url query -- )
|
: write-link ( url query -- )
|
||||||
link>string write ;
|
link>string write ;
|
||||||
|
@ -73,6 +101,7 @@ SYMBOL: form-hook
|
||||||
request get clone
|
request get clone
|
||||||
swap [ >>query ] when*
|
swap [ >>query ] when*
|
||||||
swap url-encode >>path
|
swap url-encode >>path
|
||||||
|
[ modify-query ] change-query
|
||||||
request-url ;
|
request-url ;
|
||||||
|
|
||||||
: replace-last-component ( path with -- path' )
|
: replace-last-component ( path with -- path' )
|
||||||
|
@ -82,13 +111,14 @@ SYMBOL: form-hook
|
||||||
request get clone
|
request get clone
|
||||||
swap [ >>query ] when*
|
swap [ >>query ] when*
|
||||||
swap [ '[ , replace-last-component ] change-path ] when*
|
swap [ '[ , replace-last-component ] change-path ] when*
|
||||||
dup query>> modify-query >>query
|
[ modify-query ] change-query
|
||||||
request-url ;
|
request-url ;
|
||||||
|
|
||||||
: derive-url ( to query -- url )
|
: derive-url ( to query -- url )
|
||||||
{
|
{
|
||||||
{ [ over "http://" head? ] [ link>string ] }
|
{ [ over "http://" head? ] [ link>string ] }
|
||||||
{ [ over "/" head? ] [ absolute-redirect ] }
|
{ [ over "/" head? ] [ absolute-redirect ] }
|
||||||
|
{ [ over "$" head? ] [ >r resolve-base-path r> derive-url ] }
|
||||||
[ relative-redirect ]
|
[ relative-redirect ]
|
||||||
} cond ;
|
} cond ;
|
||||||
|
|
||||||
|
@ -113,22 +143,17 @@ TUPLE: dispatcher default responders ;
|
||||||
: <dispatcher> ( -- dispatcher )
|
: <dispatcher> ( -- dispatcher )
|
||||||
dispatcher new-dispatcher ;
|
dispatcher new-dispatcher ;
|
||||||
|
|
||||||
: split-path ( path -- rest first )
|
|
||||||
[ CHAR: / = ] left-trim "/" split1 swap ;
|
|
||||||
|
|
||||||
: find-responder ( path dispatcher -- path responder )
|
: find-responder ( path dispatcher -- path responder )
|
||||||
over split-path pick responders>> at*
|
over empty? [
|
||||||
[ >r >r 2drop r> r> ] [ 2drop default>> ] if ;
|
"" over responders>> at*
|
||||||
|
[ nip ] [ drop default>> ] if
|
||||||
: redirect-with-/ ( -- response )
|
] [
|
||||||
request get path>> "/" append f <permanent-redirect> ;
|
over first over responders>> at*
|
||||||
|
[ >r drop 1 tail-slice r> ] [ drop default>> ] if
|
||||||
|
] if ;
|
||||||
|
|
||||||
M: dispatcher call-responder ( path dispatcher -- response )
|
M: dispatcher call-responder ( path dispatcher -- response )
|
||||||
over [
|
[ add-base-path ] [ find-responder call-responder ] 2bi ;
|
||||||
find-responder call-responder
|
|
||||||
] [
|
|
||||||
2drop redirect-with-/
|
|
||||||
] if ;
|
|
||||||
|
|
||||||
TUPLE: vhost-dispatcher default responders ;
|
TUPLE: vhost-dispatcher default responders ;
|
||||||
|
|
||||||
|
@ -142,15 +167,13 @@ TUPLE: vhost-dispatcher default responders ;
|
||||||
M: vhost-dispatcher call-responder ( path dispatcher -- response )
|
M: vhost-dispatcher call-responder ( path dispatcher -- response )
|
||||||
find-vhost call-responder ;
|
find-vhost call-responder ;
|
||||||
|
|
||||||
: set-main ( dispatcher name -- dispatcher )
|
|
||||||
'[ , f <permanent-redirect> ] <trivial-responder>
|
|
||||||
>>default ;
|
|
||||||
|
|
||||||
: add-responder ( dispatcher responder path -- dispatcher )
|
: add-responder ( dispatcher responder path -- dispatcher )
|
||||||
pick responders>> set-at ;
|
pick responders>> set-at ;
|
||||||
|
|
||||||
: add-main-responder ( dispatcher responder path -- dispatcher )
|
: add-main-responder ( dispatcher responder path -- dispatcher )
|
||||||
[ add-responder ] keep set-main ;
|
[ add-responder drop ]
|
||||||
|
[ drop "" add-responder drop ]
|
||||||
|
[ 2drop ] 3tri ;
|
||||||
|
|
||||||
SYMBOL: main-responder
|
SYMBOL: main-responder
|
||||||
|
|
||||||
|
@ -160,23 +183,30 @@ drop
|
||||||
|
|
||||||
SYMBOL: development-mode
|
SYMBOL: development-mode
|
||||||
|
|
||||||
: <500> ( error -- response )
|
: http-error. ( error -- )
|
||||||
500 "Internal server error" <trivial-response>
|
"Internal server error" [
|
||||||
swap '[
|
|
||||||
, "Internal server error" [
|
|
||||||
development-mode get [
|
development-mode get [
|
||||||
[ print-error nl :c ] with-html-stream
|
[ print-error nl :c ] with-html-stream
|
||||||
] [
|
] [
|
||||||
500 "Internal server error"
|
500 "Internal server error"
|
||||||
trivial-response-body
|
trivial-response-body
|
||||||
] if
|
] if
|
||||||
] simple-page
|
] simple-page ;
|
||||||
] >>body ;
|
|
||||||
|
: <500> ( error -- response )
|
||||||
|
500 "Internal server error" <trivial-response>
|
||||||
|
swap '[ , http-error. ] >>body ;
|
||||||
|
|
||||||
: do-response ( response -- )
|
: do-response ( response -- )
|
||||||
dup write-response
|
dup write-response
|
||||||
request get method>> "HEAD" =
|
request get method>> "HEAD" =
|
||||||
[ drop ] [ write-response-body ] if ;
|
[ drop ] [
|
||||||
|
'[
|
||||||
|
, write-response-body
|
||||||
|
] [
|
||||||
|
http-error.
|
||||||
|
] recover
|
||||||
|
] if ;
|
||||||
|
|
||||||
LOG: httpd-hit NOTICE
|
LOG: httpd-hit NOTICE
|
||||||
|
|
||||||
|
@ -190,11 +220,15 @@ SYMBOL: exit-continuation
|
||||||
: with-exit-continuation ( quot -- )
|
: with-exit-continuation ( quot -- )
|
||||||
'[ exit-continuation set @ ] callcc1 exit-continuation off ;
|
'[ exit-continuation set @ ] callcc1 exit-continuation off ;
|
||||||
|
|
||||||
|
: split-path ( string -- path )
|
||||||
|
"/" split [ empty? not ] subset ;
|
||||||
|
|
||||||
: do-request ( request -- response )
|
: do-request ( request -- response )
|
||||||
[
|
[
|
||||||
|
H{ } clone base-paths set
|
||||||
[ log-request ]
|
[ log-request ]
|
||||||
[ request set ]
|
[ request set ]
|
||||||
[ path>> main-responder get call-responder ] tri
|
[ path>> split-path main-responder get call-responder ] tri
|
||||||
[ <404> ] unless*
|
[ <404> ] unless*
|
||||||
] [
|
] [
|
||||||
[ \ do-request log-error ]
|
[ \ do-request log-error ]
|
||||||
|
|
|
@ -61,7 +61,7 @@ M: foo call-responder
|
||||||
<request>
|
<request>
|
||||||
"GET" >>method
|
"GET" >>method
|
||||||
request set
|
request set
|
||||||
"/etc" "manager" get call-responder
|
{ "etc" } "manager" get call-responder
|
||||||
response set
|
response set
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ M: foo call-responder
|
||||||
"id" get session-id-key set-query-param
|
"id" get session-id-key set-query-param
|
||||||
"/" >>path
|
"/" >>path
|
||||||
request set
|
request set
|
||||||
"/" "manager" get call-responder
|
{ } "manager" get call-responder
|
||||||
[ write-response-body drop ] with-string-writer
|
[ write-response-body drop ] with-string-writer
|
||||||
] with-destructors ;
|
] with-destructors ;
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ M: foo call-responder
|
||||||
"GET" >>method
|
"GET" >>method
|
||||||
"/" >>path
|
"/" >>path
|
||||||
request set
|
request set
|
||||||
"/etc" "manager" get call-responder response set
|
{ "etc" } "manager" get call-responder response set
|
||||||
[ "1" ] [ [ response get write-response-body drop ] with-string-writer ] unit-test
|
[ "1" ] [ [ response get write-response-body drop ] with-string-writer ] unit-test
|
||||||
response get
|
response get
|
||||||
] with-destructors
|
] with-destructors
|
||||||
|
@ -111,7 +111,7 @@ response set
|
||||||
"cookies" get >>cookies
|
"cookies" get >>cookies
|
||||||
"/" >>path
|
"/" >>path
|
||||||
request set
|
request set
|
||||||
"/" "manager" get call-responder
|
{ } "manager" get call-responder
|
||||||
[ write-response-body drop ] with-string-writer
|
[ write-response-body drop ] with-string-writer
|
||||||
] with-destructors ;
|
] with-destructors ;
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ response set
|
||||||
request set
|
request set
|
||||||
|
|
||||||
[
|
[
|
||||||
"/" <exiting-action> <cookie-sessions>
|
{ } <exiting-action> <cookie-sessions>
|
||||||
call-responder
|
call-responder
|
||||||
] with-destructors response set
|
] with-destructors response set
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
|
@ -69,32 +69,24 @@ TUPLE: file-responder root hook special ;
|
||||||
swap '[ , directory. ] >>body ;
|
swap '[ , directory. ] >>body ;
|
||||||
|
|
||||||
: find-index ( filename -- path )
|
: find-index ( filename -- path )
|
||||||
{ "index.html" "index.fhtml" } [ append-path ] with map
|
"index.html" append-path dup exists? [ drop f ] unless ;
|
||||||
[ exists? ] find nip ;
|
|
||||||
|
|
||||||
: serve-directory ( filename -- response )
|
: serve-directory ( filename -- response )
|
||||||
dup "/" tail? [
|
request get path>> "/" tail? [
|
||||||
dup find-index
|
dup
|
||||||
[ serve-file ] [ list-directory ] ?if
|
find-index [ serve-file ] [ list-directory ] ?if
|
||||||
] [
|
] [
|
||||||
drop request get redirect-with-/
|
drop
|
||||||
|
request get path>> "/" append f <permanent-redirect>
|
||||||
] if ;
|
] if ;
|
||||||
|
|
||||||
: serve-object ( filename -- response )
|
: serve-object ( filename -- response )
|
||||||
serving-path dup exists? [
|
serving-path dup exists?
|
||||||
dup directory? [ serve-directory ] [ serve-file ] if
|
[ dup directory? [ serve-directory ] [ serve-file ] if ]
|
||||||
] [
|
[ drop <404> ]
|
||||||
drop <404>
|
if ;
|
||||||
] if ;
|
|
||||||
|
|
||||||
M: file-responder call-responder ( path responder -- response )
|
M: file-responder call-responder ( path responder -- response )
|
||||||
file-responder set
|
file-responder set
|
||||||
dup [
|
".." over member?
|
||||||
".." over subseq? [
|
[ drop <400> ] [ "/" join serve-object ] if ;
|
||||||
drop <400>
|
|
||||||
] [
|
|
||||||
serve-object
|
|
||||||
] if
|
|
||||||
] [
|
|
||||||
drop redirect-with-/
|
|
||||||
] if ;
|
|
||||||
|
|
|
@ -104,7 +104,8 @@ SYMBOL: tags
|
||||||
: form-start-tag ( tag -- )
|
: form-start-tag ( tag -- )
|
||||||
<form
|
<form
|
||||||
"POST" =method
|
"POST" =method
|
||||||
tag-attrs print-attrs
|
[ "action" required-attr resolve-base-path =action ]
|
||||||
|
[ tag-attrs [ drop name-tag "action" = not ] assoc-subset print-attrs ] bi
|
||||||
form>
|
form>
|
||||||
hidden-form-field ;
|
hidden-form-field ;
|
||||||
|
|
||||||
|
@ -153,6 +154,7 @@ SYMBOL: tags
|
||||||
{ "form" [ form-tag ] }
|
{ "form" [ form-tag ] }
|
||||||
{ "error" [ error-tag ] }
|
{ "error" [ error-tag ] }
|
||||||
{ "if" [ if-tag ] }
|
{ "if" [ if-tag ] }
|
||||||
|
{ "comment" [ drop ] }
|
||||||
{ "call-next-template" [ drop call-next-template ] }
|
{ "call-next-template" [ drop call-next-template ] }
|
||||||
[ "Unknown chloe tag: " swap append throw ]
|
[ "Unknown chloe tag: " swap append throw ]
|
||||||
} case ;
|
} case ;
|
||||||
|
@ -189,7 +191,7 @@ SYMBOL: tags
|
||||||
] if
|
] if
|
||||||
] with-scope ;
|
] with-scope ;
|
||||||
|
|
||||||
M: chloe call-template
|
M: chloe call-template*
|
||||||
path>> utf8 <file-reader> read-xml process-chloe ;
|
path>> utf8 <file-reader> read-xml process-chloe ;
|
||||||
|
|
||||||
INSTANCE: chloe template
|
INSTANCE: chloe template
|
||||||
|
|
|
@ -76,7 +76,7 @@ TUPLE: fhtml path ;
|
||||||
|
|
||||||
C: <fhtml> fhtml
|
C: <fhtml> fhtml
|
||||||
|
|
||||||
M: fhtml call-template ( filename -- )
|
M: fhtml call-template* ( filename -- )
|
||||||
'[
|
'[
|
||||||
, path>> [
|
, path>> [
|
||||||
"quiet" on
|
"quiet" on
|
||||||
|
|
|
@ -1,10 +1,21 @@
|
||||||
USING: accessors kernel fry io.encodings.utf8 io.files
|
USING: accessors kernel fry io io.encodings.utf8 io.files
|
||||||
http http.server ;
|
http http.server debugger prettyprint continuations ;
|
||||||
IN: http.server.templating
|
IN: http.server.templating
|
||||||
|
|
||||||
MIXIN: template
|
MIXIN: template
|
||||||
|
|
||||||
GENERIC: call-template ( template -- )
|
GENERIC: call-template* ( template -- )
|
||||||
|
|
||||||
|
ERROR: template-error template error ;
|
||||||
|
|
||||||
|
M: template-error error.
|
||||||
|
"Error while processing template " write
|
||||||
|
[ template>> pprint ":" print nl ]
|
||||||
|
[ error>> error. ]
|
||||||
|
bi ;
|
||||||
|
|
||||||
|
: call-template ( template -- )
|
||||||
|
[ call-template* ] [ template-error ] recover ;
|
||||||
|
|
||||||
M: template write-response-body* call-template ;
|
M: template write-response-body* call-template ;
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,11 @@ USING: io io.mmap io.files kernel tools.test continuations
|
||||||
sequences io.encodings.ascii accessors ;
|
sequences io.encodings.ascii accessors ;
|
||||||
IN: io.mmap.tests
|
IN: io.mmap.tests
|
||||||
|
|
||||||
[ "mmap-test-file.txt" resource-path delete-file ] ignore-errors
|
[ "resource:mmap-test-file.txt" delete-file ] ignore-errors
|
||||||
[ ] [ "12345" "mmap-test-file.txt" resource-path ascii set-file-contents ] unit-test
|
[ ] [ "12345" "resource:mmap-test-file.txt" ascii set-file-contents ] unit-test
|
||||||
[ ] [ "mmap-test-file.txt" resource-path dup file-info size>> [ CHAR: 2 0 pick set-nth drop ] with-mapped-file ] unit-test
|
[ ] [ "resource:mmap-test-file.txt" dup file-info size>> [ CHAR: 2 0 pick set-nth drop ] with-mapped-file ] unit-test
|
||||||
[ 5 ] [ "mmap-test-file.txt" resource-path dup file-info size>> [ length ] with-mapped-file ] unit-test
|
[ 5 ] [ "resource:mmap-test-file.txt" dup file-info size>> [ length ] with-mapped-file ] unit-test
|
||||||
[ "22345" ] [ "mmap-test-file.txt" resource-path ascii file-contents ] unit-test
|
[ "22345" ] [ "resource:mmap-test-file.txt" ascii file-contents ] unit-test
|
||||||
[ "mmap-test-file.txt" resource-path delete-file ] ignore-errors
|
[ "resource:mmap-test-file.txt" delete-file ] ignore-errors
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,10 @@ M: mapped-file set-nth-unsafe
|
||||||
|
|
||||||
INSTANCE: mapped-file sequence
|
INSTANCE: mapped-file sequence
|
||||||
|
|
||||||
HOOK: <mapped-file> io-backend ( path length -- mmap )
|
HOOK: (mapped-file) io-backend ( path length -- mmap )
|
||||||
|
|
||||||
|
: <mapped-file> ( path length -- mmap )
|
||||||
|
>r normalize-path r> (mapped-file) ;
|
||||||
|
|
||||||
HOOK: close-mapped-file io-backend ( mmap -- )
|
HOOK: close-mapped-file io-backend ( mmap -- )
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ IN: io.unix.mmap
|
||||||
>r f -roll r> open-r/w [ 0 mmap ] keep
|
>r f -roll r> open-r/w [ 0 mmap ] keep
|
||||||
over MAP_FAILED = [ close (io-error) ] when ;
|
over MAP_FAILED = [ close (io-error) ] when ;
|
||||||
|
|
||||||
M: unix <mapped-file> ( path length -- obj )
|
M: unix (mapped-file) ( path length -- obj )
|
||||||
swap >r
|
swap >r
|
||||||
dup PROT_READ PROT_WRITE bitor MAP_FILE MAP_SHARED bitor
|
dup PROT_READ PROT_WRITE bitor MAP_FILE MAP_SHARED bitor
|
||||||
r> mmap-open f mapped-file boa ;
|
r> mmap-open f mapped-file boa ;
|
||||||
|
|
|
@ -70,7 +70,7 @@ M: wince with-privileges
|
||||||
dup close-later
|
dup close-later
|
||||||
] with-privileges ;
|
] with-privileges ;
|
||||||
|
|
||||||
M: windows <mapped-file> ( path length -- mmap )
|
M: windows (mapped-file) ( path length -- mmap )
|
||||||
[
|
[
|
||||||
swap
|
swap
|
||||||
GENERIC_WRITE GENERIC_READ bitor
|
GENERIC_WRITE GENERIC_READ bitor
|
||||||
|
|
|
@ -98,7 +98,7 @@ TUPLE: win32-monitor < monitor port ;
|
||||||
|
|
||||||
M:: winnt (monitor) ( path recursive? mailbox -- monitor )
|
M:: winnt (monitor) ( path recursive? mailbox -- monitor )
|
||||||
[
|
[
|
||||||
path mailbox win32-monitor new-monitor
|
path normalize-path mailbox win32-monitor new-monitor
|
||||||
path open-directory \ win32-monitor-port <buffered-port>
|
path open-directory \ win32-monitor-port <buffered-port>
|
||||||
recursive? >>recursive
|
recursive? >>recursive
|
||||||
>>port
|
>>port
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
USING: combinators combinators.lib io locals kernel math
|
eSING: combinators combinators.lib io locals kernel math
|
||||||
math.functions math.ranges namespaces random sequences
|
math.functions math.ranges namespaces random sequences
|
||||||
hashtables sets ;
|
hashtables sets ;
|
||||||
IN: math.miller-rabin
|
IN: math.miller-rabin
|
||||||
|
@ -76,7 +76,9 @@ TUPLE: miller-rabin-bounds ;
|
||||||
: find-relative-prime ( n -- p )
|
: find-relative-prime ( n -- p )
|
||||||
dup random find-relative-prime* ;
|
dup random find-relative-prime* ;
|
||||||
|
|
||||||
|
ERROR: too-few-primes ;
|
||||||
|
|
||||||
: unique-primes ( numbits n -- seq )
|
: unique-primes ( numbits n -- seq )
|
||||||
#! generate two primes
|
#! generate two primes
|
||||||
over 5 < [ "not enough primes below 5 bits" throw ] when
|
over 5 < [ too-few-primes ] when
|
||||||
[ [ drop random-prime ] with map ] [ all-unique? ] generate ;
|
[ [ drop random-prime ] with map ] [ all-unique? ] generate ;
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
USING: kernel sequences math.functions math ;
|
||||||
|
IN: project-euler.100
|
||||||
|
|
||||||
|
: euler100 ( -- n )
|
||||||
|
1 1
|
||||||
|
[ dup dup 1- * 2 * 10 24 ^ <= ]
|
||||||
|
[ tuck 6 * swap - 2 - ] [ ] while nip ;
|
|
@ -0,0 +1,40 @@
|
||||||
|
! Copyright (c) 2008 Eric Mertens
|
||||||
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
|
USING: sequences combinators kernel sequences.lib math assocs namespaces ;
|
||||||
|
IN: project-euler.151
|
||||||
|
|
||||||
|
SYMBOL: table
|
||||||
|
|
||||||
|
: (pick-sheet) ( seq i -- newseq )
|
||||||
|
[
|
||||||
|
<=> sgn
|
||||||
|
{
|
||||||
|
{ -1 [ ] }
|
||||||
|
{ 0 [ 1- ] }
|
||||||
|
{ 1 [ 1+ ] }
|
||||||
|
} case
|
||||||
|
] curry map-index ;
|
||||||
|
|
||||||
|
DEFER: (euler151)
|
||||||
|
|
||||||
|
: pick-sheet ( seq i -- res )
|
||||||
|
2dup swap nth dup zero? [
|
||||||
|
3drop 0
|
||||||
|
] [
|
||||||
|
[ (pick-sheet) (euler151) ] dip *
|
||||||
|
] if ;
|
||||||
|
|
||||||
|
: (euler151) ( x -- y )
|
||||||
|
table get [ {
|
||||||
|
{ { 0 0 0 1 } [ 0 ] }
|
||||||
|
{ { 0 0 1 0 } [ { 0 0 0 1 } (euler151) 1+ ] }
|
||||||
|
{ { 0 1 0 0 } [ { 0 0 1 1 } (euler151) 1+ ] }
|
||||||
|
{ { 1 0 0 0 } [ { 0 1 1 1 } (euler151) 1+ ] }
|
||||||
|
[ [ dup length [ pick-sheet ] with map sum ] [ sum ] bi / ]
|
||||||
|
} case ] cache ;
|
||||||
|
|
||||||
|
: euler151 ( -- n )
|
||||||
|
[
|
||||||
|
H{ } clone table set
|
||||||
|
{ 1 1 1 1 } (euler151)
|
||||||
|
] with-scope ;
|
|
@ -0,0 +1,48 @@
|
||||||
|
! Copyright (c) 2008 Eric Mertens
|
||||||
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
|
USING: kernel sequences sequences.lib math math.functions math.ranges locals ;
|
||||||
|
IN: project-euler.190
|
||||||
|
|
||||||
|
! PROBLEM
|
||||||
|
! -------
|
||||||
|
|
||||||
|
! http://projecteuler.net/index.php?section=problems&id=190
|
||||||
|
|
||||||
|
! Let Sm = (x1, x2, ... , xm) be the m-tuple of positive real numbers
|
||||||
|
! with x1 + x2 + ... + xm = m for which Pm = x1 * x22 * ... * xmm is
|
||||||
|
! maximised.
|
||||||
|
|
||||||
|
! For example, it can be verified that [P10] = 4112 ([ ] is the integer
|
||||||
|
! part function).
|
||||||
|
|
||||||
|
! Find Σ[Pm] for 2 ≤ m ≤ 15.
|
||||||
|
|
||||||
|
! SOLUTION
|
||||||
|
! --------
|
||||||
|
|
||||||
|
! Pm = x1 * x2^2 * x3^3 * ... * xm^m
|
||||||
|
! fm = x1 + x2 + x3 + ... + xm - m = 0
|
||||||
|
! Gm === Pm - L * fm
|
||||||
|
! dG/dx_i = 0 = i * Pm / xi - L
|
||||||
|
! xi = i * Pm / L
|
||||||
|
|
||||||
|
! Sum(i=1 to m) xi = m
|
||||||
|
! Sum(i=1 to m) i * Pm / L = m
|
||||||
|
! Pm / L * Sum(i=1 to m) i = m
|
||||||
|
! Pm / L * m*(m+1)/2 = m
|
||||||
|
! Pm / L = 2 / (m+1)
|
||||||
|
|
||||||
|
! xi = i * (2 / (m+1)) = 2*i/(m+1)
|
||||||
|
|
||||||
|
<PRIVATE
|
||||||
|
|
||||||
|
: PI ( seq quot -- n )
|
||||||
|
[ * ] compose 1 swap reduce ; inline
|
||||||
|
|
||||||
|
PRIVATE>
|
||||||
|
|
||||||
|
:: P_m ( m -- P_m )
|
||||||
|
m [1,b] [| i | 2 i * m 1+ / i ^ ] PI ;
|
||||||
|
|
||||||
|
: euler190 ( -- n )
|
||||||
|
2 15 [a,b] [ P_m truncate ] sigma ;
|
|
@ -5,7 +5,7 @@ IN: rss.tests
|
||||||
: load-news-file ( filename -- feed )
|
: load-news-file ( filename -- feed )
|
||||||
#! Load an news syndication file and process it, returning
|
#! Load an news syndication file and process it, returning
|
||||||
#! it as an feed tuple.
|
#! it as an feed tuple.
|
||||||
utf8 <file-reader> read-feed ;
|
utf8 file-contents read-feed ;
|
||||||
|
|
||||||
[ T{
|
[ T{
|
||||||
feed
|
feed
|
||||||
|
@ -36,7 +36,7 @@ IN: rss.tests
|
||||||
"http://example.org/2005/04/02/atom"
|
"http://example.org/2005/04/02/atom"
|
||||||
"\n <div xmlns=\"http://www.w3.org/1999/xhtml\">\n <p><i>[Update: The Atom draft is finished.]</i></p>\n </div>\n "
|
"\n <div xmlns=\"http://www.w3.org/1999/xhtml\">\n <p><i>[Update: The Atom draft is finished.]</i></p>\n </div>\n "
|
||||||
|
|
||||||
T{ timestamp f 2003 12 13 8 29 29 -4 }
|
T{ timestamp f 2003 12 13 8 29 29 T{ duration f 0 0 0 -4 0 0 } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} ] [ "extra/rss/atom.xml" resource-path load-news-file ] unit-test
|
} ] [ "extra/rss/atom.xml" resource-path load-news-file ] unit-test
|
||||||
|
|
|
@ -23,7 +23,7 @@ C: <entry> entry
|
||||||
[ "link" tag-named children>string ] keep
|
[ "link" tag-named children>string ] keep
|
||||||
[ "description" tag-named children>string ] keep
|
[ "description" tag-named children>string ] keep
|
||||||
f "date" "http://purl.org/dc/elements/1.1/" <name>
|
f "date" "http://purl.org/dc/elements/1.1/" <name>
|
||||||
tag-named dup [ children>string rfc3339>timestamp ] when
|
tag-named dup [ children>string rfc822>timestamp ] when
|
||||||
<entry> ;
|
<entry> ;
|
||||||
|
|
||||||
: rss1.0 ( xml -- feed )
|
: rss1.0 ( xml -- feed )
|
||||||
|
@ -39,7 +39,7 @@ C: <entry> entry
|
||||||
[ "link" tag-named ] keep
|
[ "link" tag-named ] keep
|
||||||
[ "guid" tag-named dupd ? children>string ] keep
|
[ "guid" tag-named dupd ? children>string ] keep
|
||||||
[ "description" tag-named children>string ] keep
|
[ "description" tag-named children>string ] keep
|
||||||
"pubDate" tag-named children>string rfc3339>timestamp <entry> ;
|
"pubDate" tag-named children>string rfc822>timestamp <entry> ;
|
||||||
|
|
||||||
: rss2.0 ( xml -- feed )
|
: rss2.0 ( xml -- feed )
|
||||||
"channel" tag-named
|
"channel" tag-named
|
||||||
|
@ -71,16 +71,12 @@ C: <entry> entry
|
||||||
{ "feed" [ atom1.0 ] }
|
{ "feed" [ atom1.0 ] }
|
||||||
} case ;
|
} case ;
|
||||||
|
|
||||||
: read-feed ( stream -- feed )
|
: read-feed ( string -- feed )
|
||||||
[ read-xml ] with-html-entities xml>feed ;
|
[ string>xml xml>feed ] with-html-entities ;
|
||||||
|
|
||||||
: download-feed ( url -- feed )
|
: download-feed ( url -- feed )
|
||||||
#! Retrieve an news syndication file, return as a feed tuple.
|
#! Retrieve an news syndication file, return as a feed tuple.
|
||||||
http-get-stream swap code>> success? [
|
http-get read-feed ;
|
||||||
read-feed
|
|
||||||
] [
|
|
||||||
dispose "Error retrieving newsfeed file" throw
|
|
||||||
] if ;
|
|
||||||
|
|
||||||
! Atom generation
|
! Atom generation
|
||||||
: simple-tag, ( content name -- )
|
: simple-tag, ( content name -- )
|
||||||
|
|
|
@ -52,7 +52,7 @@ MACRO: firstn ( n -- )
|
||||||
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
|
||||||
: sigma ( seq quot -- n )
|
: sigma ( seq quot -- n )
|
||||||
[ rot slip + ] curry 0 swap reduce ; inline
|
[ + ] compose 0 swap reduce ; inline
|
||||||
|
|
||||||
: count ( seq quot -- n )
|
: count ( seq quot -- n )
|
||||||
[ 1 0 ? ] compose sigma ; inline
|
[ 1 0 ? ] compose sigma ; inline
|
||||||
|
@ -131,6 +131,10 @@ MACRO: firstn ( n -- )
|
||||||
[ find drop [ head-slice ] when* ] curry
|
[ find drop [ head-slice ] when* ] curry
|
||||||
[ dup ] swap compose keep like ;
|
[ dup ] swap compose keep like ;
|
||||||
|
|
||||||
|
: replicate ( seq quot -- newseq )
|
||||||
|
#! quot: ( -- obj )
|
||||||
|
[ drop ] swap compose map ; inline
|
||||||
|
|
||||||
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
|
||||||
<PRIVATE
|
<PRIVATE
|
||||||
|
@ -236,3 +240,6 @@ PRIVATE>
|
||||||
|
|
||||||
: remove-nth ( seq n -- seq' )
|
: remove-nth ( seq n -- seq' )
|
||||||
cut-slice 1 tail-slice append ;
|
cut-slice 1 tail-slice append ;
|
||||||
|
|
||||||
|
: short ( seq n -- seq n' )
|
||||||
|
over length min ; inline
|
||||||
|
|
|
@ -131,7 +131,7 @@ M: email clone
|
||||||
"-" %
|
"-" %
|
||||||
millis #
|
millis #
|
||||||
"@" %
|
"@" %
|
||||||
smtp-domain get %
|
smtp-domain get [ host-name ] unless* %
|
||||||
">" %
|
">" %
|
||||||
] "" make ;
|
] "" make ;
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,13 @@ CLASS: {
|
||||||
{ +name+ "FactorView" }
|
{ +name+ "FactorView" }
|
||||||
{ +protocols+ { "NSTextInput" } }
|
{ +protocols+ { "NSTextInput" } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
! Rendering
|
||||||
|
! Rendering
|
||||||
|
{ "drawRect:" "void" { "id" "SEL" "id" "NSRect" }
|
||||||
|
[ 3drop window relayout-1 ]
|
||||||
|
}
|
||||||
|
|
||||||
! Events
|
! Events
|
||||||
{ "acceptsFirstMouse:" "bool" { "id" "SEL" "id" }
|
{ "acceptsFirstMouse:" "bool" { "id" "SEL" "id" }
|
||||||
[ 3drop 1 ]
|
[ 3drop 1 ]
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
! Copyright (c) 2008 Slava Pestov
|
||||||
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
|
USING: accessors kernel sequences assocs io.files io.sockets
|
||||||
|
namespaces db db.sqlite smtp
|
||||||
|
http.server
|
||||||
|
http.server.db
|
||||||
|
http.server.sessions
|
||||||
|
http.server.auth.login
|
||||||
|
http.server.auth.providers.db
|
||||||
|
http.server.sessions.storage.db
|
||||||
|
http.server.boilerplate
|
||||||
|
http.server.templating.chloe
|
||||||
|
webapps.pastebin
|
||||||
|
webapps.planet
|
||||||
|
webapps.todo ;
|
||||||
|
IN: webapps.factor-website
|
||||||
|
|
||||||
|
: test-db "test.db" resource-path sqlite-db ;
|
||||||
|
|
||||||
|
: factor-template ( path -- template )
|
||||||
|
"resource:extra/webapps/factor-website/" swap ".xml" 3append <chloe> ;
|
||||||
|
|
||||||
|
: <factor-boilerplate> ( responder -- responder' )
|
||||||
|
<login>
|
||||||
|
users-in-db >>users
|
||||||
|
allow-registration
|
||||||
|
allow-password-recovery
|
||||||
|
allow-edit-profile
|
||||||
|
<boilerplate>
|
||||||
|
"page" factor-template >>template
|
||||||
|
<url-sessions>
|
||||||
|
sessions-in-db >>sessions
|
||||||
|
test-db <db-persistence> ;
|
||||||
|
|
||||||
|
: <pastebin-app> ( -- responder )
|
||||||
|
<pastebin> <factor-boilerplate> ;
|
||||||
|
|
||||||
|
: <planet-app> ( -- responder )
|
||||||
|
<planet-factor> <factor-boilerplate> ;
|
||||||
|
|
||||||
|
: <todo-app> ( -- responder )
|
||||||
|
<todo-list> <protected> <factor-boilerplate> ;
|
||||||
|
|
||||||
|
: init-factor-db ( -- )
|
||||||
|
test-db [
|
||||||
|
init-users-table
|
||||||
|
init-sessions-table
|
||||||
|
|
||||||
|
init-pastes-table
|
||||||
|
init-annotations-table
|
||||||
|
|
||||||
|
init-blog-table
|
||||||
|
|
||||||
|
init-todo-table
|
||||||
|
] with-db ;
|
||||||
|
|
||||||
|
: <factor-website> ( -- responder )
|
||||||
|
<dispatcher>
|
||||||
|
<todo-app> "todo" add-responder
|
||||||
|
<pastebin-app> "pastebin" add-responder
|
||||||
|
<planet-app> "planet" add-responder ;
|
||||||
|
|
||||||
|
: init-factor-website ( -- )
|
||||||
|
"factorcode.org" 25 <inet> smtp-server set-global
|
||||||
|
"todo@factorcode.org" lost-password-from set-global
|
||||||
|
|
||||||
|
init-factor-db
|
||||||
|
|
||||||
|
<factor-website> main-responder set-global
|
||||||
|
|
||||||
|
"planet" main-responder get responders>> at start-update-task ;
|
|
@ -10,34 +10,26 @@
|
||||||
<head>
|
<head>
|
||||||
<t:write-title />
|
<t:write-title />
|
||||||
|
|
||||||
<t:write-atom />
|
<t:style include="resource:extra/xmode/code2html/stylesheet.css" />
|
||||||
|
|
||||||
<t:style>
|
<t:style>
|
||||||
|
body, button {
|
||||||
|
font:9pt "Lucida Grande", "Lucida Sans Unicode", verdana, geneva, sans-serif;
|
||||||
|
color:#444;
|
||||||
|
}
|
||||||
|
|
||||||
.link-button {
|
.link-button {
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inline {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
body, button {
|
|
||||||
font:9pt "Lucida Grande", "Lucida Sans Unicode", verdana, geneva, sans-serif;
|
|
||||||
color:#444;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, .link {
|
a, .link {
|
||||||
color: #222;
|
color: #222;
|
||||||
border-bottom:1px dotted #666;
|
border-bottom:1px dotted #666;
|
||||||
text-decoration:none;
|
text-decoration:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 a {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover, .link:hover {
|
a:hover, .link:hover {
|
||||||
border-bottom:1px solid #66a;
|
border-bottom:1px solid #66a;
|
||||||
}
|
}
|
||||||
|
@ -47,15 +39,34 @@
|
||||||
.field-label {
|
.field-label {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inline {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
background-color: #eee;
|
||||||
|
padding: 5px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.big-field-label {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
border: 1px dashed #ccc;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
padding: 5px;
|
||||||
|
font-size: 150%;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
</t:style>
|
</t:style>
|
||||||
|
|
||||||
<t:write-style />
|
<t:write-style />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<h1><t:a href="planet"><t:write-title /></t:a></h1>
|
|
||||||
|
|
||||||
<t:call-next-template />
|
<t:call-next-template />
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version='1.0' ?>
|
||||||
|
|
||||||
|
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
||||||
|
|
||||||
|
<h2>Annotation: <t:view component="summary" /></h2>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr><th class="field-label">Author: </th><td><t:view component="author" /></td></tr>
|
||||||
|
<tr><th class="field-label">Mode: </th><td><t:view component="mode" /></td></tr>
|
||||||
|
<tr><th class="field-label">Date: </th><td><t:view component="date" /></td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="description">
|
||||||
|
<t:view component="contents" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<t:form action="$pastebin/delete-annotation" class="inline">
|
||||||
|
<t:edit component="id" />
|
||||||
|
<t:edit component="aid" />
|
||||||
|
<button class="link-button link">Delete Annotation</button>
|
||||||
|
</t:form>
|
||||||
|
|
||||||
|
</t:chloe>
|
|
@ -0,0 +1 @@
|
||||||
|
Slava Pestov
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version='1.0' ?>
|
||||||
|
|
||||||
|
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
||||||
|
|
||||||
|
<t:title>New Annotation</t:title>
|
||||||
|
|
||||||
|
<t:form action="$pastebin/annotate">
|
||||||
|
<t:edit component="id" />
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr><th class="field-label">Summary: </th><td><t:edit component="summary" /></td></tr>
|
||||||
|
<tr><th class="field-label">Author: </th><td><t:edit component="author" /></td></tr>
|
||||||
|
<tr><th class="field-label">Mode: </th><td><t:edit component="mode" /></td></tr>
|
||||||
|
<tr><th class="field-label big-field-label">Description:</th><td><t:edit component="contents" /></td></tr>
|
||||||
|
<tr><th class="field-label">Captcha: </th><td><t:edit component="captcha" /></td></tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>Leave the captcha blank. Spam-bots will fill it indiscriminantly, so their attempts to register will be blocked.</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<input type="SUBMIT" value="Done" />
|
||||||
|
</t:form>
|
||||||
|
|
||||||
|
</t:chloe>
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version='1.0' ?>
|
||||||
|
|
||||||
|
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
||||||
|
|
||||||
|
<t:title>New Paste</t:title>
|
||||||
|
|
||||||
|
<t:form action="$pastebin/new-paste">
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr><th class="field-label">Summary: </th><td><t:edit component="summary" /></td></tr>
|
||||||
|
<tr><th class="field-label">Author: </th><td><t:edit component="author" /></td></tr>
|
||||||
|
<tr><th class="field-label">Mode: </th><td><t:edit component="mode" /></td></tr>
|
||||||
|
<tr><th class="field-label big-field-label">Description: </th><td><t:edit component="contents" /></td></tr>
|
||||||
|
<tr><th class="field-label">Captcha: </th><td><t:edit component="captcha" /></td></tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>Leave the captcha blank. Spam-bots will fill it indiscriminantly, so their attempts to register will be blocked.</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<input type="SUBMIT" value="Submit" />
|
||||||
|
</t:form>
|
||||||
|
</t:chloe>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version='1.0' ?>
|
||||||
|
|
||||||
|
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
||||||
|
|
||||||
|
<t:title>Pastebin</t:title>
|
||||||
|
|
||||||
|
<table width="100%">
|
||||||
|
<th align="left" width="50%">Summary:</th>
|
||||||
|
<th align="left" width="100">Paste by:</th>
|
||||||
|
<th align="left" width="200">Date:</th>
|
||||||
|
|
||||||
|
<t:summary component="pastes" />
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</t:chloe>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version='1.0' ?>
|
||||||
|
|
||||||
|
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><t:a href="view-paste" query="id"><t:view component="summary" /></t:a></td>
|
||||||
|
<td><t:view component="author" /></td>
|
||||||
|
<td><t:view component="date" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</t:chloe>
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?xml version='1.0' ?>
|
||||||
|
|
||||||
|
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
||||||
|
|
||||||
|
<t:title>Pastebin</t:title>
|
||||||
|
|
||||||
|
<h2>Paste: <t:view component="summary" /></h2>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr><th class="field-label">Author: </th><td><t:view component="author" /></td></tr>
|
||||||
|
<tr><th class="field-label">Mode: </th><td><t:view component="mode" /></td></tr>
|
||||||
|
<tr><th class="field-label">Date: </th><td><t:view component="date" /></td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="description">
|
||||||
|
<t:view component="contents" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<t:form action="$pastebin/delete-paste" class="inline">
|
||||||
|
<t:edit component="id" />
|
||||||
|
<button class="link-button link">Delete Paste</button>
|
||||||
|
</t:form>
|
||||||
|
|
|
||||||
|
<t:a href="$pastebin/annotate" query="id">Annotate</t:a>
|
||||||
|
|
||||||
|
<t:view component="annotations" />
|
||||||
|
</t:chloe>
|
|
@ -0,0 +1,7 @@
|
||||||
|
pre.code {
|
||||||
|
border:1px dashed #ccc;
|
||||||
|
background-color:#f5f5f5;
|
||||||
|
padding:5px;
|
||||||
|
font-size:150%;
|
||||||
|
color:#000000;
|
||||||
|
}
|
|
@ -0,0 +1,253 @@
|
||||||
|
USING: namespaces assocs sorting sequences kernel accessors
|
||||||
|
hashtables sequences.lib locals db.types db.tuples db
|
||||||
|
calendar calendar.format rss xml.writer
|
||||||
|
xmode.catalog
|
||||||
|
http.server
|
||||||
|
http.server.crud
|
||||||
|
http.server.actions
|
||||||
|
http.server.components
|
||||||
|
http.server.components.code
|
||||||
|
http.server.templating.chloe
|
||||||
|
http.server.boilerplate
|
||||||
|
http.server.validators
|
||||||
|
http.server.forms ;
|
||||||
|
IN: webapps.pastebin
|
||||||
|
|
||||||
|
: <mode> ( id -- component )
|
||||||
|
modes keys natural-sort <choice> ;
|
||||||
|
|
||||||
|
: pastebin-template ( name -- template )
|
||||||
|
"resource:extra/webapps/pastebin/" swap ".xml" 3append <chloe> ;
|
||||||
|
|
||||||
|
TUPLE: paste id summary author mode date contents annotations captcha ;
|
||||||
|
|
||||||
|
paste "PASTE"
|
||||||
|
{
|
||||||
|
{ "id" "ID" INTEGER +native-id+ }
|
||||||
|
{ "summary" "SUMMARY" { VARCHAR 256 } +not-null+ }
|
||||||
|
{ "author" "AUTHOR" { VARCHAR 256 } +not-null+ }
|
||||||
|
{ "mode" "MODE" { VARCHAR 256 } +not-null+ }
|
||||||
|
{ "date" "DATE" DATETIME +not-null+ }
|
||||||
|
{ "contents" "CONTENTS" TEXT +not-null+ }
|
||||||
|
} define-persistent
|
||||||
|
|
||||||
|
: <paste> ( id -- paste )
|
||||||
|
paste new
|
||||||
|
swap >>id ;
|
||||||
|
|
||||||
|
: pastes ( -- pastes )
|
||||||
|
f <paste> select-tuples ;
|
||||||
|
|
||||||
|
TUPLE: annotation aid id summary author mode contents date captcha ;
|
||||||
|
|
||||||
|
annotation "ANNOTATION"
|
||||||
|
{
|
||||||
|
{ "aid" "AID" INTEGER +native-id+ }
|
||||||
|
{ "id" "ID" INTEGER +not-null+ }
|
||||||
|
{ "summary" "SUMMARY" { VARCHAR 256 } +not-null+ }
|
||||||
|
{ "author" "AUTHOR" { VARCHAR 256 } +not-null+ }
|
||||||
|
{ "mode" "MODE" { VARCHAR 256 } +not-null+ }
|
||||||
|
{ "date" "DATE" DATETIME +not-null+ }
|
||||||
|
{ "contents" "CONTENTS" TEXT +not-null+ }
|
||||||
|
} define-persistent
|
||||||
|
|
||||||
|
: <annotation> ( id aid -- annotation )
|
||||||
|
annotation new
|
||||||
|
swap >>aid
|
||||||
|
swap >>id ;
|
||||||
|
|
||||||
|
: fetch-annotations ( paste -- paste )
|
||||||
|
dup annotations>> [
|
||||||
|
dup id>> f <annotation> select-tuples >>annotations
|
||||||
|
] unless ;
|
||||||
|
|
||||||
|
: <annotation-form> ( -- form )
|
||||||
|
"paste" <form>
|
||||||
|
"id" <integer>
|
||||||
|
hidden >>renderer
|
||||||
|
add-field
|
||||||
|
"aid" <integer>
|
||||||
|
hidden >>renderer
|
||||||
|
add-field
|
||||||
|
"annotation" pastebin-template >>view-template
|
||||||
|
"summary" <string> add-field
|
||||||
|
"author" <string> add-field
|
||||||
|
"mode" <mode> add-field
|
||||||
|
"contents" "mode" <code> add-field
|
||||||
|
"date" <date> add-field ;
|
||||||
|
|
||||||
|
: <new-annotation-form> ( -- form )
|
||||||
|
"paste" <form>
|
||||||
|
"new-annotation" pastebin-template >>edit-template
|
||||||
|
"id" <integer>
|
||||||
|
hidden >>renderer
|
||||||
|
t >>required add-field
|
||||||
|
"summary" <string>
|
||||||
|
t >>required add-field
|
||||||
|
"author" <string>
|
||||||
|
t >>required
|
||||||
|
add-field
|
||||||
|
"mode" <mode>
|
||||||
|
"factor" >>default
|
||||||
|
t >>required
|
||||||
|
add-field
|
||||||
|
"contents" "mode" <code>
|
||||||
|
t >>required add-field
|
||||||
|
"captcha" <captcha> add-field ;
|
||||||
|
|
||||||
|
: <paste-form> ( -- form )
|
||||||
|
"paste" <form>
|
||||||
|
"paste" pastebin-template >>view-template
|
||||||
|
"paste-summary" pastebin-template >>summary-template
|
||||||
|
"id" <integer>
|
||||||
|
hidden >>renderer add-field
|
||||||
|
"summary" <string> add-field
|
||||||
|
"author" <string> add-field
|
||||||
|
"mode" <mode> add-field
|
||||||
|
"date" <date> add-field
|
||||||
|
"contents" "mode" <code> add-field
|
||||||
|
"annotations" <annotation-form> +plain+ <list> add-field ;
|
||||||
|
|
||||||
|
: <new-paste-form> ( -- form )
|
||||||
|
"paste" <form>
|
||||||
|
"new-paste" pastebin-template >>edit-template
|
||||||
|
"summary" <string>
|
||||||
|
t >>required add-field
|
||||||
|
"author" <string>
|
||||||
|
t >>required add-field
|
||||||
|
"mode" <mode>
|
||||||
|
"factor" >>default
|
||||||
|
t >>required
|
||||||
|
add-field
|
||||||
|
"contents" "mode" <code>
|
||||||
|
t >>required add-field
|
||||||
|
"captcha" <captcha> add-field ;
|
||||||
|
|
||||||
|
: <paste-list-form> ( -- form )
|
||||||
|
"pastebin" <form>
|
||||||
|
"paste-list" pastebin-template >>view-template
|
||||||
|
"pastes" <paste-form> +plain+ <list> add-field ;
|
||||||
|
|
||||||
|
:: <paste-list-action> ( -- action )
|
||||||
|
[let | form [ <paste-list-form> ] |
|
||||||
|
<action>
|
||||||
|
[
|
||||||
|
blank-values
|
||||||
|
|
||||||
|
pastes "pastes" set-value
|
||||||
|
|
||||||
|
form view-form
|
||||||
|
] >>display
|
||||||
|
] ;
|
||||||
|
|
||||||
|
:: <annotate-action> ( form ctor next -- action )
|
||||||
|
<action>
|
||||||
|
{ { "id" [ v-number ] } } >>get-params
|
||||||
|
|
||||||
|
[
|
||||||
|
"id" get f ctor call
|
||||||
|
|
||||||
|
from-tuple form set-defaults
|
||||||
|
] >>init
|
||||||
|
|
||||||
|
[ form edit-form ] >>display
|
||||||
|
|
||||||
|
[
|
||||||
|
f f ctor call from-tuple
|
||||||
|
|
||||||
|
form validate-form
|
||||||
|
|
||||||
|
values-tuple insert-tuple
|
||||||
|
|
||||||
|
"id" value next <id-redirect>
|
||||||
|
] >>submit ;
|
||||||
|
|
||||||
|
: pastebin-feed-entries ( -- entries )
|
||||||
|
pastes <reversed> 20 short head [
|
||||||
|
[ summary>> ]
|
||||||
|
[ "$pastebin/view-paste" swap id>> "id" associate link>string ]
|
||||||
|
[ date>> ] tri
|
||||||
|
f swap <entry>
|
||||||
|
] map ;
|
||||||
|
|
||||||
|
: pastebin-feed ( -- feed )
|
||||||
|
feed new
|
||||||
|
"Factor Pastebin" >>title
|
||||||
|
"http://paste.factorcode.org" >>link
|
||||||
|
pastebin-feed-entries >>entries ;
|
||||||
|
|
||||||
|
: <feed-action> ( -- action )
|
||||||
|
<action>
|
||||||
|
[
|
||||||
|
"text/xml" <content>
|
||||||
|
[ pastebin-feed feed>xml write-xml ] >>body
|
||||||
|
] >>display ;
|
||||||
|
|
||||||
|
:: <view-paste-action> ( form ctor -- action )
|
||||||
|
<action>
|
||||||
|
{ { "id" [ v-number ] } } >>get-params
|
||||||
|
|
||||||
|
[ "id" get ctor call select-tuple fetch-annotations from-tuple ] >>init
|
||||||
|
|
||||||
|
[ form view-form ] >>display ;
|
||||||
|
|
||||||
|
:: <delete-paste-action> ( ctor next -- action )
|
||||||
|
<action>
|
||||||
|
{ { "id" [ v-number ] } } >>post-params
|
||||||
|
|
||||||
|
[
|
||||||
|
"id" get ctor call delete-tuple
|
||||||
|
|
||||||
|
"id" get f <annotation> select-tuples [ delete-tuple ] each
|
||||||
|
|
||||||
|
next f <permanent-redirect>
|
||||||
|
] >>submit ;
|
||||||
|
|
||||||
|
:: <delete-annotation-action> ( ctor next -- action )
|
||||||
|
<action>
|
||||||
|
{ { "id" [ v-number ] } { "aid" [ v-number ] } } >>post-params
|
||||||
|
|
||||||
|
[
|
||||||
|
"id" get "aid" get ctor call delete-tuple
|
||||||
|
|
||||||
|
"id" get next <id-redirect>
|
||||||
|
] >>submit ;
|
||||||
|
|
||||||
|
:: <new-paste-action> ( form ctor next -- action )
|
||||||
|
<action>
|
||||||
|
[
|
||||||
|
f ctor call from-tuple
|
||||||
|
|
||||||
|
form set-defaults
|
||||||
|
] >>init
|
||||||
|
|
||||||
|
[ form edit-form ] >>display
|
||||||
|
|
||||||
|
[
|
||||||
|
f ctor call from-tuple
|
||||||
|
|
||||||
|
form validate-form
|
||||||
|
|
||||||
|
values-tuple insert-tuple
|
||||||
|
|
||||||
|
"id" value next <id-redirect>
|
||||||
|
] >>submit ;
|
||||||
|
|
||||||
|
TUPLE: pastebin < dispatcher ;
|
||||||
|
|
||||||
|
: <pastebin> ( -- responder )
|
||||||
|
pastebin new-dispatcher
|
||||||
|
<paste-list-action> "list" add-main-responder
|
||||||
|
<feed-action> "feed.xml" add-responder
|
||||||
|
<paste-form> [ <paste> ] <view-paste-action> "view-paste" add-responder
|
||||||
|
[ <paste> ] "$pastebin/list" <delete-paste-action> "delete-paste" add-responder
|
||||||
|
[ <annotation> ] "$pastebin/view-paste" <delete-annotation-action> "delete-annotation" add-responder
|
||||||
|
<paste-form> [ <paste> ] <view-paste-action> "$pastebin/view-paste" add-responder
|
||||||
|
<new-paste-form> [ <paste> now >>date ] "$pastebin/view-paste" <new-paste-action> "new-paste" add-responder
|
||||||
|
<new-annotation-form> [ <annotation> now >>date ] "$pastebin/view-paste" <annotate-action> "annotate" add-responder
|
||||||
|
<boilerplate>
|
||||||
|
"pastebin" pastebin-template >>template ;
|
||||||
|
|
||||||
|
: init-pastes-table paste ensure-table ;
|
||||||
|
|
||||||
|
: init-annotations-table annotation ensure-table ;
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?xml version='1.0' ?>
|
||||||
|
|
||||||
|
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
||||||
|
|
||||||
|
<t:atom title="Pastebin - Atom" href="$pastebin/feed.xml" />
|
||||||
|
|
||||||
|
<t:style include="resource:extra/webapps/pastebin/pastebin.css" />
|
||||||
|
|
||||||
|
<div class="navbar">
|
||||||
|
<t:a href="$pastebin/list">Pastes</t:a>
|
||||||
|
| <t:a href="$pastebin/new-paste">New Paste</t:a>
|
||||||
|
| <t:a href="$pastebin/feed.xml">Atom Feed</t:a>
|
||||||
|
|
||||||
|
<t:comment>
|
||||||
|
<t:if code="http.server.auth.login:allow-edit-profile?">
|
||||||
|
| <t:a href="$login/edit-profile">Edit Profile</t:a>
|
||||||
|
</t:if>
|
||||||
|
|
||||||
|
<t:form action="$login/logout" class="inline">
|
||||||
|
| <button type="submit" class="link-button link">Logout</button>
|
||||||
|
</t:form>
|
||||||
|
</t:comment>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1><t:write-title /></h1>
|
||||||
|
|
||||||
|
<t:call-next-template />
|
||||||
|
|
||||||
|
</t:chloe>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version='1.0' ?>
|
||||||
|
|
||||||
|
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
||||||
|
|
||||||
|
<t:title>Planet Factor Administration</t:title>
|
||||||
|
|
||||||
|
<t:summary component="blogroll" />
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<t:a href="$planet-factor/admin/edit-blog">Add Blog</t:a>
|
||||||
|
| <t:a href="$planet-factor/admin/update">Update</t:a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</t:chloe>
|
|
@ -2,6 +2,6 @@
|
||||||
|
|
||||||
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
||||||
|
|
||||||
<t:a href="view-blog" query="id"><t:view component="name" /></t:a>
|
<t:a href="$planet-factor/admin/edit-blog" query="id"><t:view component="name" /></t:a>
|
||||||
|
|
||||||
</t:chloe>
|
</t:chloe>
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
<t:title>Edit Blog</t:title>
|
<t:title>Edit Blog</t:title>
|
||||||
|
|
||||||
<t:form action="edit-blog">
|
<t:form action="$planet-factor/admin/edit-blog">
|
||||||
|
|
||||||
<t:edit component="id" />
|
<t:edit component="id" />
|
||||||
|
|
||||||
|
@ -21,8 +21,8 @@
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th class="field-label">Atom feed:</th>
|
<th class="field-label">Feed:</th>
|
||||||
<td><t:edit component="atom-url" /></td>
|
<td><t:edit component="feed-url" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
@ -31,9 +31,7 @@
|
||||||
|
|
||||||
</t:form>
|
</t:form>
|
||||||
|
|
||||||
<t:a href="view" query="id">View</t:a>
|
<t:form action="$planet-factor/admin/delete-blog" class="inline">
|
||||||
|
|
|
||||||
<t:form action="delete-blog" class="inline">
|
|
||||||
<t:edit component="id" />
|
<t:edit component="id" />
|
||||||
<button type="submit" class="link-button link">Delete</button>
|
<button type="submit" class="link-button link">Delete</button>
|
||||||
</t:form>
|
</t:form>
|
||||||
|
|
|
@ -2,8 +2,16 @@
|
||||||
|
|
||||||
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
||||||
|
|
||||||
<h2 class="posting-title"><t:view component="title" /></h2>
|
<h2 class="posting-title">
|
||||||
<p class="posting-body"> <t:view component="description" /> </p>
|
<t:a value="link"><t:view component="title" /></t:a>
|
||||||
<p class="posting-date"> <t:view component="pub-date" /> </p>
|
</h2>
|
||||||
|
|
||||||
|
<p class="posting-body">
|
||||||
|
<t:view component="description" />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="posting-date">
|
||||||
|
<t:a value="link"><t:view component="pub-date" /></t:a>
|
||||||
|
</p>
|
||||||
|
|
||||||
</t:chloe>
|
</t:chloe>
|
||||||
|
|
|
@ -1,8 +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: kernel accessors sequences sorting locals math
|
USING: kernel accessors sequences sorting locals math
|
||||||
calendar alarms logging concurrency.combinators
|
calendar alarms logging concurrency.combinators namespaces
|
||||||
db.types db.tuples db
|
sequences.lib db.types db.tuples db
|
||||||
rss xml.writer
|
rss xml.writer
|
||||||
http.server
|
http.server
|
||||||
http.server.crud
|
http.server.crud
|
||||||
|
@ -10,17 +10,27 @@ http.server.forms
|
||||||
http.server.actions
|
http.server.actions
|
||||||
http.server.boilerplate
|
http.server.boilerplate
|
||||||
http.server.templating.chloe
|
http.server.templating.chloe
|
||||||
http.server.components ;
|
http.server.components
|
||||||
|
http.server.auth.login ;
|
||||||
IN: webapps.planet
|
IN: webapps.planet
|
||||||
|
|
||||||
TUPLE: blog id name www-url atom-url ;
|
TUPLE: planet-factor < dispatcher postings ;
|
||||||
|
|
||||||
|
: planet-template ( name -- template )
|
||||||
|
"resource:extra/webapps/planet/" swap ".xml" 3append <chloe> ;
|
||||||
|
|
||||||
|
TUPLE: blog id name www-url feed-url ;
|
||||||
|
|
||||||
|
M: blog link-title name>> ;
|
||||||
|
|
||||||
|
M: blog link-href www-url>> ;
|
||||||
|
|
||||||
blog "BLOGS"
|
blog "BLOGS"
|
||||||
{
|
{
|
||||||
{ "id" "ID" INTEGER +native-id+ }
|
{ "id" "ID" INTEGER +native-id+ }
|
||||||
{ "name" "NAME" { VARCHAR 256 } +not-null+ }
|
{ "name" "NAME" { VARCHAR 256 } +not-null+ }
|
||||||
{ "www-url" "WWWURL" { VARCHAR 256 } +not-null+ }
|
{ "www-url" "WWWURL" { VARCHAR 256 } +not-null+ }
|
||||||
{ "atom-url" "ATOMURL" { VARCHAR 256 } +not-null+ }
|
{ "feed-url" "FEEDURL" { VARCHAR 256 } +not-null+ }
|
||||||
} define-persistent
|
} define-persistent
|
||||||
|
|
||||||
: init-blog-table blog ensure-table ;
|
: init-blog-table blog ensure-table ;
|
||||||
|
@ -29,8 +39,8 @@ blog "BLOGS"
|
||||||
blog new
|
blog new
|
||||||
swap >>id ;
|
swap >>id ;
|
||||||
|
|
||||||
: planet-template ( name -- template )
|
: blogroll ( -- seq )
|
||||||
"resource:extra/webapps/planet/" swap ".xml" 3append <chloe> ;
|
f <blog> select-tuples [ [ name>> ] compare ] sort ;
|
||||||
|
|
||||||
: <entry-form> ( -- form )
|
: <entry-form> ( -- form )
|
||||||
"entry" <form>
|
"entry" <form>
|
||||||
|
@ -43,8 +53,7 @@ blog "BLOGS"
|
||||||
: <blog-form> ( -- form )
|
: <blog-form> ( -- form )
|
||||||
"blog" <form>
|
"blog" <form>
|
||||||
"edit-blog" planet-template >>edit-template
|
"edit-blog" planet-template >>edit-template
|
||||||
"view-blog" planet-template >>view-template
|
"blog-admin-link" planet-template >>summary-template
|
||||||
"blog-summary" planet-template >>summary-template
|
|
||||||
"id" <integer>
|
"id" <integer>
|
||||||
hidden >>renderer
|
hidden >>renderer
|
||||||
add-field
|
add-field
|
||||||
|
@ -54,21 +63,33 @@ blog "BLOGS"
|
||||||
"www-url" <url>
|
"www-url" <url>
|
||||||
t >>required
|
t >>required
|
||||||
add-field
|
add-field
|
||||||
"atom-url" <url>
|
"feed-url" <url>
|
||||||
t >>required
|
t >>required
|
||||||
add-field ;
|
add-field ;
|
||||||
|
|
||||||
: <planet-factor-form> ( -- form )
|
: <planet-factor-form> ( -- form )
|
||||||
"planet-factor" <form>
|
"planet-factor" <form>
|
||||||
"planet" planet-template >>view-template
|
"postings" planet-template >>view-template
|
||||||
"mini-planet" planet-template >>summary-template
|
"postings-summary" planet-template >>summary-template
|
||||||
"postings" <entry-form> +plain+ <list> add-field
|
"postings" <entry-form> +plain+ <list> add-field
|
||||||
|
"blogroll" "blog" <link> +unordered+ <list> add-field ;
|
||||||
|
|
||||||
|
: <admin-form> ( -- form )
|
||||||
|
"admin" <form>
|
||||||
|
"admin" planet-template >>view-template
|
||||||
"blogroll" <blog-form> +unordered+ <list> add-field ;
|
"blogroll" <blog-form> +unordered+ <list> add-field ;
|
||||||
|
|
||||||
: blogroll ( -- seq )
|
:: <edit-blogroll-action> ( planet -- action )
|
||||||
f <blog> select-tuples [ [ name>> ] compare ] sort ;
|
[let | form [ <admin-form> ] |
|
||||||
|
<action>
|
||||||
|
[
|
||||||
|
blank-values
|
||||||
|
|
||||||
TUPLE: planet-factor < dispatcher postings ;
|
blogroll "blogroll" set-value
|
||||||
|
|
||||||
|
form view-form
|
||||||
|
] >>display
|
||||||
|
] ;
|
||||||
|
|
||||||
:: <planet-action> ( planet -- action )
|
:: <planet-action> ( planet -- action )
|
||||||
[let | form [ <planet-factor-form> ] |
|
[let | form [ <planet-factor-form> ] |
|
||||||
|
@ -83,14 +104,11 @@ TUPLE: planet-factor < dispatcher postings ;
|
||||||
] >>display
|
] >>display
|
||||||
] ;
|
] ;
|
||||||
|
|
||||||
: safe-head ( seq n -- seq' )
|
|
||||||
over length min head ;
|
|
||||||
|
|
||||||
:: planet-feed ( planet -- feed )
|
:: planet-feed ( planet -- feed )
|
||||||
feed new
|
feed new
|
||||||
"[ planet-factor ]" >>title
|
"Planet Factor" >>title
|
||||||
"http://planet.factorcode.org" >>link
|
"http://planet.factorcode.org" >>link
|
||||||
planet postings>> 30 safe-head >>entries ;
|
planet postings>> 16 short head >>entries ;
|
||||||
|
|
||||||
:: <feed-action> ( planet -- action )
|
:: <feed-action> ( planet -- action )
|
||||||
<action>
|
<action>
|
||||||
|
@ -109,7 +127,7 @@ TUPLE: planet-factor < dispatcher postings ;
|
||||||
|
|
||||||
: fetch-blogroll ( blogroll -- entries )
|
: fetch-blogroll ( blogroll -- entries )
|
||||||
dup
|
dup
|
||||||
[ atom-url>> fetch-feed ] parallel-map
|
[ feed-url>> fetch-feed ] parallel-map
|
||||||
[ >r name>> r> [ <posting> ] with map ] 2map concat ;
|
[ >r name>> r> [ <posting> ] with map ] 2map concat ;
|
||||||
|
|
||||||
: sort-entries ( entries -- entries' )
|
: sort-entries ( entries -- entries' )
|
||||||
|
@ -117,7 +135,8 @@ TUPLE: planet-factor < dispatcher postings ;
|
||||||
|
|
||||||
: update-cached-postings ( planet -- )
|
: update-cached-postings ( planet -- )
|
||||||
"webapps.planet" [
|
"webapps.planet" [
|
||||||
blogroll fetch-blogroll sort-entries >>postings drop
|
blogroll fetch-blogroll sort-entries 8 short head
|
||||||
|
>>postings drop
|
||||||
] with-logging ;
|
] with-logging ;
|
||||||
|
|
||||||
:: <update-action> ( planet -- action )
|
:: <update-action> ( planet -- action )
|
||||||
|
@ -127,48 +146,26 @@ TUPLE: planet-factor < dispatcher postings ;
|
||||||
"" f <temporary-redirect>
|
"" f <temporary-redirect>
|
||||||
] >>display ;
|
] >>display ;
|
||||||
|
|
||||||
: start-update-task ( planet -- )
|
:: <planet-factor-admin> ( planet-factor -- responder )
|
||||||
[ update-cached-postings ] curry 10 minutes every drop ;
|
|
||||||
|
|
||||||
:: <planet-factor> ( -- responder )
|
|
||||||
[let | blog-form [ <blog-form> ]
|
[let | blog-form [ <blog-form> ]
|
||||||
blog-ctor [ [ <blog> ] ] |
|
blog-ctor [ [ <blog> ] ] |
|
||||||
planet-factor new-dispatcher
|
<dispatcher>
|
||||||
dup <planet-action> >>default
|
planet-factor <edit-blogroll-action> >>default
|
||||||
dup <feed-action> "feed.xml" add-responder
|
|
||||||
dup <update-action> "update" add-responder
|
planet-factor <update-action> "update" add-responder
|
||||||
|
|
||||||
! Administrative CRUD
|
! Administrative CRUD
|
||||||
blog-ctor "" <delete-action> "delete-blog" add-responder
|
blog-ctor "$planet-factor/admin" <delete-action> "delete-blog" add-responder
|
||||||
blog-form blog-ctor <view-action> "view-blog" add-responder
|
blog-form blog-ctor "$planet-factor/admin" <edit-action> "edit-blog" add-responder
|
||||||
blog-form blog-ctor "view-blog" <edit-action> "edit-blog" add-responder
|
|
||||||
] ;
|
] ;
|
||||||
|
|
||||||
USING: namespaces io.files io.sockets
|
: <planet-factor> ( -- responder )
|
||||||
db.sqlite smtp
|
planet-factor new-dispatcher
|
||||||
http.server.db
|
dup <planet-action> "list" add-main-responder
|
||||||
http.server.sessions
|
dup <feed-action> "feed.xml" add-responder
|
||||||
http.server.auth.login
|
dup <planet-factor-admin> <protected> "admin" add-responder
|
||||||
http.server.auth.providers.db
|
|
||||||
http.server.sessions.storage.db ;
|
|
||||||
|
|
||||||
: test-db "planet.db" resource-path sqlite-db ;
|
|
||||||
|
|
||||||
: <planet-app> ( -- responder )
|
|
||||||
<planet-factor>
|
|
||||||
<boilerplate>
|
<boilerplate>
|
||||||
"page" planet-template >>template
|
"planet" planet-template >>template ;
|
||||||
! <url-sessions>
|
|
||||||
! sessions-in-db >>sessions
|
|
||||||
test-db <db-persistence> ;
|
|
||||||
|
|
||||||
: init-planet ( -- )
|
: start-update-task ( planet -- )
|
||||||
! test-db [
|
[ update-cached-postings ] curry 10 minutes every drop ;
|
||||||
! init-blog-table
|
|
||||||
! init-users-table
|
|
||||||
! init-sessions-table
|
|
||||||
! ] with-db
|
|
||||||
|
|
||||||
<dispatcher>
|
|
||||||
<planet-app> "planet" add-responder
|
|
||||||
main-responder set-global ;
|
|
||||||
|
|
|
@ -2,36 +2,29 @@
|
||||||
|
|
||||||
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
||||||
|
|
||||||
<t:title>Planet Factor</t:title>
|
<t:comment>
|
||||||
|
<t:atom title="Planet Factor - Atom" href="$planet/feed.xml" />
|
||||||
<t:atom title="Planet Factor - Atom" href="feed.xml" />
|
</t:comment>
|
||||||
|
|
||||||
<t:style include="resource:extra/webapps/planet/planet.css" />
|
<t:style include="resource:extra/webapps/planet/planet.css" />
|
||||||
|
|
||||||
<table width="100%" cellpadding="10">
|
<div class="navbar">
|
||||||
<tr>
|
<t:a href="$planet-factor/list">Front Page</t:a>
|
||||||
<td> <t:view component="postings" /> </td>
|
| <t:a href="$planet-factor/feed.xml">Atom Feed</t:a>
|
||||||
|
| <t:a href="$planet-factor/admin">Admin</t:a>
|
||||||
|
|
||||||
<td valign="top" width="25%" class="infobox">
|
<t:comment>
|
||||||
<p>
|
<t:if code="http.server.auth.login:allow-edit-profile?">
|
||||||
<strong>planet-factor</strong> is an Atom feed aggregator that collects the
|
| <t:a href="$login/edit-profile">Edit Profile</t:a>
|
||||||
contents of <a href="http://factorcode.org/">Factor</a>-related blogs. It was inspired by
|
</t:if>
|
||||||
<a href="http://planet.lisp.org">Planet Lisp</a>.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<img src="http://planet.lisp.org/feed-icon-14x14.png" />
|
|
||||||
<a href="feed.xml"> Syndicate </a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>Blogroll</h2>
|
<t:form action="$login/logout" class="inline">
|
||||||
|
| <button type="submit" class="link-button link">Logout</button>
|
||||||
|
</t:form>
|
||||||
|
</t:comment>
|
||||||
|
</div>
|
||||||
|
|
||||||
<t:summary component="blogroll" />
|
<h1><t:write-title /></h1>
|
||||||
|
|
||||||
Admin: <t:a href="edit-blog">Add Blog</t:a>
|
<t:call-next-template />
|
||||||
|
|
|
||||||
<t:a href="update">Update</t:a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</t:chloe>
|
</t:chloe>
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version='1.0' ?>
|
||||||
|
|
||||||
|
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
||||||
|
|
||||||
|
<t:title>Planet Factor</t:title>
|
||||||
|
|
||||||
|
<table width="100%" cellpadding="10">
|
||||||
|
<tr>
|
||||||
|
<td> <t:view component="postings" /> </td>
|
||||||
|
|
||||||
|
<td valign="top" width="25%" class="infobox">
|
||||||
|
<h2>Blogroll</h2>
|
||||||
|
|
||||||
|
<t:summary component="blogroll" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</t:chloe>
|
|
@ -1,41 +0,0 @@
|
||||||
<?xml version='1.0' ?>
|
|
||||||
|
|
||||||
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
|
||||||
|
|
||||||
<t:title>View Blog</t:title>
|
|
||||||
|
|
||||||
<table>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<th class="field-label">Blog name:</th>
|
|
||||||
<td><t:view component="name" /></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<th class="field-label">Home page:</th>
|
|
||||||
<td>
|
|
||||||
<t:a value="www-url">
|
|
||||||
<t:view component="www-url" />
|
|
||||||
</t:a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<th class="field-label">Atom feed:</th>
|
|
||||||
<td>
|
|
||||||
<t:a value="atom-url">
|
|
||||||
<t:view component="atom-url" />
|
|
||||||
</t:a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<t:a href="edit-blog" query="id">Edit</t:a>
|
|
||||||
|
|
|
||||||
<t:form action="delete-blog" class="inline">
|
|
||||||
<t:edit component="id" />
|
|
||||||
<button type="submit" class="link-button link">Delete</button>
|
|
||||||
</t:form>
|
|
||||||
|
|
||||||
</t:chloe>
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
<t:title>Edit Item</t:title>
|
<t:title>Edit Item</t:title>
|
||||||
|
|
||||||
<t:form action="edit">
|
<t:form action="$todo-list/edit">
|
||||||
<t:edit component="id" />
|
<t:edit component="id" />
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
|
@ -16,9 +16,9 @@
|
||||||
<input type="SUBMIT" value="Done" />
|
<input type="SUBMIT" value="Done" />
|
||||||
</t:form>
|
</t:form>
|
||||||
|
|
||||||
<t:a href="view" query="id">View</t:a>
|
<t:a href="$todo-list/view" query="id">View</t:a>
|
||||||
|
|
|
|
||||||
<t:form action="delete" class="inline">
|
<t:form action="$todo-list/delete" class="inline">
|
||||||
<t:edit component="id" />
|
<t:edit component="id" />
|
||||||
<button type="submit" class="link-button link">Delete</button>
|
<button type="submit" class="link-button link">Delete</button>
|
||||||
</t:form>
|
</t:form>
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
<?xml version='1.0' ?>
|
|
||||||
|
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
|
||||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
||||||
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
||||||
|
|
||||||
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<t:write-title />
|
|
||||||
|
|
||||||
<t:style>
|
|
||||||
body, button {
|
|
||||||
font:9pt "Lucida Grande", "Lucida Sans Unicode", verdana, geneva, sans-serif;
|
|
||||||
color:#444;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, .link {
|
|
||||||
color: #222;
|
|
||||||
border-bottom:1px dotted #666;
|
|
||||||
text-decoration:none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover, .link:hover {
|
|
||||||
border-bottom:1px solid #66a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error { color: #a00; }
|
|
||||||
|
|
||||||
.field-label {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
</t:style>
|
|
||||||
|
|
||||||
<t:write-style />
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<t:call-next-template />
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</t:chloe>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -10,10 +10,10 @@
|
||||||
<t:view component="priority" />
|
<t:view component="priority" />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<t:a href="view" query="id">View</t:a>
|
<t:a href="$todo-list/view" query="id">View</t:a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<t:a href="edit" query="id">Edit</t:a>
|
<t:a href="$todo-list/edit" query="id">Edit</t:a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
|
|
@ -1,31 +1,3 @@
|
||||||
.big-field-label {
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description {
|
|
||||||
border: 1px dashed #ccc;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
padding: 5px;
|
|
||||||
font-size: 150%;
|
|
||||||
color: #000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link-button {
|
|
||||||
padding: 0px;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar {
|
|
||||||
background-color: #eeeeee;
|
|
||||||
padding: 5px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inline {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
font-size: 75%;
|
font-size: 75%;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
! 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 locals sequences
|
USING: accessors kernel locals sequences namespaces
|
||||||
db db.types db.tuples
|
db db.types db.tuples
|
||||||
http.server.components http.server.components.farkup
|
http.server.components http.server.components.farkup
|
||||||
http.server.forms http.server.templating.chloe
|
http.server.forms http.server.templating.chloe
|
||||||
http.server.boilerplate http.server.crud http.server.auth
|
http.server.boilerplate http.server.crud http.server.auth
|
||||||
http.server.actions http.server.db
|
http.server.actions http.server.db
|
||||||
|
http.server.auth.login
|
||||||
http.server ;
|
http.server ;
|
||||||
IN: webapps.todo
|
IN: webapps.todo
|
||||||
|
|
||||||
|
@ -56,58 +57,17 @@ todo "TODO"
|
||||||
"list" <todo-form> +plain+ <list>
|
"list" <todo-form> +plain+ <list>
|
||||||
add-field ;
|
add-field ;
|
||||||
|
|
||||||
TUPLE: todo-responder < dispatcher ;
|
TUPLE: todo-list < dispatcher ;
|
||||||
|
|
||||||
:: <todo-responder> ( -- responder )
|
:: <todo-list> ( -- responder )
|
||||||
[let | todo-form [ <todo-form> ]
|
[let | todo-form [ <todo-form> ]
|
||||||
list-form [ <todo-list-form> ]
|
list-form [ <todo-list-form> ]
|
||||||
ctor [ [ <todo> ] ] |
|
ctor [ [ <todo> ] ] |
|
||||||
todo-responder new-dispatcher
|
todo-list new-dispatcher
|
||||||
list-form ctor <list-action> "list" add-main-responder
|
list-form ctor <list-action> "list" add-main-responder
|
||||||
todo-form ctor <view-action> "view" add-responder
|
todo-form ctor <view-action> "view" add-responder
|
||||||
todo-form ctor "view" <edit-action> "edit" add-responder
|
todo-form ctor "$todo-list/view" <edit-action> "edit" add-responder
|
||||||
ctor "list" <delete-action> "delete" add-responder
|
ctor "$todo-list/list" <delete-action> "delete" add-responder
|
||||||
<boilerplate>
|
<boilerplate>
|
||||||
"todo" todo-template >>template
|
"todo" todo-template >>template
|
||||||
] ;
|
] ;
|
||||||
|
|
||||||
! What follows below is somewhat akin to a 'deployment descriptor'
|
|
||||||
! for the todo application. The <todo-responder> can be integrated
|
|
||||||
! into an existing web app that provides session management and
|
|
||||||
! login facilities, or <todo-app> can be used to run a
|
|
||||||
! self-contained todo instance.
|
|
||||||
USING: namespaces io.files io.sockets
|
|
||||||
db.sqlite smtp
|
|
||||||
http.server.sessions
|
|
||||||
http.server.auth.login
|
|
||||||
http.server.auth.providers.db
|
|
||||||
http.server.sessions.storage.db ;
|
|
||||||
|
|
||||||
: test-db "todo.db" resource-path sqlite-db ;
|
|
||||||
|
|
||||||
: <todo-app> ( -- responder )
|
|
||||||
<todo-responder>
|
|
||||||
<login>
|
|
||||||
users-in-db >>users
|
|
||||||
allow-registration
|
|
||||||
allow-password-recovery
|
|
||||||
allow-edit-profile
|
|
||||||
<boilerplate>
|
|
||||||
"page" todo-template >>template
|
|
||||||
<url-sessions>
|
|
||||||
sessions-in-db >>sessions
|
|
||||||
test-db <db-persistence> ;
|
|
||||||
|
|
||||||
: init-todo ( -- )
|
|
||||||
"factorcode.org" 25 <inet> smtp-server set-global
|
|
||||||
"todo@factorcode.org" lost-password-from set-global
|
|
||||||
|
|
||||||
test-db [
|
|
||||||
init-todo-table
|
|
||||||
init-users-table
|
|
||||||
init-sessions-table
|
|
||||||
] with-db
|
|
||||||
|
|
||||||
<dispatcher>
|
|
||||||
<todo-app> "todo" add-responder
|
|
||||||
main-responder set-global ;
|
|
||||||
|
|
|
@ -4,17 +4,15 @@
|
||||||
|
|
||||||
<t:style include="resource:extra/webapps/todo/todo.css" />
|
<t:style include="resource:extra/webapps/todo/todo.css" />
|
||||||
|
|
||||||
<t:style include="resource:extra/xmode/code2html/stylesheet.css" />
|
|
||||||
|
|
||||||
<div class="navbar">
|
<div class="navbar">
|
||||||
<t:a href="list">List Items</t:a>
|
<t:a href="$todo-list/list">List Items</t:a>
|
||||||
| <t:a href="edit">Add Item</t:a>
|
| <t:a href="$todo-list/edit">Add Item</t:a>
|
||||||
|
|
||||||
<t:if code="http.server.auth.login:allow-edit-profile?">
|
<t:if code="http.server.auth.login:allow-edit-profile?">
|
||||||
| <t:a href="edit-profile">Edit Profile</t:a>
|
| <t:a href="$login/edit-profile">Edit Profile</t:a>
|
||||||
</t:if>
|
</t:if>
|
||||||
|
|
||||||
<t:form action="logout" class="inline">
|
<t:form action="$login/logout" class="inline">
|
||||||
| <button type="submit" class="link-button link">Logout</button>
|
| <button type="submit" class="link-button link">Logout</button>
|
||||||
</t:form>
|
</t:form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,9 +13,9 @@
|
||||||
<t:view component="description" />
|
<t:view component="description" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<t:a href="edit" query="id">Edit</t:a>
|
<t:a href="$todo-list/edit" query="id">Edit</t:a>
|
||||||
|
|
|
|
||||||
<t:form action="delete" class="inline">
|
<t:form action="$todo-list/delete" class="inline">
|
||||||
<t:edit component="id" />
|
<t:edit component="id" />
|
||||||
<button class="link-button link">Delete</button>
|
<button class="link-button link">Delete</button>
|
||||||
</t:form>
|
</t:form>
|
||||||
|
|
|
@ -30,10 +30,10 @@ FUNCTION: void* error_message ( DWORD id ) ;
|
||||||
: win32-error ( -- )
|
: win32-error ( -- )
|
||||||
GetLastError (win32-error) ;
|
GetLastError (win32-error) ;
|
||||||
|
|
||||||
: win32-error=0/f { 0 f } member? [ win32-error ] when ;
|
: win32-error=0/f ( n -- ) { 0 f } member? [ win32-error ] when ;
|
||||||
: win32-error>0 0 > [ win32-error ] when ;
|
: win32-error>0 ( n -- ) 0 > [ win32-error ] when ;
|
||||||
: win32-error<0 0 < [ win32-error ] when ;
|
: win32-error<0 ( n -- ) 0 < [ win32-error ] when ;
|
||||||
: win32-error<>0 zero? [ win32-error ] unless ;
|
: win32-error<>0 ( n -- ) zero? [ win32-error ] unless ;
|
||||||
|
|
||||||
: invalid-handle? ( handle -- )
|
: invalid-handle? ( handle -- )
|
||||||
INVALID_HANDLE_VALUE = [
|
INVALID_HANDLE_VALUE = [
|
||||||
|
|
|
@ -255,7 +255,7 @@ static int global_var;
|
||||||
|
|
||||||
void ffi_test_36_point_5(void)
|
void ffi_test_36_point_5(void)
|
||||||
{
|
{
|
||||||
printf("int_ffi_test_36_point_5\n");
|
printf("ffi_test_36_point_5\n");
|
||||||
global_var = 0;
|
global_var = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ struct test_struct_12 { int a; double x; };
|
||||||
|
|
||||||
DLLEXPORT double ffi_test_36(struct test_struct_12 x);
|
DLLEXPORT double ffi_test_36(struct test_struct_12 x);
|
||||||
|
|
||||||
DLLEXPORT void int_ffi_test_36_point_5(void);
|
DLLEXPORT void ffi_test_36_point_5(void);
|
||||||
|
|
||||||
DLLEXPORT int ffi_test_37(int (*f)(int, int, int));
|
DLLEXPORT int ffi_test_37(int (*f)(int, int, int));
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue