Repository: google/go-cloud
Pull request #926 (2018-12-11 17:11:34)

blob/docs: improve package godoc
- Expand package documentation.
- Ensure code formatting is applied appropriately.
- Use a consistent format for bulleted lists.
- Move a couple of Examples up the package level to make them more visible.

Updates #896.

Ping me if you want a demo.

vangent: PTAL

Patch:
diff --git a/blob/blob.go b/blob/blob.go
index b034b466..b47d878e 100644
--- a/blob/blob.go
+++ b/blob/blob.go
@@ -12,8 +12,33 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Package blob provides an easy way to interact with Blob objects within
-// a bucket. It utilizes standard io packages to handle reads and writes.
+// Package blob provides an easy and portable way to interact with blob objects
+// within a storage location, hereafter called a "bucket".
+//
+// It supports operations like reading and writing blobs (using standard
+// interfaces from the io package), deleting blobs, and listing blobs in a
+// bucket.
+//
+// To construct a *Bucket, don't use the NewBucket function in this package.
+// Instead, import one of the blob subpackages (e.g., fileblob, gcsblob,
+// s3blob), and use its exported function(s). For example:
+//
+//  bucket, err := fileblob.OpenBucket("path/to/dir", nil)
+//  if err != nil {
+//      return fmt.Errorf("could not open bucket: %v", err)
+//  }
+//  buf, err := bucket.ReadAll(ctx.Background(), "myfile.txt")
+//  ...
+//
+// Write your application code using the *Bucket type, and you can easily
+// reconfigure your initialization code to choose a different provider.
+// You can develop your application locally using fileblob, or deploy it to
+// multiple Cloud providers. You may find http://github.com/google/wire useful
+// for managing your initialization code.
+//
+// Alternatively, you can construct a *Bucket using blob.Open by providing
+// a URL that's supported by a blob subpackage that you have linked
+// in to your application.
 package blob
 
 import (
@@ -280,8 +305,10 @@ type Bucket struct {
 	b driver.Bucket
 }
 
-// NewBucket creates a new Bucket for a group of objects for a blob service.
-// It is for use by provider implementations.
+// NewBucket creates a new *Bucket based on a specific driver implementation.
+// Most end users should use subpackages to construct a *Bucket instead of this
+// function; see the package documentation for details.
+// It is intended for use by provider implementations.
 func NewBucket(b driver.Bucket) *Bucket {
 	return &Bucket{b: b}
 }
diff --git a/blob/fileblob/fileblob.go b/blob/fileblob/fileblob.go
index b079bdda..5c98634e 100644
--- a/blob/fileblob/fileblob.go
+++ b/blob/fileblob/fileblob.go
@@ -12,18 +12,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Package fileblob provides a bucket implementation that operates on the local
-// filesystem. This should not be used for production: it is intended for local
-// development.
+// Package fileblob provides a blob implementation that uses the filesystem.
+// Use OpenBucket to construct a blob.Bucket.
 //
 // Blob keys are escaped before being used as filenames, and filenames are
 // unescaped when they are passed back as blob keys during List. The escape
 // algorithm is:
-// -- Alphanumeric characters (A-Z a-z 0-9) are not escaped.
-// -- Space (' '), dash ('-'), underscore ('_'), and period ('.') are not escaped.
-// -- Slash ('/') is always escaped to the OS-specific path separator character
+//  - Alphanumeric characters (A-Z a-z 0-9) are not escaped.
+//  - Space (' '), dash ('-'), underscore ('_'), and period ('.') are not escaped.
+//  - Slash ('/') is always escaped to the OS-specific path separator character
 //    (os.PathSeparator).
-// -- All other characters are escaped similar to url.PathEscape:
+//  - All other characters are escaped similar to url.PathEscape:
 //    "%<hex UTF-8 byte>", with capital letters ABCDEF in the hex code.
 //
 // Filenames that can't be unescaped due to invalid escape sequences
@@ -31,13 +30,20 @@
 // (e.g., "~", which unescapes to "~", which escapes back to "%7E" != "~"),
 // aren't visible using fileblob.
 //
-// For blob.Open URLs, fileblob registers for the "file" scheme.
+// Open URLs
+//
+// For blob.Open URLs, fileblob registers for the protocol "file".
 // The URL's Path is used as the root directory; the URL's Host is ignored.
 // If os.PathSeparator != "/", any leading "/" from the Path is dropped.
 // No query options are supported. Examples:
-// -- file:///a/directory passes "/a/directory" to OpenBucket.
-// -- file://localhost/a/directory also passes "/a/directory".
-// -- file:///c:/foo/bar passes "c:/foo/bar".
+//  - file:///a/directory
+//    -> Passes "/a/directory" to OpenBucket.
+//  - file://localhost/a/directory
+//    -> Also passes "/a/directory".
+//  - file:///c:/foo/bar
+//    -> Passes "c:/foo/bar".
+//
+// As
 //
 // fileblob does not support any types for As.
 package fileblob
@@ -94,8 +100,8 @@ func openBucket(dir string, _ *Options) (driver.Bucket, error) {
 	return &bucket{dir}, nil
 }
 
-// OpenBucket creates a *blob.Bucket that reads and writes to dir.
-// dir must exist.
+// OpenBucket creates a *blob.Bucket backed by the filesystem and rooted at
+// dir, which must exist. See the package documentation for an example.
 func OpenBucket(dir string, opts *Options) (*blob.Bucket, error) {
 	drv, err := openBucket(dir, opts)
 	if err != nil {
diff --git a/blob/gcsblob/gcsblob.go b/blob/gcsblob/gcsblob.go
index a5fe9cf7..d82ddbff 100644
--- a/blob/gcsblob/gcsblob.go
+++ b/blob/gcsblob/gcsblob.go
@@ -12,30 +12,34 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Package gcsblob provides an implementation of blob that uses GCS.
+// Package gcsblob provides a blob implementation that uses GCS. Use OpenBucket
+// to construct a blob.Bucket.
 //
-// For blob.Open URLs, gcsblob registers for the "gs" protocol.
+// Open URLs
+//
+// For blob.Open URLs, gcsblob registers for the protocol "gs".
 // The URL's Host is used as the bucket name.
 // The following query options are supported:
 //
-//  * cred_path - Sets path to the Google credentials file. If unset, default 
-//    credentials are loaded. See 
-//    https://cloud.google.com/docs/authentication/production.
-//  * access_id - Sets Options.GoogleAccessID.
-//  * private_key_path - Sets path to a private key, which is read and used to 
-//    set Options.PrivateKey.
-//    
-// Example URL: 
-//    blob.Open("gs://mybucket")
+//  - cred_path: Sets path to the Google credentials file. If unset, default
+//    credentials are loaded.
+//    See https://cloud.google.com/docs/authentication/production.
+//  - access_id: Sets Options.GoogleAccessID.
+//  - private_key_path: Sets path to a private key, which is read and used
+//    to set Options.PrivateKey.
+// Example URL:
+//  gs://mybucket
+//
+// As
 //
-// It exposes the following types for As:
-//  Bucket: *storage.Client
-//  Error: *googleapi.Error
-//  ListObject: storage.ObjectAttrs
-//  ListOptions.BeforeList: *storage.Query
-//  Reader: storage.Reader
-//  Attributes: storage.ObjectAttrs
-//  WriterOptions.BeforeWrite: *storage.Writer
+// gcsblob exposes the following types for As:
+//  - Bucket: *storage.Client
+//  - Error: *googleapi.Error
+//  - ListObject: storage.ObjectAttrs
+//  - ListOptions.BeforeList: *storage.Query
+//  - Reader: storage.Reader
+//  - Attributes: storage.ObjectAttrs
+//  - WriterOptions.BeforeWrite: *storage.Writer
 package gcsblob
 
 import (
@@ -142,7 +146,8 @@ func openBucket(ctx context.Context, bucketName string, client *gcp.HTTPClient,
 	return &bucket{name: bucketName, client: c, opts: opts}, nil
 }
 
-// OpenBucket returns a GCS Bucket that communicates using the given HTTP client.
+// OpenBucket returns a *blob.Bucket backed by GCS. See the package
+// documentation for an example.
 func OpenBucket(ctx context.Context, bucketName string, client *gcp.HTTPClient, opts *Options) (*blob.Bucket, error) {
 	drv, err := openBucket(ctx, bucketName, client, opts)
 	if err != nil {
diff --git a/blob/s3blob/s3blob.go b/blob/s3blob/s3blob.go
index 36f5147e..495643f6 100644
--- a/blob/s3blob/s3blob.go
+++ b/blob/s3blob/s3blob.go
@@ -12,24 +12,30 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Package s3blob provides an implementation of blob using S3.
+// Package s3blob provides a blob implementation that uses S3. Use OpenBucket
+// to construct a blob.Bucket.
 //
-// For blob.Open URLs, s3blob registers for the "s3" protocol.
+// Open URLs
+//
+// For blob.Open URLs, s3blob registers for the protocol "s3".
 // The URL's Host is used as the bucket name.
 // The AWS session is created as described in
 // https://docs.aws.amazon.com/sdk-for-go/api/aws/session/.
 // The following query options are supported:
-// - region: The AWS region for requests; sets aws.Config.Region.
-// Example URL: blob.Open("s3://mybucket?region=us-east-1")
+//  - region: The AWS region for requests; sets aws.Config.Region.
+// Example URL:
+//  s3://mybucket?region=us-east-1
+//
+// As
 //
 // s3blob exposes the following types for As:
-// Bucket: *s3.S3
-// Error: awserr.Error
-// ListObject: s3.Object for objects, s3.CommonPrefix for "directories".
-// ListOptions.BeforeList: *s3.ListObjectsV2Input
-// Reader: s3.GetObjectOutput
-// Attributes: s3.HeadObjectOutput
-// WriterOptions.BeforeWrite: *s3manager.UploadInput
+//  - Bucket: *s3.S3
+//  - Error: awserr.Error
+//  - ListObject: s3.Object for objects, s3.CommonPrefix for "directories".
+//  - ListOptions.BeforeList: *s3.ListObjectsV2Input
+//  - Reader: s3.GetObjectOutput
+//  - Attributes: s3.HeadObjectOutput
+//  - WriterOptions.BeforeWrite: *s3manager.UploadInput
 package s3blob
 
 import (
@@ -93,7 +99,8 @@ func openBucket(ctx context.Context, bucketName string, sess client.ConfigProvid
 	}, nil
 }
 
-// OpenBucket returns an S3 Bucket.
+// OpenBucket returns a *blob.Bucket backed by S3. See the package documentation
+// for an example.
 func OpenBucket(ctx context.Context, bucketName string, sess client.ConfigProvider, opts *Options) (*blob.Bucket, error) {
 	drv, err := openBucket(ctx, bucketName, sess, opts)
 	if err != nil {


Test patch:
diff --git a/blob/example_test.go b/blob/example_test.go
index 1591aa12..46d779ff 100644
--- a/blob/example_test.go
+++ b/blob/example_test.go
@@ -140,25 +140,26 @@ func ExampleBucket_NewWriter() {
 	// text/plain; charset=utf-8
 }
 
-func ExampleBucket_ReadAll() {
+func Example() {
 	// Connect to a bucket when your program starts up.
-	// This example uses the file-based implementation.
+	// This example uses the file-based implementation in fileblob, and creates
+	// a temporary directory to use as the root directory.
 	dir, cleanup := newTempDir()
 	defer cleanup()
-
-	// Create the file-based bucket.
 	bucket, err := fileblob.OpenBucket(dir, nil)
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	// Write a blob using WriteAll.
+	// We now have a *blob.Bucket! We can write our application using the
+	// *blob.Bucket type, and have the freedom to change the initialization code
+	// above to choose a different provider later.
+
+	// In this example, we'll write a blob and then read it.
 	ctx := context.Background()
 	if err := bucket.WriteAll(ctx, "foo.txt", []byte("Go Cloud"), nil); err != nil {
 		log.Fatal(err)
 	}
-
-	// Read it back using ReadAll.
 	b, err := bucket.ReadAll(ctx, "foo.txt")
 	if err != nil {
 		log.Fatal(err)
diff --git a/blob/gcsblob/example_test.go b/blob/gcsblob/example_test.go
index fc43c5a3..e731706b 100644
--- a/blob/gcsblob/example_test.go
+++ b/blob/gcsblob/example_test.go
@@ -21,7 +21,7 @@ import (
 	"github.com/google/go-cloud/gcp"
 )
 
-func ExampleOpenBucket() {
+func Example() {
 	ctx := context.Background()
 
 	// Load credentials for GCP.
diff --git a/blob/s3blob/example_test.go b/blob/s3blob/example_test.go
index d4b4b506..44d168f0 100644
--- a/blob/s3blob/example_test.go
+++ b/blob/s3blob/example_test.go
@@ -21,7 +21,7 @@ import (
 	"github.com/google/go-cloud/blob/s3blob"
 )
 
-func ExampleOpenBucket() {
+func Example() {
 	ctx := context.Background()
 
 	// Create an AWS session.