github.com/karrick/gorill@v1.10.3/README.md (about)

     1  # gorill
     2  
     3  Small Go library for various stream wrappers.  A 'rill' is a small stream.
     4  
     5  ### Usage
     6  
     7  Documentation is available via
     8  [![GoDoc](https://godoc.org/github.com/karrick/gorill?status.svg)](https://godoc.org/github.com/karrick/gorill).
     9  
    10  ### Description
    11  
    12  One of the strengths of Go's interface system is that it allows easy composability of data types.
    13  An `io.Writer` is any data structure that exposes the `Write([]byte) (int,error)` method.
    14  
    15  If a program has an `io.Writer` but requires one that buffers its output somewhat, the program could
    16  use the Go standard library `bufio.Writer` to wrap the original `io.Writer`, providing buffering on
    17  the data writes.  That works great, but the programmer must be willing to allow the buffer to
    18  completely fill before flushing data to the final output stream. Instead, the program could use
    19  `gorill.SpooledWriteCloser` which buffers writes, but flushes data at a configurable periodicity.
    20  
    21  ### Supported Use cases
    22  
    23  ##### FilesReader
    24  
    25  FilesReader is an io.ReadCloser that can be used to read over the contents of all of the files
    26  specified by pathnames. It only opens a single file handle at a time. When reading from the
    27  currently open file handle returns io.EOF, it closes that file handle, and the next Read will cause
    28  the following file in the series to be opened and read from. Similar to io.MultiReader.
    29  
    30  ```Go
    31      var ior io.Reader
    32      if flag.NArg() == 0 {
    33          ior = os.Stdin
    34      } else {
    35          ior = &gorill.FilesReader{Pathnames: flag.Args()}
    36      }
    37  
    38      lines := bufio.NewScanner(ior)
    39      for lines.Scan() {
    40          // ...
    41      }
    42  ```
    43  
    44  ##### LineTerminatedReader
    45  
    46  When a program has an `io.Reader` and needs to ensure the final byte
    47  read is a newline, the `gorill.LineTerminatedReader` may be used to
    48  wrap the source `io.Reader` to provide that assurance.
    49  
    50  ```Go
    51  func ExampleNewLineTerminatedReader() {
    52  	r := &LineTerminatedReader{R: bytes.NewReader([]byte("123\n456"))}
    53  	buf, err := ioutil.ReadAll(r)
    54  	if err != nil {
    55  		fmt.Fprintf(os.Stderr, "%s\n", err)
    56  		os.Exit(1)
    57  	}
    58  	if got, want := len(buf), 8; got != want {
    59  		fmt.Fprintf(os.Stderr, "GOT: %v; WANT: %v\n", got, want)
    60  		os.Exit(1)
    61  	}
    62  	fmt.Printf("%q\n", buf[len(buf)-1])
    63  	// Output: '\n'
    64  }
    65  ```
    66  
    67  ##### NopWriteCloser
    68  
    69  If a program has an `io.Writer` but requires an `io.WriteCloser`, the program can imbue the
    70  `io.Writer` with a no-op `Close` method.  The resultant structure can be used anywhere an
    71  `io.WriteCloser` is required.
    72  
    73  ```Go
    74      iowc := gorill.NopCloseWriter(iow)
    75      iowc.Close() // does nothing
    76  ```
    77  
    78  Alternatively, if you already have an `io.WriteCloser`, but you want its `Close` method to do
    79  nothing, then wrap it in a `NopCloseWriter`.
    80  
    81  ##### NopReadCloser
    82  
    83  If a program has an `io.Reader` but requires an `io.ReadCloser`, the program can imbue the
    84  `io.Reader` with a no-op `Close` method.  The resultant structure can be used anywhere an
    85  `io.ReadCloser` is required.  The Go standard library provides this exact functionality by the
    86  `ioutil.NopCloser(io.Reader) io.ReadCloser` function.  It is also provided by this library for
    87  symmetry with the `gorill.NopCloseWriter` call above.
    88  
    89  ```Go
    90      iorc := gorill.NopCloseReader(ior)
    91      iorc.Close() // does nothing
    92  ```
    93  
    94  Alternatively, if you already have an `io.ReadCloser`, but you want its `Close` method to do
    95  nothing, then wrap it in a `gorill.NopCloseReader`.
    96  
    97  ##### SpooledWriteCloser
    98  
    99  If a program has an `io.WriteCloser` but requires one that spools its data over perhaps a slow
   100  network connection, the program can use a `gorill.SpooledWriteCloser` to wrap the original
   101  `io.WriteCloser`, but ensure the data is flushed periodically.
   102  
   103  ```Go
   104      // uses gorill.DefaultFlushPeriod and gorill.DefaultBufSize
   105      spooler, err := gorill.NewSpooledWriteCloser(iowc)
   106      if err != nil {
   107          return err
   108      }
   109  ```
   110  
   111  You can customize either or both the size of the underlying buffer, and the frequency of buffer
   112  flushes, based on your program's requirements.  Simply list the required customizations after the
   113  underlying `io.WriteCloser`.
   114  
   115  ```Go
   116      spooler, err := gorill.NewSpooledWriteCloser(iowc, gorill.BufSize(8192), gorill.Flush(time.Second))
   117      if err != nil {
   118          return err
   119      }
   120  ```
   121  
   122  If the program has an `io.Writer` but needs a spooled writer, it can compose data structures to
   123  achieve the required functionality:
   124  
   125  ```Go
   126      spooler, err := gorill.NewSpooledWriteCloser(gorill.NopCloseWriter(iow), gorill.Flush(time.Second))
   127      if err != nil {
   128          return err
   129      }
   130  ```
   131  
   132  ##### TimedReadCloser
   133  
   134  If a program has an `io.Reader` but requires one that has a built in timeout for reads, one can
   135  wrap the original `io.Reader`, but modify the `Read` method to provide the required timeout
   136  handling.  The new data structure can be used anywhere the original `io.Reader` was used, and
   137  seemlessly handles reads that take too long.
   138  
   139  ```Go
   140      timed := gorill.NewTimedReadCloser(iowc, 10*time.Second)
   141      buf := make([]byte, 1000)
   142      n, err := timed.Read(buf)
   143      if err != nil {
   144          if terr, ok := err.(gorill.ErrTimeout); ok {
   145              // timeout occurred
   146          }
   147          return err
   148      }
   149  ```
   150  
   151  ##### TimedWriteCloser
   152  
   153  If a program has an `io.Writer` but requires one that has a built in timeout for writes, one can
   154  wrap the original `io.Writer`, but modify the `Write` method to provide the required timeout
   155  handling.  The new data structure can be used anywhere the original `io.Writer` was used, and
   156  seemlessly handles writes that take too long.
   157  
   158  ```Go
   159      timed := gorill.NewTimedWriteCloser(iowc, 10*time.Second)
   160      n, err := timed.Write([]byte("example"))
   161      if err != nil {
   162          if terr, ok := err.(gorill.ErrTimeout); ok {
   163              // timeout occurred
   164          }
   165          return err
   166      }
   167  ```
   168  
   169  ## LockingWriteCloser
   170  
   171  If a program needs an `io.WriteCloser` that can be concurrently used by more than one go-routine, it
   172  can use a `gorill.LockingWriteCloser`.  Benchmarks show a 3x performance gain by using `sync.Mutex`
   173  rather than channels for this case.  `gorill.LockingWriteCloser` data structures provide this
   174  peformance benefit.
   175  
   176  ```Go
   177      lwc := gorill.NewLockingWriteCloser(os.Stdout)
   178      for i := 0; i < 1000; i++ {
   179          go func(iow io.Writer, i int) {
   180              for j := 0; j < 100; j++ {
   181                  _, err := iow.Write([]byte("Hello, World, from %d!\n", i))
   182                  if err != nil {
   183                      return
   184                  }
   185              }
   186          }(lwc, i)
   187      }
   188  ```
   189  
   190  ##### MultiWriteCloserFanIn
   191  
   192  If a program needs to be able to fan in writes from multiple `io.WriteCloser` instances to a single
   193  `io.WriteCloser`, the program can use a `gorill.MultiWriteCloserFanIn`.
   194  
   195  ```Go
   196      func Example(largeBuf []byte) {
   197          bb := gorill.NewNopCloseBuffer()
   198          first := gorill.NewMultiWriteCloserFanIn(bb)
   199          second := first.Add()
   200          first.Write(largeBuf)
   201          first.Close()
   202          second.Write(largeBuf)
   203          second.Close()
   204      }
   205  ```
   206  
   207  ##### MultiWriteCloserFanOut
   208  
   209  If a program needs to be able to fan out writes to multiple `io.WriteCloser` instances, the program
   210  can use a `gorill.MultiWriteCloserFanOut`.
   211  
   212  ```Go
   213      bb1 = gorill.NewNopCloseBuffer()
   214      bb2 = gorill.NewNopCloseBuffer()
   215      mw = gorill.NewMultiWriteCloserFanOut(bb1, bb2)
   216      n, err := mw.Write([]byte("blob"))
   217      if want := 4; n != want {
   218          t.Errorf("Actual: %#v; Expected: %#v", n, want)
   219      }
   220      if err != nil {
   221          t.Errorf("Actual: %#v; Expected: %#v", err, nil)
   222      }
   223      if want := "blob"; bb1.String() != want {
   224          t.Errorf("Actual: %#v; Expected: %#v", bb1.String(), want)
   225      }
   226      if want := "blob"; bb2.String() != want {
   227          t.Errorf("Actual: %#v; Expected: %#v", bb2.String(), want)
   228      }
   229  ```
   230  
   231  ### Supported Use cases for Testing
   232  
   233  ##### NopCloseBuffer
   234  
   235  If a test needs a `bytes.Buffer`, but one that has a `Close` method, the test could simply wrap the
   236  `bytes.Buffer` structure with `ioutil.NopClose()`, but the resultant data structure would only
   237  provide an `io.ReadCloser` interface, and not all the other convenient `bytes.Buffer` methods.
   238  Instead the test could use `gorill.NopCloseBuffer` which simply imbues a no-op `Close` method to a
   239  `bytes.Buffer` instance:
   240  
   241  ```Go
   242      func TestSomething(t *testing.T) {
   243          bb := gorill.NopCloseBuffer()
   244          bb.Write([]byte("example"))
   245          bb.Close() // does nothing
   246      }
   247  ```
   248  
   249  Custom buffer sizes can also be used:
   250  
   251  ```Go
   252      func TestSomething(t *testing.T) {
   253          bb := gorill.NopCloseBufferSize(16384)
   254          bb.Write([]byte("example"))
   255          bb.Close() // does nothing
   256      }
   257  ```
   258  
   259  ##### ShortWriter
   260  
   261  If a test needs an `io.Writer` that simulates write errors, the test could wrap an existing
   262  `io.Writer` with a `gorill.ShortWriter`.  Writes to the resultant `io.Writer` will work as before,
   263  unless the length of data to be written exceeds some preset limit.  In this case, only the preset
   264  limit number of bytes will be written to the underlying `io.Writer`, but the write will return this
   265  limit and an `io.ErrShortWrite` error.
   266  
   267  ```Go
   268      func TestShortWrites(t *testing.T) {
   269          bb := gorill.NopCloseBuffer()
   270          sw := gorill.ShortWriter(bb, 16)
   271  
   272          n, err := sw.Write([]byte("short write"))
   273          // n == 11, err == nil
   274  
   275          n, err := sw.Write([]byte("a somewhat longer write"))
   276          // n == 16, err == io.ErrShortWrite
   277      }
   278  ```
   279  
   280  ##### SlowReader
   281  
   282  If a test needs an `io.Reader` that writes all the data to the underlying `io.Reader`, but does so
   283  after a delay, the test could wrap an existing `io.Reader` with a `gorill.SlowReader`.
   284  
   285  ```Go
   286      bb := gorill.NopCloseBuffer()
   287      sr := gorill.SlowReader(bb, 10*time.Second)
   288  
   289      buf := make([]byte, 1000)
   290      n, err := sr.Read(buf) // this call takes at least 10 seconds to return
   291      // n == 7, err == nil
   292  ```
   293  
   294  ##### SlowWriter
   295  
   296  If a test needs an `io.Writer` that writes all the data to the underlying `io.Writer`, but does so
   297  after a delay, the test could wrap an existing `io.Writer` with a `gorill.SlowWriter`.
   298  
   299  ```Go
   300      func TestSlowWrites(t *testing.T) {
   301          bb := gorill.NopCloseBuffer()
   302          sw := gorill.SlowWriter(bb, 10*time.Second)
   303  
   304          n, err := sw.Write([]byte("example")) // this call takes at least 10 seconds to return
   305          // n == 7, err == nil
   306      }
   307  ```