Skip to content

Commit

Permalink
add cli and make go-phrase a cli app
Browse files Browse the repository at this point in the history
  • Loading branch information
weynsee committed May 19, 2015
1 parent a063f40 commit e3bc223
Show file tree
Hide file tree
Showing 45 changed files with 2,870 additions and 130 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
/examples/*.yml

.phrase
.ruby-version
/cli/test/*.yml
6 changes: 5 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ go:
- tip
before_install:
- go get github.com/axw/gocov/gocov
- go get github.com/modocache/gover
- go get github.com/mattn/goveralls
- if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
script:
- $HOME/gopath/bin/goveralls -service=travis-ci ./phrase
- go test -coverprofile=phrase.coverprofile ./phrase
- go test -coverprofile=cli.coverprofile ./cli
- $HOME/gopath/bin/gover
- $HOME/gopath/bin/goveralls -coverprofile=gover.coverprofile -service travis-ci
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# go-phrase #

go-phrase is a Go client library for accessing the [Phrase API](http://docs.phraseapp.com/api/v1/).
go-phrase is a Go client library for accessing the [PhraseApp API](http://docs.phraseapp.com/api/v1/).

**Documentation:** [![GoDoc](https://godoc.org/github.com/weynsee/go-phrase?status.svg)](https://godoc.org/github.com/weynsee/go-phrase)
**Build Status:** [![Build Status](https://travis-ci.org/weynsee/go-phrase.svg?branch=master)](https://travis-ci.org/weynsee/go-phrase)
Expand All @@ -14,8 +14,8 @@ go-phrase requires Go version 1.1 or greater.
import "github.com/weynsee/go-phrase/phrase"
```

Construct a new Phrase client, then use the various services on the client to
access different parts of the Phrase API. For example, to list all
Construct a new API client, then use the various services on the client to
access different parts of the PhraseApp API. For example, to list all
the locales for your project token:

```go
Expand Down
35 changes: 35 additions & 0 deletions cli/api_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package cli

import (
"github.com/weynsee/go-phrase/phrase"
"net/http"
"net/http/httptest"
"net/url"
"os"
)

var (
mux *http.ServeMux
client *phrase.Client
server *httptest.Server

testFolder = "test"
)

func setupAPI() {
mux = http.NewServeMux()
server = httptest.NewServer(mux)
client = phrase.New("faketoken")
client.BaseURL, _ = url.Parse(server.URL)
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
})
}

func tearDown() {
shutdownAPI()
os.RemoveAll(testFolder)
}

func shutdownAPI() {
server.Close()
}
23 changes: 23 additions & 0 deletions cli/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package cli

import (
mcli "github.com/mitchellh/cli"
)

type cli struct {
cli *mcli.CLI
}

// NewCLI returns a CLI instance.
func NewCLI(version string, args []string) *cli {
c := mcli.NewCLI("go-phrase", version)
c.Args = args
c.Commands = commands

return &cli{c}
}

// Run runs the actual program based on the arguments given.
func (c *cli) Run() (int, error) {
return c.cli.Run()
}
36 changes: 36 additions & 0 deletions cli/cli_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package cli

import (
mcli "github.com/mitchellh/cli"
"reflect"
"testing"
)

func TestNewCLI(t *testing.T) {
args := []string{"a", "b"}
cli := NewCLI("0.0.1", args)
if got := cli.cli.Name; got != "go-phrase" {
t.Error("cli Name should be go-phrase, not %+v", got)
}
if got := cli.cli.Args; !reflect.DeepEqual(got, args) {
t.Errorf("cli.Args returned %+v, want %+v", got, args)
}
}

func TestNewCLI_Run(t *testing.T) {
args := []string{"foo", "-bar", "-baz"}
cli := NewCLI("0.0.1", args)
command := new(mcli.MockCommand)
cli.cli.Commands = map[string]mcli.CommandFactory{
"foo": func() (mcli.Command, error) {
return command, nil
},
}
res, err := cli.Run()
if err != nil {
t.Fatalf("NewCLI Run returned error %s", err.Error())
}
if res != 0 {
t.Fatal("NewCLI Run exit code should be 0")
}
}
58 changes: 58 additions & 0 deletions cli/commands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package cli

import (
mcli "github.com/mitchellh/cli"
"github.com/weynsee/go-phrase/phrase"
"os"
)

var commands map[string]mcli.CommandFactory

func init() {
ui := &mcli.ConcurrentUi{
Ui: &mcli.ColoredUi{
Ui: &mcli.BasicUi{
Reader: os.Stdin,
Writer: os.Stdout,
ErrorWriter: os.Stderr,
},
WarnColor: mcli.UiColorYellow,
ErrorColor: mcli.UiColorRed,
OutputColor: mcli.UiColorGreen,
},
}

config, _ := NewConfig(".phrase")
api := phrase.New(config.Secret)

commands = map[string]mcli.CommandFactory{
"init": func() (mcli.Command, error) {
return &InitCommand{
Ui: ui,
Config: config,
API: api,
}, nil
},
"push": func() (mcli.Command, error) {
return &PushCommand{
Ui: ui,
Config: config,
API: api,
}, nil
},
"pull": func() (mcli.Command, error) {
return &PullCommand{
Ui: ui,
Config: config,
API: api,
}, nil
},
"tags": func() (mcli.Command, error) {
return &TagsCommand{
Ui: ui,
Config: config,
API: api,
}, nil
},
}
}
15 changes: 15 additions & 0 deletions cli/commands_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package cli

import (
"testing"
)

func TestCommands(t *testing.T) {
keys := []string{"push", "pull", "tags", "init"}
for _, command := range keys {
_, err := commands[command]()
if err != nil {
t.Errorf("%s command returned error %s", command, err.Error())
}
}
}
117 changes: 117 additions & 0 deletions cli/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package cli

import (
"encoding/json"
"errors"
"github.com/weynsee/go-phrase/phrase"
"os"
)

// Config stores the default values for some of the properties of the PhraseApp API client
type Config struct {
path string

// Project auth token. You can find the auth token in your project overview or project settings form.
Secret string `json:"secret"`
// Default locale your PhraseApp project (default is en).
DefaultLocale string `json:"default_locale"`
// Set a domain for use with Gettext translation files (default is phrase).
Domain string `json:"domain,omitempty"`
// Specify a format that should be used as the default format when downloading files (default is yml).
Format string `json:"format,omitempty"`
// Set the target directly to store your localization files retrieved by phrase pull. Allows placeholders: https://github.com/phrase/phrase#allowed-placeholders-for-advanced-configuration
TargetDirectory string `json:"target_directory,omitempty"`
// Set the directory that contains your source locales (used by the go-phrase push command). Allows placeholders: https://github.com/phrase/phrase#allowed-placeholders-for-advanced-configuration
LocaleDirectory string `json:"locale_directory,omitempty"`
// Set the filename for files you download from PhraseApp via phrase pull. Allows placeholders: https://github.com/phrase/phrase#allowed-placeholders-for-advanced-configuration
LocaleFilename string `json:"locale_filename,omitempty"`
// Set the encoding for your localization files to UTF-8, UTF-16 or Latin-1. Please note that the encodings only work for a handful of formats like IOS .strings or Java .properties. The default will be UTF-8. If none is provided the default encoding of the formats is used.
Encoding string `json:"encoding,omitempty"`
}

// Config stores locale specific configuration options
type LocaleConfig struct {
Config
format
}

// NewConfig returns a Config instance. Its properties will be
// populated from the file found in the path argument
// (assumed to be in a certain JSON format), and some properties
// will have default values assigned.
func NewConfig(path string) (*Config, error) {
config := new(Config)
config.path = path
if _, err := os.Stat(path); err == nil {
// if file exists, initialize the object with its contents
f, err := os.Open(path)
if err != nil {
return nil, err
}
if err = json.NewDecoder(f).Decode(config); err != nil {
return nil, err
}
}

// defaults
if config.Domain == "" {
config.Domain = "phrase"
}
if config.DefaultLocale == "" {
config.DefaultLocale = "en"
}

return config, nil
}

// ForLocale returns a LocaleConfig for the given Locale.
func (c *Config) ForLocale(l *phrase.Locale) *LocaleConfig {
return newLocaleConfig(c, l)
}

// Valid validates the current config to check whether all values are valid.
func (c *Config) Valid() error {
_, found := formats[c.Format]
if !found {
return errors.New("Unrecognized format: " + c.Format)
}
return nil
}

func newLocaleConfig(c *Config, l *phrase.Locale) *LocaleConfig {
format := formats[c.Format]
lc := &LocaleConfig{*c, format}
if lc.LocaleDirectory = replacePlaceholders(c.LocaleDirectory, c, l); lc.LocaleDirectory == "" {
lc.LocaleDirectory = format.directoryForLocale(c, l)
}
if lc.LocaleFilename = replacePlaceholders(c.LocaleFilename, c, l); lc.LocaleFilename == "" {
lc.LocaleFilename = format.filenameForLocale(c, l)
}
if lc.TargetDirectory = c.TargetDirectory; lc.TargetDirectory == "" {
lc.TargetDirectory = format.properties().targetDirectory
if lc.TargetDirectory == "" {
lc.TargetDirectory = "phrase/locales/"
}
}
return lc
}

// Save saves current values of the properties of the Config
// instance to disk.
func (c *Config) Save() error {
f, err := os.OpenFile(c.path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0660)
if err != nil {
return err
}
bytes, err := json.MarshalIndent(c, "", " ")
if err != nil {
return err
}

_, err = f.Write(bytes)
if err != nil {
return err
}

return f.Close()
}
Loading

0 comments on commit e3bc223

Please sign in to comment.