Merge lp:~thumper/juju-core/support-string-slice-args into lp:~go-bot/juju-core/trunk

Proposed by Tim Penhey
Status: Merged
Approved by: Tim Penhey
Approved revision: no longer in the source branch.
Merged at revision: 2166
Proposed branch: lp:~thumper/juju-core/support-string-slice-args
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 196 lines (+187/-0)
2 files modified
cmd/args.go (+36/-0)
cmd/args_test.go (+151/-0)
To merge this branch: bzr merge lp:~thumper/juju-core/support-string-slice-args
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+199576@code.launchpad.net

Commit message

Add gnuflag support for []string

Add a StringsValue type for gnuflag parsing.

This allows comma separated values to be used as a single arg, and they are
split up into the []string passed in to the NewStringsValue function.

https://codereview.appspot.com/39150045/

Description of the change

Add gnuflag support for []string

Add a StringsValue type for gnuflag parsing.

This allows comma separated values to be used as a single arg, and they are
split up into the []string passed in to the NewStringsValue function.

https://codereview.appspot.com/39150045/

To post a comment you must log in.
Revision history for this message
Tim Penhey (thumper) wrote :

Reviewers: mp+199576_code.launchpad.net,

Message:
Please take a look.

Description:
Add gnuflag support for []string

Add a StringsValue type for gnuflag parsing.

This allows comma separated values to be used as a single arg, and they
are
split up into the []string passed in to the NewStringsValue function.

https://code.launchpad.net/~thumper/juju-core/support-string-slice-args/+merge/199576

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/39150045/

Affected files (+157, -0 lines):
   A [revision details]
   A cmd/args.go
   A cmd/args_test.go

Revision history for this message
Roger Peppe (rogpeppe) wrote :

Looks reasonable - some suggestions below.

https://codereview.appspot.com/39150045/diff/1/cmd/args.go
File cmd/args.go (right):

https://codereview.appspot.com/39150045/diff/1/cmd/args.go#newcode15
cmd/args.go:15: type StringsValue struct {
why not:

type StringsValue []string

?

https://codereview.appspot.com/39150045/diff/1/cmd/args.go#newcode21
cmd/args.go:21: func NewStringsValue(defaultValue []string, target
*[]string) *StringsValue {
If the type is as above, this function has less value.

https://codereview.appspot.com/39150045/diff/1/cmd/args.go#newcode31
cmd/args.go:31: trimmed := strings.TrimSpace(v)
I'm not sure I'd do this - I think it's not a bad thing to be able to
include spaces.

If you do decide it's necessary, this behaviour should at least be
documented.

https://codereview.appspot.com/39150045/diff/1/cmd/args.go#newcode32
cmd/args.go:32: if trimmed != "" {
I definitely wouldn't do this.
It's perfectly reasonable to want an empty item in a list.

https://codereview.appspot.com/39150045/

Revision history for this message
Tim Penhey (thumper) wrote :

Please take a look.

https://codereview.appspot.com/39150045/diff/1/cmd/args.go
File cmd/args.go (right):

https://codereview.appspot.com/39150045/diff/1/cmd/args.go#newcode15
cmd/args.go:15: type StringsValue struct {
On 2013/12/18 23:10:33, rog wrote:
> why not:

> type StringsValue []string

I ended up basing this on the constraints.Value work.

Using the type alias now.

https://codereview.appspot.com/39150045/diff/1/cmd/args.go#newcode21
cmd/args.go:21: func NewStringsValue(defaultValue []string, target
*[]string) *StringsValue {
On 2013/12/18 23:10:33, rog wrote:
> If the type is as above, this function has less value.

It is still necessary as it handles the cast and the assignment of
values.

https://codereview.appspot.com/39150045/diff/1/cmd/args.go#newcode31
cmd/args.go:31: trimmed := strings.TrimSpace(v)
On 2013/12/18 23:10:33, rog wrote:
> I'm not sure I'd do this - I think it's not a bad thing to be able to
include
> spaces.

> If you do decide it's necessary, this behaviour should at least be
documented.

well, it was only trimming off leading and trailing spaces, but fair
enough

https://codereview.appspot.com/39150045/diff/1/cmd/args.go#newcode32
cmd/args.go:32: if trimmed != "" {
On 2013/12/18 23:10:33, rog wrote:
> I definitely wouldn't do this.
> It's perfectly reasonable to want an empty item in a list.

/me shrugs

ok.

https://codereview.appspot.com/39150045/

Revision history for this message
Roger Peppe (rogpeppe) wrote :

LGTM, thanks.
One suggestion below.

https://codereview.appspot.com/39150045/diff/10004/cmd/args_test.go
File cmd/args_test.go (right):

https://codereview.appspot.com/39150045/diff/10004/cmd/args_test.go#newcode21
cmd/args_test.go:21: func (*ArgsSuite) TestNewStringsValue(c *gc.C) {
It'd be quite nice to see at least one test that tests it as it is
intended to be used (in a FlagSet). ISTM that this test could be easily
adapted to do that.

https://codereview.appspot.com/39150045/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'cmd/args.go'
2--- cmd/args.go 1970-01-01 00:00:00 +0000
3+++ cmd/args.go 2013-12-19 22:35:21 +0000
4@@ -0,0 +1,36 @@
5+// Copyright 2013 Canonical Ltd.
6+// Licensed under the AGPLv3, see LICENCE file for details.
7+
8+package cmd
9+
10+import (
11+ "strings"
12+
13+ "launchpad.net/gnuflag"
14+)
15+
16+// StringsValue implements gnuflag.Value for a comma separated list of
17+// strings. This allows flags to be created where the target is []string, and
18+// the caller is after comma separated values.
19+type StringsValue []string
20+
21+var _ gnuflag.Value = (*StringsValue)(nil)
22+
23+// NewStringsValue is used to create the type passed into the gnuflag.FlagSet Var function.
24+// f.Var(cmd.NewStringsValue(defaultValue, &someMember), "name", "help")
25+func NewStringsValue(defaultValue []string, target *[]string) *StringsValue {
26+ value := (*StringsValue)(target)
27+ *value = defaultValue
28+ return value
29+}
30+
31+// Implements gnuflag.Value Set.
32+func (v *StringsValue) Set(s string) error {
33+ *v = strings.Split(s, ",")
34+ return nil
35+}
36+
37+// Implements gnuflag.Value String.
38+func (v *StringsValue) String() string {
39+ return strings.Join(*v, ",")
40+}
41
42=== added file 'cmd/args_test.go'
43--- cmd/args_test.go 1970-01-01 00:00:00 +0000
44+++ cmd/args_test.go 2013-12-19 22:35:21 +0000
45@@ -0,0 +1,151 @@
46+// Copyright 2013 Canonical Ltd.
47+// Licensed under the AGPLv3, see LICENCE file for details.
48+
49+package cmd_test
50+
51+import (
52+ "fmt"
53+ "io/ioutil"
54+
55+ "launchpad.net/gnuflag"
56+ gc "launchpad.net/gocheck"
57+
58+ "launchpad.net/juju-core/cmd"
59+ "launchpad.net/juju-core/testing/testbase"
60+)
61+
62+type ArgsSuite struct {
63+ testbase.LoggingSuite
64+}
65+
66+var _ = gc.Suite(&ArgsSuite{})
67+
68+func (*ArgsSuite) TestFlagsUsage(c *gc.C) {
69+ for i, test := range []struct {
70+ message string
71+ defaultValue []string
72+ args []string
73+ expectedValue []string
74+ }{{
75+ message: "nil default and no arg",
76+ }, {
77+ message: "default value and not set by args",
78+ defaultValue: []string{"foo", "bar"},
79+ expectedValue: []string{"foo", "bar"},
80+ }, {
81+ message: "no value set by args",
82+ args: []string{"--value", "foo,bar"},
83+ expectedValue: []string{"foo", "bar"},
84+ }, {
85+ message: "default value and set by args",
86+ defaultValue: []string{"omg"},
87+ args: []string{"--value", "foo,bar"},
88+ expectedValue: []string{"foo", "bar"},
89+ }} {
90+ c.Log(fmt.Sprintf("%v: %s", i, test.message))
91+ f := gnuflag.NewFlagSet("test", gnuflag.ContinueOnError)
92+ f.SetOutput(ioutil.Discard)
93+ var value []string
94+ f.Var(cmd.NewStringsValue(test.defaultValue, &value), "value", "help")
95+ err := f.Parse(false, test.args)
96+ c.Check(err, gc.IsNil)
97+ c.Check(value, gc.DeepEquals, test.expectedValue)
98+ }
99+}
100+
101+func (*ArgsSuite) TestNewStringsValue(c *gc.C) {
102+ for i, test := range []struct {
103+ message string
104+ defaultValue []string
105+ }{{
106+ message: "null default",
107+ }, {
108+ message: "empty default",
109+ defaultValue: []string{},
110+ }, {
111+ message: "single value",
112+ defaultValue: []string{"foo"},
113+ }, {
114+ message: "multiple values",
115+ defaultValue: []string{"foo", "bar", "baz"},
116+ }} {
117+ c.Log(fmt.Sprintf("%v: %s", i, test.message))
118+ var underlyingValue []string
119+ _ = cmd.NewStringsValue(test.defaultValue, &underlyingValue)
120+ c.Assert(underlyingValue, gc.DeepEquals, test.defaultValue)
121+ }
122+}
123+
124+func (*ArgsSuite) TestSet(c *gc.C) {
125+ for i, test := range []struct {
126+ message string
127+ arg string
128+ expected []string
129+ }{{
130+ message: "empty",
131+ expected: []string{""},
132+ }, {
133+ message: "just whitespace",
134+ arg: " ",
135+ expected: []string{" "},
136+ }, {
137+ message: "whitespace and comma",
138+ arg: " , ",
139+ expected: []string{" ", " "},
140+ }, {
141+ message: "single value",
142+ arg: "foo",
143+ expected: []string{"foo"},
144+ }, {
145+ message: "single value with comma",
146+ arg: "foo,",
147+ expected: []string{"foo", ""},
148+ }, {
149+ message: "single value with whitespace",
150+ arg: " foo ",
151+ expected: []string{" foo "},
152+ }, {
153+ message: "multiple values",
154+ arg: "foo,bar,baz",
155+ expected: []string{"foo", "bar", "baz"},
156+ }, {
157+ message: "multiple values with spaces",
158+ arg: "foo, bar, baz",
159+ expected: []string{"foo", " bar", " baz"},
160+ }} {
161+ c.Log(fmt.Sprintf("%v: %s", i, test.message))
162+ var result []string
163+ value := cmd.NewStringsValue(nil, &result)
164+ error := value.Set(test.arg)
165+ c.Check(error, gc.IsNil)
166+ c.Check(result, gc.DeepEquals, test.expected)
167+ }
168+}
169+
170+func (*ArgsSuite) TestString(c *gc.C) {
171+ for i, test := range []struct {
172+ message string
173+ target []string
174+ expected string
175+ }{{
176+ message: "null",
177+ expected: "",
178+ }, {
179+ message: "empty",
180+ target: []string{},
181+ expected: "",
182+ }, {
183+ message: "single value",
184+ target: []string{"foo"},
185+ expected: "foo",
186+ }, {
187+ message: "multiple values",
188+ target: []string{"foo", "bar", "baz"},
189+ expected: "foo,bar,baz",
190+ }} {
191+ c.Log(fmt.Sprintf("%v: %s", i, test.message))
192+ var temp []string
193+ value := cmd.NewStringsValue(test.target, &temp)
194+ c.Assert(value.String(), gc.Equals, test.expected)
195+ }
196+}

Subscribers

People subscribed via source and target branches

to status/vote changes: