allow "directory-require.rkt" files to apply to subdirectories (closes #61)

pull/84/head
Matthew Butterick 9 years ago
parent 1fde2db75e
commit 096bc7a6dd

@ -1,18 +1,36 @@
#lang racket/base #lang racket/base
(require "world.rkt" sugar/define sugar/coerce) (require "world.rkt" sugar/define sugar/coerce)
(define (paths? x) (and (list? x) (andmap path? x))) (define (complete-paths? x) (and (list? x) (andmap complete-path? x)))
(define/contract+provide (get-directory-require-files source-path) ; keep contract local to ensure coercion (define/contract+provide (get-directory-require-files source-path) ; keep contract local to ensure coercion
(coerce/path? . -> . (or/c #f paths?)) (coerce/path? . -> . (or/c #f complete-paths?))
(define possible-requires (list (simplify-path (build-path source-path 'up world:directory-require))))
(and (andmap file-exists? possible-requires) possible-requires)) (define (dirname path)
(let-values ([(dir name dir?) (split-path path)])
dir))
(define (find-upward filename-to-find)
(parameterize ([current-directory (dirname (->complete-path source-path))])
(let loop ([dir (current-directory)][path (string->path filename-to-find)])
(and dir ; dir is #f when it hits the top of the filesystem
(let ([completed-path (path->complete-path path)])
(if (file-exists? completed-path)
(simplify-path completed-path)
(loop (dirname dir) (build-path 'up path))))))))
(define require-filenames (list world:directory-require))
(define not-false? (λ(x) x))
(define possible-requires (filter not-false? (map find-upward require-filenames)))
(and (not (null? possible-requires)) possible-requires))
(define+provide/contract (require+provide-directory-require-files here-path #:provide [provide #t]) (define+provide/contract (require+provide-directory-require-files here-path #:provide [provide #t])
(coerce/path? . -> . (or/c list? void?)) (coerce/path? . -> . (or/c list? void?))
(define (put-file-in-require-form file) (define (put-file-in-require-form file)
`(file ,(path->string file))) `(file ,(path->string file)))
(define directory-require-files (get-directory-require-files here-path)) (define directory-require-files (get-directory-require-files here-path))
(if directory-require-files (if directory-require-files
@ -21,7 +39,7 @@
(require ,@files-in-require-form) (require ,@files-in-require-form)
,@(if provide ,@(if provide
(list `(provide (all-from-out ,@files-in-require-form))) (list `(provide (all-from-out ,@files-in-require-form)))
'()))) null)))
'(begin))) '(begin)))

@ -61,7 +61,7 @@ Any value or function that is defined within the source file using @racket[defin
@subsection{The @filepath{directory-require.rkt} file} @subsection{The @filepath{directory-require.rkt} file}
If a file called @filepath{directory-require.rkt} exists in the same directory with a source file, it's automatically imported when the source file is compiled. If a file called @filepath{directory-require.rkt} exists in the same directory with a source file, or in a parent directory of that source file, it's automatically imported when the source file is compiled.
@bold{How is this different from Racket?} In Racket, you must explicitly import files using @racket[require]. @bold{How is this different from Racket?} In Racket, you must explicitly import files using @racket[require].

@ -654,9 +654,9 @@ First, using this file is not mandatory. You can always import functions and val
Second, notice from the @filepath{.rkt} suffix that @filepath{directory-require.rkt} is a source file containing Racket code, not Pollen code. This is the default because while Pollen is better for text-driven source files, Racket is better for code-driven source files. Still, the choice is yours: the name of this file can be changed by resetting the @racket[world:directory-require] value. Second, notice from the @filepath{.rkt} suffix that @filepath{directory-require.rkt} is a source file containing Racket code, not Pollen code. This is the default because while Pollen is better for text-driven source files, Racket is better for code-driven source files. Still, the choice is yours: the name of this file can be changed by resetting the @racket[world:directory-require] value.
Third, notice from the @filepath{directory-} prefix that @filepath{directory-require.rkt} is only used by Pollen source files @italic{in the same directory}. So if your project has source files nested inside a subdirectory, you'll need to explicitly create another @filepath{directory-require.rkt} there and share the functions & values as needed. Third, the @filepath{directory-} prefix represents the minimum scope for the file, not the maximum. Pollen source files nested in subdirectories will look for a @filepath{directory-require.rkt} in their own directory first. But if they can't find it, they'll look in the parent directory, then the next parent directory, and so on. Thus, by default, a @filepath{directory-require.rkt} in the root folder of a project will apply to all the source files in the project. But when you add a new @filepath{directory-require.rkt} to a subdirectory, it will apply to all files underneath.
@margin-note{``Why not make this file visible throughout a project, rather than just a directory?'' Good idea, but I couldn't figure out how to do it without creating finicky new dependencies. If you have a better idea, I'm open to it.} @margin-note{Though a subdirectory-specific @filepath{directory-require.rkt} will supersede the one in the enclosing directory, you can still use @racket[(require "../directory-require.rkt")] to pull in definitions from above, and @racket[provide] to propagate them into the current subdirectory. For instance, @racket[(provide (all-from-out "../directory-require.rkt"))] will re-export everything.}
Let's see how this works in practice. In the same directory as @filepath{article.html.pm}, create a new @filepath{directory-require.rkt} file as follows: Let's see how this works in practice. In the same directory as @filepath{article.html.pm}, create a new @filepath{directory-require.rkt} file as follows:

@ -68,8 +68,6 @@ Determines the default HTTP port for the project server. Initialized to @racket[
@defoverridable[meta-tag-name symbol?]{Name of the tag used to mark metas within Pollen source.} @defoverridable[meta-tag-name symbol?]{Name of the tag used to mark metas within Pollen source.}
@defoverridable[directory-require string?]{File implicitly required into every Pollen source file from its directory. Initialized to @filepath{directory-require.rkt}.}
@defoverridable[server-extras-dir string?]{Name of directory where server support files live. Initialized to @tt{server-extras}.} @defoverridable[server-extras-dir string?]{Name of directory where server support files live. Initialized to @tt{server-extras}.}
@defoverridable[extension-escape-char char?]{Character for escaping output-file extensions within source-file names. Initialized to @racket[#\!].} @defoverridable[extension-escape-char char?]{Character for escaping output-file extensions within source-file names. Initialized to @racket[#\!].}

@ -0,0 +1,8 @@
#lang racket/base
(provide (all-defined-out))
(define (root . xs)
`(one ,@xs))
(define (puppy)
"one")

@ -0,0 +1,8 @@
#lang racket/base
(provide (all-defined-out))
(define (root . xs)
`(two ,@xs))
(define (puppy)
"two")

@ -0,0 +1,11 @@
#lang at-exp racket/base
(require rackunit racket/runtime-path pollen/project)
(define-runtime-path pathup-one "data/pathup/subdir/test-pathup-one.html.pm")
(define-runtime-path dr-top "data/pathup/directory-require.rkt")
(define-runtime-path pathup-two "data/pathup/subdir/subdir/test-pathup-two.html.pm")
(define-runtime-path dr-sub "data/pathup/subdir/subdir/directory-require.rkt")
(check-false (get-directory-require-files "test-pathup.rkt"))
(check-equal? (get-directory-require-files pathup-one) (list dr-top))
(check-equal? (get-directory-require-files pathup-two) (list dr-sub))
Loading…
Cancel
Save