diff --git a/README.md b/README.md index a5e40c4..818d1c5 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ __Idioms__: | Pattern | Description | |:-------:| ----------- | -| [Functional Options](functional_options.go) | Allows creating clean APIs with sane defaults and idiomatic overrides | +| [Functional Options](idiom/functional_options.md) | Allows creating clean APIs with sane defaults and idiomatic overrides | __Anti-Patterns__: diff --git a/functional_options.go b/functional_options.go deleted file mode 100644 index 249a462..0000000 --- a/functional_options.go +++ /dev/null @@ -1,65 +0,0 @@ -package main - -import "os" - -type Arguments struct { - UID int - GID int - Flags int - Contents string - Permissions os.FileMode -} - -type Argument func(*Arguments) - -func UID(userID int) Argument { - return func(args *Arguments) { - args.UID = userID - } -} - -func GID(groupID int) Argument { - return func(args *Arguments) { - args.GID = groupID - } -} - -func Contents(c string) Argument { - return func(args *Arguments) { - args.Contents = c - } -} - -func Permissions(perms os.FileMode) Argument { - return func(args *Arguments) { - args.Permissions = perms - } -} - -func New(filepath string, setters ...Argument) error { - // Default Arguments - args := &Arguments{ - UID: os.Getuid(), - GID: os.Getgid(), - Contents: "", - Permissions: 0666, - Flags: os.O_CREATE | os.O_EXCL | os.O_WRONLY, - } - - for _, setter := range setters { - setter(args) - } - - f, err := os.OpenFile(filepath, args.Flags, args.Permissions) - if err != nil { - return err - } else { - defer f.Close() - } - - if _, err := f.WriteString(args.Contents); err != nil { - return err - } - - return f.Chown(args.UID, args.GID) -} diff --git a/idiom/functional_options.md b/idiom/functional_options.md new file mode 100644 index 0000000..435c5b8 --- /dev/null +++ b/idiom/functional_options.md @@ -0,0 +1,90 @@ +# Functional Options +Functional options are a method of implementing clean/eloquent APIs in Go. +Options implemented as a function set the state of that option. + +## Implementation + +### Options +```go +package file + +type Options struct { + UID int + GID int + Flags int + Contents string + Permissions os.FileMode +} + +type Option func(*Options) + +func UID(userID int) Option { + return func(args *Options) { + args.UID = userID + } +} + +func GID(groupID int) Option { + return func(args *Options) { + args.GID = groupID + } +} + +func Contents(c string) Option { + return func(args *Options) { + args.Contents = c + } +} + +func Permissions(perms os.FileMode) Option { + return func(args *Options) { + args.Permissions = perms + } +} +``` + +### Constructor +```go +package file + +func New(filepath string, setters ...Option) error { + // Default Options + args := &Options{ + UID: os.Getuid(), + GID: os.Getgid(), + Contents: "", + Permissions: 0666, + Flags: os.O_CREATE | os.O_EXCL | os.O_WRONLY, + } + + for _, setter := range setters { + setter(args) + } + + f, err := os.OpenFile(filepath, args.Flags, args.Permissions) + if err != nil { + return err + } else { + defer f.Close() + } + + if _, err := f.WriteString(args.Contents); err != nil { + return err + } + + return f.Chown(args.UID, args.GID) +} +``` + +## Usage +```go +emptyFile, err := file.New("/tmp/empty.txt") +if err != nil { + panic(err) +} + +fillerFile, err := file.New("/tmp/file.txt", file.UID(1000), file.Contents("Lorem Ipsum Dolor Amet")) +if err != nil { + panic(err) +} +```