summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2024-08-09 15:21:01 +0700
committerShulhan <ms@kilabit.info>2024-08-09 15:21:01 +0700
commit473738cfc9b33c18ee749112c7d248f4f626e7fb (patch)
treedebb0c880a59dbbd915c8453b035051704be8fe8
parentf703cf50a4e3b2092a5ad615eef9bd56f58a25d3 (diff)
downloadpakakeh.go-473738cfc9b33c18ee749112c7d248f4f626e7fb.tar.xz
lib/memfs: document the comparison with "go:embed" directive
Compared it with "go:embed", the memfs package is more flexible and portable. Currently, we found three disadvantages of using "go:embed", #1 - The "go:embed" only works if files or directory to be embedded is in the same parent directory. #2 - Accessing the embedded file require the original path. #3 - No development mode.
-rw-r--r--lib/memfs/doc.go166
1 files changed, 138 insertions, 28 deletions
diff --git a/lib/memfs/doc.go b/lib/memfs/doc.go
index bddb4831..fc892ff3 100644
--- a/lib/memfs/doc.go
+++ b/lib/memfs/doc.go
@@ -7,7 +7,7 @@
//
// # Usage
//
-// The first step is to create new instance of memfs using `New()`.
+// The first step is to create new instance of memfs using [New] function.
// The following example embed all files inside directory named "include" or
// any files with extension ".css", ".html", and ".js";
// but exclude any files inside directory named "exclude".
@@ -26,15 +26,18 @@
//
// By default only file with size less or equal to 5 MB will be included in
// memory.
-// To increase the default size set the MaxFileSize (in bytes).
-// For example, to set maximum file size to 10 MB,
+// To increase the default size set the [Options.MaxFileSize] (in bytes).
+// For example, to change maximum file size to 10 MB,
//
-// opts.MaxFileSize = 1024 * 1024 * 10
+// var opts = memfs.Options{
+// MaxFileSize: 1024 * 1024 * 10,
+// }
//
-// Later, if we want to get the file from memory, call Get() which will return
-// the node object with content can be accessed from field "Content".
-// Remember that if file size is larger that maximum,
-// the content will need to be read manually,
+// Later, if we want to get the file from memory, call [MemFS.Get] which
+// will return the [Node] object with content can be accessed from field
+// "Content".
+// If file size is larger than maximum, the content will need to be read
+// manually (as long as the file exist on system),
//
// node, err := mfs.Get("/")
// if err != nil {
@@ -51,23 +54,27 @@
//
// # Go embed
//
-// The memfs package also support embedding the files into Go generated source
-// file.
-// After we create memfs instance, we call GoEmbed() to dump all directory
-// and files as Go source code.
+// The memfs package support embedding the files into Go generated source
+// code.
+// After we create the MemFS instance, call the [GoEmbed] method to dump all
+// directory and files into Go source code.
//
-// First, define global variable as container for all files later in the same
-// package as generated code,
+// First, define global variable as container for all files to be embedded
+// in the same package as generated code,
//
// package mypackage
//
// var myFS *memfs.MemFS
//
-// Second, create new instance of MemFS with Options.Embed is set, and write
-// the content of memfs instance as Go source code file,
+// Second, create new instance of MemFS with [Options.Embed] is set,
//
-// opts := &Options{
-// Root: "./mydir",
+// var opts = &Options{
+// Embed: EmbedOptions{
+// PackageName: `mypackage`,
+// VarName: `myFS`,
+// GoFileName: `mypackage/embed.go`,
+// },
+// Root: `./mydir`,
// Includes: []string{
// `.*/include`,
// `.*\.(css|html|js)$`,
@@ -75,18 +82,121 @@
// Excludes: []string{
// `.*/exclude`,
// },
-// Embed: EmbedOptions{
-// PackageName: "mypackage",
-// VarName: "myFS",
-// GoFileName: "mypackage/embed.go",
-// },
// }
-// mfs, _ := memfs.New(opts)
+//
+// var mfs *memfs.MemFS
+// mfs, err = memfs.New(opts)
+// ...
+//
+// Third, call method [MemFS.GoEmbed] from the instance,
+//
// mfs.GoEmbed()
//
-// The Go generated file will be defined with package named "mypackage" using
-// global variable "myFS" as container stored in file "mypackage/file.go"
-// with each content encoded (compressed) using gzip.
+// This method will create Go file "mypackage/embed.go" that contains all
+// path and content of files inside the mfs instance, under package named
+// "mypackage".
+// Code that can read "myFS" variable then can access any files using
+// [MemFS.Get] method, with "/" as root prefix (not "./mydir").
+//
+// # Comparison with builtin go:embed
+//
+// This section list the disadvantages of "go:embed" directive.
+//
+// The memfs package created on [November 2018], based on my experiences
+// maintains the fork of [go-bindata] project.
+// The "go:embed" directive introduced into Go tools since
+// [Go version 1.16], released February 2021, three years after the first
+// release of memfs package.
+//
+// Given the following directory structure,
+//
+// module-root/
+// +-- cmd/prog/main.go
+// +-- _content/
+// +-- index.adoc
+// +-- index.html
+// +-- static/
+// +-- index.png
+// +-- index.png
+//
+// We want to embed the directory "_content" but only html files and all
+// files inside the "static/" directory.
+//
+// Cons #1: The "go:embed" only works if files or directory to be embedded
+// is in the same parent directory.
+//
+// The "go:embed" directive define in "cmd/prog/main.go" will not
+// able to embed files in their parent.
+// The following code will not compile,
+//
+// //go:embed ../../_content/*.html
+// //go:embed ../../_content/static
+// var contentFS embed.FS
+//
+// // go build output,
+// // pattern ../../_content/*.html: invalid pattern syntax
+//
+// If we remove the ".." and execute "go build" from module-root, it will
+// still not compile,
+//
+// //go:embed _content/*.html
+// //go:embed _content/static
+// var contentFS embed.FS
+//
+// // go build or run output,
+// // pattern _content/*.html: no matching files found
+//
+// The only solution is to create and export the variable "ContentFS" in the
+// same parent directory as "_content".
+//
+// The memfs package does not have this limitation.
+// As long as the Go commands are executed from the module-root directory,
+// you can define the variable in any packages.
+//
+// Cons #2: Accessing the embedded file require the original path.
+//
+// Let say we have embeded the "_content" directory using the following
+// syntax,
+//
+// //go:embed _content/*.html
+// //go:embed _content/static
+// var ContentFS embed.FS
+//
+// To access the file "index.html" you need to pass their full path, in this
+// case "_content/index.html".
+// The path "_content" leaked to the parent FS and not portable.
+//
+// In the memfs package, the content of [Options.Root] directory can be
+// accessed with "/", so it would become "/index.html".
+// This design allow flexibility and consistency between modules and
+// packages.
+// If an external, third-party package accept the MemFS instance and the
+// first thing they do is to read all contents of "/" directory, the caller
+// can embed any path without have specific prefix or name.
+//
+// Case example, when we embed SQL files for migration under directory
+// "db/migration" using the "go:embed" directive,
+//
+// //go:embed db/migration/*.sql
+// var DBMigrationFS embed.FS
+//
+// and then call the [Migrate] function, it cannot found any ".sql" files
+// inside the "/" directory because the files is stored under
+// "db/migration/" prefix.
+//
+// Cons #3: No development mode.
+//
+// Let say we run our server that served the content from FS instance.
+// If we changes the html files, and refresh the browser, the new content
+// will not reflected because it serve the content on the first embed.
+//
+// The memfs package have [Options.TryDirect] that bypass file in memory and
+// read directly to the file system.
+// This allow quick development when changes only template or non-code
+// files.
//
-// Thats it!
+// [November 2018]: https://git.sr.ht/~shulhan/pakakeh.go/commit/05b02c7b
+// [go-bindata]: https://github.com/shuLhan/go-bindata
+// [Go version 1.16]: https://go.dev/doc/go1.16
+// [Migrate]: https://pkg.go.dev/git.sr.ht/~shulhan/pakakeh.go/lib/sql#Client.Migrate
package memfs