Skip to content

Commit

Permalink
Final API implementation of RawRead returning an io.ReadCloser (file …
Browse files Browse the repository at this point in the history
…like) object. #13
  • Loading branch information
danielfireman committed Feb 27, 2018
1 parent 552a703 commit 5e9bbd4
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 33 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,9 @@ Once the data package is loaded, we could use the [Resource.RawRead](https://god

```go
so := pkg.GetResource("schemaorg")
soContents, _ := so.RawRead()
rc, _ := so.RawRead()
defer rc.Close()
contents, _ := ioutil.ReadAll(rc)
// Use contents. For instance, one could validate the JSON-LD schema and unmarshal it into a data structure.

data := pkg.GetResource("data")
Expand Down
4 changes: 3 additions & 1 deletion datapackage/package_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ func ExampleLoad_readRaw() {
ioutil.WriteFile(resPath, resContent, 0666)

pkg, _ := Load(descriptorPath, validator.InMemoryLoader())
contents, _ := pkg.GetResource("res1").RawRead()
rc, _ := pkg.GetResource("res1").RawRead()
defer rc.Close()
contents, _ := ioutil.ReadAll(rc)
fmt.Println(string(contents))
// Output: {"@context": {"@vocab": "http://schema.org/"}}
}
Expand Down
61 changes: 32 additions & 29 deletions datapackage/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,15 +194,7 @@ func (r *Resource) GetTable(opts ...csv.CreationOpts) (table.Table, error) {
return nil, fmt.Errorf("only csv and string is supported for inlining data")
}
}
buf, err := loadContents(r.basePath, r.path, csvLoadFunc)
if err != nil {
return nil, err
}
t, err := csv.NewTable(func() (io.ReadCloser, error) { return ioutil.NopCloser(bytes.NewReader(buf)), nil }, fullOpts...)
if err != nil {
return nil, err
}
return t, nil
return csv.NewTable(func() (io.ReadCloser, error) { return loadContents(r.basePath, r.path, csvLoadFunc) }, fullOpts...)
}

func csvLoadFunc(p string) func() (io.ReadCloser, error) {
Expand Down Expand Up @@ -233,23 +225,39 @@ func binaryLoadFunc(p string) func() (io.ReadCloser, error) {
if err != nil {
return nil, err
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return ioutil.NopCloser(bytes.NewReader(b)), nil
return resp.Body, nil
}
}
return func() (io.ReadCloser, error) {
return os.Open(p)
}
}

type loadFunc func(string) func() (io.ReadCloser, error)
type multiReadCloser struct {
io.Reader
rcs []io.ReadCloser
}

func (m *multiReadCloser) Close() error {
var err error
for _, rc := range m.rcs {
if e := rc.Close(); e != nil {
err = e
}
}
return err
}

func newMultiReadCloser(rcs []io.ReadCloser) io.ReadCloser {
readers := make([]io.Reader, len(rcs))
for i := range rcs {
readers[i] = io.Reader(rcs[i])
}
return &multiReadCloser{io.MultiReader(readers...), rcs}
}

func loadContents(basePath string, path []string, f loadFunc) ([]byte, error) {
var buf bytes.Buffer
func loadContents(basePath string, path []string, f func(string) func() (io.ReadCloser, error)) (io.ReadCloser, error) {
var rcs []io.ReadCloser
for _, p := range path {
if basePath != "" {
p = joinPaths(basePath, p)
Expand All @@ -258,17 +266,12 @@ func loadContents(basePath string, path []string, f loadFunc) ([]byte, error) {
if err != nil {
return nil, err
}
defer rc.Close()
b, err := ioutil.ReadAll(rc)
if err != nil {
return nil, err
}
buf.Write(b)
rcs = append(rcs, rc)
if len(path) > 1 {
buf.WriteRune('\n')
rcs = append(rcs, ioutil.NopCloser(bytes.NewReader([]byte{'\n'})))
}
}
return buf.Bytes(), nil
return newMultiReadCloser(rcs), nil
}

func joinPaths(basePath, path string) string {
Expand All @@ -289,11 +292,11 @@ func (r *Resource) ReadAll(opts ...csv.CreationOpts) ([][]string, error) {
return t.ReadAll()
}

// RawRead reads all resource contents and return it as byte slice.
// RawRead returns an io.ReaderCloser associated to the resource contents.
// It can be used to access the content of non-tabular resources.
func (r *Resource) RawRead() ([]byte, error) {
func (r *Resource) RawRead() (io.ReadCloser, error) {
if r.data != nil {
return []byte(r.data.(string)), nil
return ioutil.NopCloser(bytes.NewReader([]byte(r.data.(string)))), nil
}
return loadContents(r.basePath, r.path, binaryLoadFunc)
}
Expand Down
10 changes: 8 additions & 2 deletions datapackage/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,10 @@ func TestResource_RawRead(t *testing.T) {
}`, ts.URL)
res, err := NewResourceFromString(resStr, validator.MustInMemoryRegistry())
is.NoErr(err)
contents, err := res.RawRead()
rc, err := res.RawRead()
is.NoErr(err)
defer rc.Close()
contents, err := ioutil.ReadAll(rc)
is.NoErr(err)
is.Equal(string(contents), "1234")
})
Expand All @@ -446,7 +449,10 @@ func TestResource_RawRead(t *testing.T) {
}`
res, err := NewResourceFromString(resStr, validator.MustInMemoryRegistry())
is.NoErr(err)
contents, err := res.RawRead()
rc, err := res.RawRead()
is.NoErr(err)
defer rc.Close()
contents, err := ioutil.ReadAll(rc)
is.NoErr(err)
is.Equal(string(contents), "{\"foo\":\"1234\"}")
})
Expand Down

0 comments on commit 5e9bbd4

Please sign in to comment.