Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Stop supporting legacy config files (~/.exercism.go)
* Deleted deprecated login/logout commands
* Deleted deprecated key names in config
* [#153](https://github.com/exercism/cli/pull/153): Refactored configuration package - [@kytrinyx](https://github.com/kytrinyx)
* Your contribution here

## v1.9.2 (Jan 11, 2015)
Expand Down
2 changes: 1 addition & 1 deletion cmd/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
// If a setting is not passed as an argument, default
// values are used.
func Configure(ctx *cli.Context) {
c, err := config.Read(ctx.GlobalString("config"))
c, err := config.New(ctx.GlobalString("config"))
if err != nil {
log.Fatal(err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func Debug(ctx *cli.Context) {
}
fmt.Printf("Home Dir: %s\n", dir)

c, err := config.Read(ctx.GlobalString("config"))
c, err := config.New(ctx.GlobalString("config"))
if err != nil {
log.Fatal(err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/demo.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

// Demo returns one problem for each active track.
func Demo(ctx *cli.Context) {
c, err := config.Read(ctx.GlobalString("config"))
c, err := config.New(ctx.GlobalString("config"))
if err != nil {
log.Fatal(err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (

// Download returns specified submissions and related problem.
func Download(ctx *cli.Context) {
c, err := config.Read(ctx.GlobalString("config"))
c, err := config.New(ctx.GlobalString("config"))
if err != nil {
log.Fatal(err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

// Fetch returns exercism problems.
func Fetch(ctx *cli.Context) {
c, err := config.Read(ctx.GlobalString("config"))
c, err := config.New(ctx.GlobalString("config"))
if err != nil {
log.Fatal(err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

// Restore returns a user's solved problems.
func Restore(ctx *cli.Context) {
c, err := config.Read(ctx.GlobalString("config"))
c, err := config.New(ctx.GlobalString("config"))
if err != nil {
log.Fatal(err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/submit.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func Submit(ctx *cli.Context) {
log.Fatal("Please enter a file name")
}

c, err := config.Read(ctx.GlobalString("config"))
c, err := config.New(ctx.GlobalString("config"))
if err != nil {
log.Fatal(err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/tracks.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

// Tracks lists available tracks.
func Tracks(ctx *cli.Context) {
c, err := config.Read(ctx.GlobalString("config"))
c, err := config.New(ctx.GlobalString("config"))
if err != nil {
log.Fatal(err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/unsubmit.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
// If no iteration is specified, the most recent iteration
// is deleted.
func Unsubmit(ctx *cli.Context) {
c, err := config.Read(ctx.GlobalString("config"))
c, err := config.New(ctx.GlobalString("config"))
if err != nil {
log.Fatal(err)
}
Expand Down
139 changes: 63 additions & 76 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package config
import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
Expand Down Expand Up @@ -65,99 +64,116 @@ func Home() (string, error) {
return dir, nil
}

// Read loads the config from the stored JSON file.
func Read(file string) (*Config, error) {
func New(path string) (*Config, error) {
c := &Config{}
err := c.Read(file)
err := c.load(path, os.Getenv(fileEnvKey))
return c, err
}

// Update sets new values where given.
func (c *Config) Update(key, host, dir, xapi string) {
key = strings.TrimSpace(key)
if key != "" {
c.APIKey = key
}

host = strings.TrimSpace(host)
if host != "" {
c.API = host
}

dir = strings.TrimSpace(dir)
if dir != "" {
c.Dir = dir
}

xapi = strings.TrimSpace(xapi)
if xapi != "" {
c.XAPI = xapi
}

c.configure()
}

// Expand takes inputs for a config file location and builds an absolute path.
func Expand(path, env, home string) string {
if path == "" {
path = env
}

if path != "" && path[0] == '~' {
path = strings.Replace(path, "~/", fmt.Sprintf("%s/", home), 1)
}

if path == "" {
path = filepath.Join(home, File)
// Write saves the config as JSON.
func (c *Config) Write() error {
// truncates existing file if it exists
f, err := os.Create(c.File)
if err != nil {
return err
}
defer f.Close()

return path
e := json.NewEncoder(f)
return e.Encode(c)
}

// Read loads the config from the stored JSON file.
func (c *Config) Read(file string) error {
home, err := c.homeDir()
func (c *Config) load(argPath, envPath string) error {
path, err := c.resolvePath(argPath, envPath)
if err != nil {
return err
}
c.File = path

c.File = Expand(file, os.Getenv(fileEnvKey), home)
if err := c.read(); err != nil {
return err
}

// in case people manually update the config file
// with weird formatting
c.APIKey = strings.TrimSpace(c.APIKey)
c.Dir = strings.TrimSpace(c.Dir)
c.API = strings.TrimSpace(c.API)
c.XAPI = strings.TrimSpace(c.XAPI)

return c.setDefaults()
}

func (c *Config) read() error {
if _, err := os.Stat(c.File); err != nil {
if os.IsNotExist(err) {
c.configure()
return nil
}
return err
}

f, err := os.Open(c.File)
if err != nil {
return err
}
defer f.Close()

d := json.NewDecoder(f)
err = d.Decode(&c)
if err != nil {
return err
return d.Decode(&c)
}

// IsAuthenticated returns true if the config contains an API key.
// This does not check whether or not that key is valid.
func (c *Config) IsAuthenticated() bool {
return c.APIKey != ""
}

// homeDir caches the lookup of the user's home directory.
func (c *Config) homeDir() (string, error) {
if c.home != "" {
return c.home, nil
}
c.configure()
return nil
return Home()
}

// Write() saves the config as JSON.
func (c *Config) Write() error {
// truncates existing file if it exists
f, err := os.Create(c.File)
func (c *Config) resolvePath(argPath, envPath string) (string, error) {
path := argPath
if path == "" {
path = envPath
}
if path == "" {
path = filepath.Join("~", File)
}
h, err := c.homeDir()
if err != nil {
return err
return "", err
}
defer f.Close()

e := json.NewEncoder(f)
return e.Encode(c)
return strings.Replace(path, "~", h, 1), nil
}

func (c *Config) configure() (*Config, error) {
c.sanitize()

func (c *Config) setDefaults() error {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this name does a much better job of communicating what it does. 👍

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, everything seemed a bit tangled before. Or perhaps: both tangled and fragmented. This time around I've kept larger chunks. I think fewer abstractions will be easier to manage.

if c.API == "" {
c.API = hostAPI
}
Expand All @@ -166,44 +182,15 @@ func (c *Config) configure() (*Config, error) {
c.XAPI = hostXAPI
}

homeDir, err := c.homeDir()
h, err := c.homeDir()
if err != nil {
return c, err
return err
}

if c.Dir == "" {
// fall back to default value
c.Dir = filepath.Join(homeDir, DirExercises)
} else {
// replace '~' with user's home
c.Dir = strings.Replace(c.Dir, "~/", fmt.Sprintf("%s/", homeDir), 1)
}

if c.File == "" {
c.File = filepath.Join(homeDir, File)
}

return c, nil
}

// IsAuthenticated returns true if the config contains an API key.
// This does not check whether or not that key is valid.
func (c *Config) IsAuthenticated() bool {
return c.APIKey != ""
}

// See: http://stackoverflow.com/questions/7922270/obtain-users-home-directory
// we can't cross compile using cgo and use user.Current()
func (c *Config) homeDir() (string, error) {
if c.home != "" {
return c.home, nil
c.Dir = filepath.Join(h, DirExercises)
}
return Home()
}
c.Dir = strings.Replace(c.Dir, "~", h, 1)

func (c *Config) sanitize() {
c.APIKey = strings.TrimSpace(c.APIKey)
c.Dir = strings.TrimSpace(c.Dir)
c.API = strings.TrimSpace(c.API)
c.XAPI = strings.TrimSpace(c.XAPI)
return nil
}
Loading