Merge lp:~niemeyer/juju-core/schema-field-map-defaults into lp:~juju/juju-core/trunk
- schema-field-map-defaults
- Merge into trunk
Proposed by
Gustavo Niemeyer
Status: | Merged |
---|---|
Merged at revision: | 318 |
Proposed branch: | lp:~niemeyer/juju-core/schema-field-map-defaults |
Merge into: | lp:~juju/juju-core/trunk |
Prerequisite: | lp:~niemeyer/juju-core/schema-string-map |
Diff against target: |
382 lines (+113/-75) 7 files modified
charm/config.go (+4/-1) charm/meta.go (+11/-12) environs/config/config.go (+12/-12) environs/dummy/environs.go (+3/-3) environs/ec2/config.go (+12/-19) schema/schema.go (+39/-18) schema/schema_test.go (+32/-10) |
To merge this branch: | bzr merge lp:~niemeyer/juju-core/schema-field-map-defaults |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
The Go Language Gophers | Pending | ||
Review via email:
|
Commit message
Description of the change
schema: support defaults in FieldMap
To post a comment you must log in.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Gustavo Niemeyer (niemeyer) wrote : | # |
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Gustavo Niemeyer (niemeyer) wrote : | # |
Please take a look.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Roger Peppe (rogpeppe) wrote : | # |
On 2012/07/20 00:32:02, niemeyer wrote:
> Please take a look.
excellent stuff; make schema work for its living!
LGTM
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Roger Peppe (rogpeppe) wrote : | # |
https:/
File schema/schema.go (right):
https:/
schema/
nice.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
William Reade (fwereade) wrote : | # |
LGTM, very nice.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Gustavo Niemeyer (niemeyer) wrote : | # |
*** Submitted:
schema: support defaults in FieldMap
R=rog, fwereade
CC=
https:/
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'charm/config.go' | |||
2 | --- charm/config.go 2012-07-19 22:31:15 +0000 | |||
3 | +++ charm/config.go 2012-07-20 00:33:23 +0000 | |||
4 | @@ -130,7 +130,10 @@ | |||
5 | 130 | "default": schema.OneOf(schema.String(), schema.Int(), schema.Float(), schema.Bool()), | 130 | "default": schema.OneOf(schema.String(), schema.Int(), schema.Float(), schema.Bool()), |
6 | 131 | "description": schema.String(), | 131 | "description": schema.String(), |
7 | 132 | }, | 132 | }, |
9 | 133 | schema.Optional{"default", "description"}, | 133 | schema.Defaults{ |
10 | 134 | "default": schema.Omit, | ||
11 | 135 | "description": schema.Omit, | ||
12 | 136 | }, | ||
13 | 134 | ) | 137 | ) |
14 | 135 | 138 | ||
15 | 136 | var configSchema = schema.FieldMap( | 139 | var configSchema = schema.FieldMap( |
16 | 137 | 140 | ||
17 | === modified file 'charm/meta.go' | |||
18 | --- charm/meta.go 2012-07-19 22:31:15 +0000 | |||
19 | +++ charm/meta.go 2012-07-20 00:33:23 +0000 | |||
20 | @@ -155,10 +155,6 @@ | |||
21 | 155 | return | 155 | return |
22 | 156 | } | 156 | } |
23 | 157 | 157 | ||
24 | 158 | // Optional values are context-sensitive and/or have | ||
25 | 159 | // defaults, which is different than what FieldMap can | ||
26 | 160 | // readily support. So just do it here first, then | ||
27 | 161 | // coerce to the real schema. | ||
28 | 162 | v, err = mapC.Coerce(v, path) | 158 | v, err = mapC.Coerce(v, path) |
29 | 163 | if err != nil { | 159 | if err != nil { |
30 | 164 | return | 160 | return |
31 | @@ -167,12 +163,6 @@ | |||
32 | 167 | if _, ok := m["limit"]; !ok { | 163 | if _, ok := m["limit"]; !ok { |
33 | 168 | m["limit"] = c.limit | 164 | m["limit"] = c.limit |
34 | 169 | } | 165 | } |
35 | 170 | if _, ok := m["optional"]; !ok { | ||
36 | 171 | m["optional"] = false | ||
37 | 172 | } | ||
38 | 173 | if _, ok := m["scope"]; !ok { | ||
39 | 174 | m["scope"] = ScopeGlobal | ||
40 | 175 | } | ||
41 | 176 | return ifaceSchema.Coerce(m, path) | 166 | return ifaceSchema.Coerce(m, path) |
42 | 177 | } | 167 | } |
43 | 178 | 168 | ||
44 | @@ -183,7 +173,10 @@ | |||
45 | 183 | "scope": schema.OneOf(schema.Const(ScopeGlobal), schema.Const(ScopeContainer)), | 173 | "scope": schema.OneOf(schema.Const(ScopeGlobal), schema.Const(ScopeContainer)), |
46 | 184 | "optional": schema.Bool(), | 174 | "optional": schema.Bool(), |
47 | 185 | }, | 175 | }, |
49 | 186 | schema.Optional{"scope"}, | 176 | schema.Defaults{ |
50 | 177 | "scope": ScopeGlobal, | ||
51 | 178 | "optional": false, | ||
52 | 179 | }, | ||
53 | 187 | ) | 180 | ) |
54 | 188 | 181 | ||
55 | 189 | var charmSchema = schema.FieldMap( | 182 | var charmSchema = schema.FieldMap( |
56 | @@ -197,5 +190,11 @@ | |||
57 | 197 | "revision": schema.Int(), // Obsolete | 190 | "revision": schema.Int(), // Obsolete |
58 | 198 | "subordinate": schema.Bool(), | 191 | "subordinate": schema.Bool(), |
59 | 199 | }, | 192 | }, |
61 | 200 | schema.Optional{"provides", "requires", "peers", "revision", "subordinate"}, | 193 | schema.Defaults{ |
62 | 194 | "provides": schema.Omit, | ||
63 | 195 | "requires": schema.Omit, | ||
64 | 196 | "peers": schema.Omit, | ||
65 | 197 | "revision": schema.Omit, | ||
66 | 198 | "subordinate": schema.Omit, | ||
67 | 199 | }, | ||
68 | 201 | ) | 200 | ) |
69 | 202 | 201 | ||
70 | === modified file 'environs/config/config.go' | |||
71 | --- environs/config/config.go 2012-07-19 22:31:15 +0000 | |||
72 | +++ environs/config/config.go 2012-07-20 00:33:23 +0000 | |||
73 | @@ -23,20 +23,21 @@ | |||
74 | 23 | m: m.(map[string]interface{}), | 23 | m: m.(map[string]interface{}), |
75 | 24 | t: make(map[string]interface{}), | 24 | t: make(map[string]interface{}), |
76 | 25 | } | 25 | } |
78 | 26 | if s, _ := c.m["default-series"].(string); s == "" { | 26 | |
79 | 27 | if c.m["default-series"].(string) == "" { | ||
80 | 27 | c.m["default-series"] = CurrentSeries | 28 | c.m["default-series"] = CurrentSeries |
81 | 28 | } | 29 | } |
82 | 29 | 30 | ||
83 | 30 | // Load authorized-keys-path onto authorized-keys, if necessary. | 31 | // Load authorized-keys-path onto authorized-keys, if necessary. |
86 | 31 | path, _ := c.m["authorized-keys-path"].(string) | 32 | path := c.m["authorized-keys-path"].(string) |
87 | 32 | keys, _ := c.m["authorized-keys"].(string) | 33 | keys := c.m["authorized-keys"].(string) |
88 | 33 | if path != "" || keys == "" { | 34 | if path != "" || keys == "" { |
89 | 34 | c.m["authorized-keys"], err = authorizedKeys(path) | 35 | c.m["authorized-keys"], err = authorizedKeys(path) |
90 | 35 | if err != nil { | 36 | if err != nil { |
91 | 36 | return nil, err | 37 | return nil, err |
92 | 37 | } | 38 | } |
93 | 38 | delete(c.m, "authorized-keys-path") | ||
94 | 39 | } | 39 | } |
95 | 40 | delete(c.m, "authorized-keys-path") | ||
96 | 40 | 41 | ||
97 | 41 | // Check if there are any required fields that are empty. | 42 | // Check if there are any required fields that are empty. |
98 | 42 | for _, attr := range []string{"name", "type", "default-series", "authorized-keys"} { | 43 | for _, attr := range []string{"name", "type", "default-series", "authorized-keys"} { |
99 | @@ -103,11 +104,10 @@ | |||
100 | 103 | "authorized-keys-path": schema.String(), | 104 | "authorized-keys-path": schema.String(), |
101 | 104 | } | 105 | } |
102 | 105 | 106 | ||
111 | 106 | var checker = schema.FieldMap( | 107 | var defaults = schema.Defaults{ |
112 | 107 | fields, | 108 | "default-series": CurrentSeries, |
113 | 108 | []string{ | 109 | "authorized-keys": "", |
114 | 109 | "default-series", | 110 | "authorized-keys-path": "", |
115 | 110 | "authorized-keys", | 111 | } |
116 | 111 | "authorized-keys-path", | 112 | |
117 | 112 | }, | 113 | var checker = schema.FieldMap(fields, defaults) |
110 | 113 | ) | ||
118 | 114 | 114 | ||
119 | === modified file 'environs/dummy/environs.go' | |||
120 | --- environs/dummy/environs.go 2012-07-19 22:31:15 +0000 | |||
121 | +++ environs/dummy/environs.go 2012-07-20 00:33:23 +0000 | |||
122 | @@ -218,8 +218,8 @@ | |||
123 | 218 | "zookeeper": schema.Bool(), | 218 | "zookeeper": schema.Bool(), |
124 | 219 | "broken": schema.Bool(), | 219 | "broken": schema.Bool(), |
125 | 220 | }, | 220 | }, |
128 | 221 | []string{ | 221 | schema.Defaults{ |
129 | 222 | "broken", | 222 | "broken": false, |
130 | 223 | }, | 223 | }, |
131 | 224 | ) | 224 | ) |
132 | 225 | 225 | ||
133 | @@ -233,7 +233,7 @@ | |||
134 | 233 | Config: cfg, | 233 | Config: cfg, |
135 | 234 | zookeeper: m1["zookeeper"].(bool), | 234 | zookeeper: m1["zookeeper"].(bool), |
136 | 235 | } | 235 | } |
138 | 236 | ecfg.broken, _ = m1["broken"].(bool) | 236 | ecfg.broken = m1["broken"].(bool) |
139 | 237 | return ecfg, nil | 237 | return ecfg, nil |
140 | 238 | } | 238 | } |
141 | 239 | 239 | ||
142 | 240 | 240 | ||
143 | === modified file 'environs/ec2/config.go' | |||
144 | --- environs/ec2/config.go 2012-07-19 22:31:15 +0000 | |||
145 | +++ environs/ec2/config.go 2012-07-20 00:33:23 +0000 | |||
146 | @@ -25,11 +25,12 @@ | |||
147 | 25 | "region": schema.String(), | 25 | "region": schema.String(), |
148 | 26 | "control-bucket": schema.String(), | 26 | "control-bucket": schema.String(), |
149 | 27 | "public-bucket": schema.String(), | 27 | "public-bucket": schema.String(), |
155 | 28 | }, []string{ | 28 | }, |
156 | 29 | "access-key", | 29 | schema.Defaults{ |
157 | 30 | "secret-key", | 30 | "access-key": "", |
158 | 31 | "region", | 31 | "secret-key": "", |
159 | 32 | "public-bucket", | 32 | "region": "us-east-1", |
160 | 33 | "public-bucket": "", | ||
161 | 33 | }, | 34 | }, |
162 | 34 | ) | 35 | ) |
163 | 35 | 36 | ||
164 | @@ -41,9 +42,9 @@ | |||
165 | 41 | m := v.(map[string]interface{}) | 42 | m := v.(map[string]interface{}) |
166 | 42 | c := &providerConfig{Config: config} | 43 | c := &providerConfig{Config: config} |
167 | 43 | c.bucket = m["control-bucket"].(string) | 44 | c.bucket = m["control-bucket"].(string) |
171 | 44 | c.publicBucket = maybeString(m["public-bucket"], "") | 45 | c.publicBucket = m["public-bucket"].(string) |
172 | 45 | c.auth.AccessKey = maybeString(m["access-key"], "") | 46 | c.auth.AccessKey = m["access-key"].(string) |
173 | 46 | c.auth.SecretKey = maybeString(m["secret-key"], "") | 47 | c.auth.SecretKey = m["secret-key"].(string) |
174 | 47 | if c.auth.AccessKey == "" || c.auth.SecretKey == "" { | 48 | if c.auth.AccessKey == "" || c.auth.SecretKey == "" { |
175 | 48 | if c.auth.AccessKey != "" { | 49 | if c.auth.AccessKey != "" { |
176 | 49 | return nil, fmt.Errorf("environment has access-key but no secret-key") | 50 | return nil, fmt.Errorf("environment has access-key but no secret-key") |
177 | @@ -57,17 +58,9 @@ | |||
178 | 57 | } | 58 | } |
179 | 58 | } | 59 | } |
180 | 59 | 60 | ||
184 | 60 | regionName := maybeString(m["region"], "us-east-1") | 61 | c.region = m["region"].(string) |
185 | 61 | if _, ok := aws.Regions[regionName]; !ok { | 62 | if _, ok := aws.Regions[c.region]; !ok { |
186 | 62 | return nil, fmt.Errorf("invalid region name %q", regionName) | 63 | return nil, fmt.Errorf("invalid region name %q", c.region) |
187 | 63 | } | 64 | } |
188 | 64 | c.region = regionName | ||
189 | 65 | return c, nil | 65 | return c, nil |
190 | 66 | } | 66 | } |
191 | 67 | |||
192 | 68 | func maybeString(x interface{}, dflt string) string { | ||
193 | 69 | if x == nil { | ||
194 | 70 | return dflt | ||
195 | 71 | } | ||
196 | 72 | return x.(string) | ||
197 | 73 | } | ||
198 | 74 | 67 | ||
199 | === modified file 'schema/schema.go' | |||
200 | --- schema/schema.go 2012-07-19 22:31:15 +0000 | |||
201 | +++ schema/schema.go 2012-07-20 00:33:23 +0000 | |||
202 | @@ -305,8 +305,14 @@ | |||
203 | 305 | return out, nil | 305 | return out, nil |
204 | 306 | } | 306 | } |
205 | 307 | 307 | ||
206 | 308 | // Omit is a marker for FieldMap and StructFieldMap defaults parameter. | ||
207 | 309 | // If a field is not present in the map and defaults to Omit, the missing | ||
208 | 310 | // field will be ommitted from the coerced map as well. | ||
209 | 311 | var Omit omit | ||
210 | 312 | type omit struct{} | ||
211 | 313 | |||
212 | 308 | type Fields map[string]Checker | 314 | type Fields map[string]Checker |
214 | 309 | type Optional []string | 315 | type Defaults map[string]interface{} |
215 | 310 | 316 | ||
216 | 311 | // FieldMap returns a Checker that accepts a map value with defined | 317 | // FieldMap returns a Checker that accepts a map value with defined |
217 | 312 | // string keys. Every key has an independent checker associated, | 318 | // string keys. Every key has an independent checker associated, |
218 | @@ -314,32 +320,27 @@ | |||
219 | 314 | // individually. If a field fails to be processed, processing stops | 320 | // individually. If a field fails to be processed, processing stops |
220 | 315 | // and returns with the underlying error. | 321 | // and returns with the underlying error. |
221 | 316 | // | 322 | // |
222 | 323 | // Fields in defaults will be set to the provided value if not present | ||
223 | 324 | // in the coerced map. If the default value is schema.Omit, the field | ||
224 | 325 | // missing field will be ommitted from the coerced map as well. | ||
225 | 326 | // | ||
226 | 317 | // The coerced output value has type map[string]interface{}. | 327 | // The coerced output value has type map[string]interface{}. |
229 | 318 | func FieldMap(fields Fields, optional Optional) Checker { | 328 | func FieldMap(fields Fields, defaults Defaults) Checker { |
230 | 319 | return fieldMapC{fields, optional, false} | 329 | return fieldMapC{fields, defaults, false} |
231 | 320 | } | 330 | } |
232 | 321 | 331 | ||
233 | 322 | // StrictFieldMap returns a Checker that acts as the one returned by FieldMap, | 332 | // StrictFieldMap returns a Checker that acts as the one returned by FieldMap, |
234 | 323 | // but the Checker returns an error if it encounters an unknown key. | 333 | // but the Checker returns an error if it encounters an unknown key. |
237 | 324 | func StrictFieldMap(fields Fields, optional Optional) Checker { | 334 | func StrictFieldMap(fields Fields, defaults Defaults) Checker { |
238 | 325 | return fieldMapC{fields, optional, true} | 335 | return fieldMapC{fields, defaults, true} |
239 | 326 | } | 336 | } |
240 | 327 | 337 | ||
241 | 328 | type fieldMapC struct { | 338 | type fieldMapC struct { |
242 | 329 | fields Fields | 339 | fields Fields |
244 | 330 | optional []string | 340 | defaults Defaults |
245 | 331 | strict bool | 341 | strict bool |
246 | 332 | } | 342 | } |
247 | 333 | 343 | ||
248 | 334 | func (c fieldMapC) isOptional(key string) bool { | ||
249 | 335 | for _, k := range c.optional { | ||
250 | 336 | if k == key { | ||
251 | 337 | return true | ||
252 | 338 | } | ||
253 | 339 | } | ||
254 | 340 | return false | ||
255 | 341 | } | ||
256 | 342 | |||
257 | 343 | func (c fieldMapC) Coerce(v interface{}, path []string) (interface{}, error) { | 344 | func (c fieldMapC) Coerce(v interface{}, path []string) (interface{}, error) { |
258 | 344 | rv := reflect.ValueOf(v) | 345 | rv := reflect.ValueOf(v) |
259 | 345 | if rv.Kind() != reflect.Map { | 346 | if rv.Kind() != reflect.Map { |
260 | @@ -362,20 +363,40 @@ | |||
261 | 362 | l := rv.Len() | 363 | l := rv.Len() |
262 | 363 | out := make(map[string]interface{}, l) | 364 | out := make(map[string]interface{}, l) |
263 | 364 | for k, checker := range c.fields { | 365 | for k, checker := range c.fields { |
264 | 365 | vpath[len(vpath)-1] = k | ||
265 | 366 | var value interface{} | 366 | var value interface{} |
266 | 367 | valuev := rv.MapIndex(reflect.ValueOf(k)) | 367 | valuev := rv.MapIndex(reflect.ValueOf(k)) |
267 | 368 | if valuev.IsValid() { | 368 | if valuev.IsValid() { |
268 | 369 | value = valuev.Interface() | 369 | value = valuev.Interface() |
271 | 370 | } else if c.isOptional(k) { | 370 | } else if dflt, ok := c.defaults[k]; ok { |
272 | 371 | continue | 371 | if dflt == Omit { |
273 | 372 | continue | ||
274 | 373 | } | ||
275 | 374 | value = dflt | ||
276 | 372 | } | 375 | } |
277 | 376 | vpath[len(vpath)-1] = k | ||
278 | 373 | newv, err := checker.Coerce(value, vpath) | 377 | newv, err := checker.Coerce(value, vpath) |
279 | 374 | if err != nil { | 378 | if err != nil { |
280 | 375 | return nil, err | 379 | return nil, err |
281 | 376 | } | 380 | } |
282 | 377 | out[k] = newv | 381 | out[k] = newv |
283 | 378 | } | 382 | } |
284 | 383 | for k, v := range c.defaults { | ||
285 | 384 | if v == Omit { | ||
286 | 385 | continue | ||
287 | 386 | } | ||
288 | 387 | if _, ok := out[k]; !ok { | ||
289 | 388 | checker, ok := c.fields[k] | ||
290 | 389 | if !ok { | ||
291 | 390 | return nil, fmt.Errorf("got default value for unknown field %q", k) | ||
292 | 391 | } | ||
293 | 392 | vpath[len(vpath)-1] = k | ||
294 | 393 | newv, err := checker.Coerce(v, vpath) | ||
295 | 394 | if err != nil { | ||
296 | 395 | return nil, err | ||
297 | 396 | } | ||
298 | 397 | out[k] = newv | ||
299 | 398 | } | ||
300 | 399 | } | ||
301 | 379 | return out, nil | 400 | return out, nil |
302 | 380 | } | 401 | } |
303 | 381 | 402 | ||
304 | 382 | 403 | ||
305 | === modified file 'schema/schema_test.go' | |||
306 | --- schema/schema_test.go 2012-07-19 22:31:15 +0000 | |||
307 | +++ schema/schema_test.go 2012-07-20 00:33:23 +0000 | |||
308 | @@ -240,7 +240,7 @@ | |||
309 | 240 | out, err := sch.Coerce(map[string]interface{}{"a": "A", "b": "B"}, aPath) | 240 | out, err := sch.Coerce(map[string]interface{}{"a": "A", "b": "B"}, aPath) |
310 | 241 | 241 | ||
311 | 242 | c.Assert(err, IsNil) | 242 | c.Assert(err, IsNil) |
313 | 243 | c.Assert(out, DeepEquals, map[string]interface{}{"a": "A", "b": "B"}) | 243 | c.Assert(out, DeepEquals, map[string]interface{}{"a": "A", "b": "B", "c": "C"}) |
314 | 244 | 244 | ||
315 | 245 | out, err = sch.Coerce(42, aPath) | 245 | out, err = sch.Coerce(42, aPath) |
316 | 246 | c.Assert(out, IsNil) | 246 | c.Assert(out, IsNil) |
317 | @@ -261,7 +261,7 @@ | |||
318 | 261 | // b is optional | 261 | // b is optional |
319 | 262 | out, err = sch.Coerce(map[string]interface{}{"a": "A"}, aPath) | 262 | out, err = sch.Coerce(map[string]interface{}{"a": "A"}, aPath) |
320 | 263 | c.Assert(err, IsNil) | 263 | c.Assert(err, IsNil) |
322 | 264 | c.Assert(out, DeepEquals, map[string]interface{}{"a": "A"}) | 264 | c.Assert(out, DeepEquals, map[string]interface{}{"a": "A", "c": "C"}) |
323 | 265 | 265 | ||
324 | 266 | // First path entry shouldn't have dots in an error message. | 266 | // First path entry shouldn't have dots in an error message. |
325 | 267 | out, err = sch.Coerce(map[string]bool{"a": true}, nil) | 267 | out, err = sch.Coerce(map[string]bool{"a": true}, nil) |
326 | @@ -273,26 +273,48 @@ | |||
327 | 273 | fields := schema.Fields{ | 273 | fields := schema.Fields{ |
328 | 274 | "a": schema.Const("A"), | 274 | "a": schema.Const("A"), |
329 | 275 | "b": schema.Const("B"), | 275 | "b": schema.Const("B"), |
332 | 276 | } | 276 | "c": schema.Const("C"), |
333 | 277 | sch := schema.FieldMap(fields, schema.Optional{"b"}) | 277 | } |
334 | 278 | defaults := schema.Defaults{ | ||
335 | 279 | "b": schema.Omit, | ||
336 | 280 | "c": "C", | ||
337 | 281 | } | ||
338 | 282 | sch := schema.FieldMap(fields, defaults) | ||
339 | 278 | assertFieldMap(c, sch) | 283 | assertFieldMap(c, sch) |
340 | 279 | 284 | ||
342 | 280 | out, err := sch.Coerce(map[string]interface{}{"a": "A", "b": "B", "c": "C"}, aPath) | 285 | out, err := sch.Coerce(map[string]interface{}{"a": "A", "b": "B", "d": "D"}, aPath) |
343 | 281 | c.Assert(err, IsNil) | 286 | c.Assert(err, IsNil) |
345 | 282 | c.Assert(out, DeepEquals, map[string]interface{}{"a": "A", "b": "B"}) | 287 | c.Assert(out, DeepEquals, map[string]interface{}{"a": "A", "b": "B", "c": "C"}) |
346 | 288 | } | ||
347 | 289 | |||
348 | 290 | func (s *S) TestFieldMapDefaultInvalid(c *C) { | ||
349 | 291 | fields := schema.Fields{ | ||
350 | 292 | "a": schema.Const("A"), | ||
351 | 293 | } | ||
352 | 294 | defaults := schema.Defaults{ | ||
353 | 295 | "a": "B", | ||
354 | 296 | } | ||
355 | 297 | sch := schema.FieldMap(fields, defaults) | ||
356 | 298 | _, err := sch.Coerce(map[string]interface{}{}, aPath) | ||
357 | 299 | c.Assert(err, ErrorMatches, `<path>.a: expected "A", got "B"`) | ||
358 | 283 | } | 300 | } |
359 | 284 | 301 | ||
360 | 285 | func (s *S) TestStrictFieldMap(c *C) { | 302 | func (s *S) TestStrictFieldMap(c *C) { |
361 | 286 | fields := schema.Fields{ | 303 | fields := schema.Fields{ |
362 | 287 | "a": schema.Const("A"), | 304 | "a": schema.Const("A"), |
363 | 288 | "b": schema.Const("B"), | 305 | "b": schema.Const("B"), |
366 | 289 | } | 306 | "c": schema.Const("C"), |
367 | 290 | sch := schema.StrictFieldMap(fields, schema.Optional{"b"}) | 307 | } |
368 | 308 | defaults := schema.Defaults{ | ||
369 | 309 | "b": schema.Omit, | ||
370 | 310 | "c": "C", | ||
371 | 311 | } | ||
372 | 312 | sch := schema.StrictFieldMap(fields, defaults) | ||
373 | 291 | assertFieldMap(c, sch) | 313 | assertFieldMap(c, sch) |
374 | 292 | 314 | ||
376 | 293 | out, err := sch.Coerce(map[string]interface{}{"a": "A", "b": "B", "c": "C"}, aPath) | 315 | out, err := sch.Coerce(map[string]interface{}{"a": "A", "b": "B", "d": "D"}, aPath) |
377 | 294 | c.Assert(out, IsNil) | 316 | c.Assert(out, IsNil) |
379 | 295 | c.Assert(err, ErrorMatches, `<path>.c: expected nothing, got "C"`) | 317 | c.Assert(err, ErrorMatches, `<path>.d: expected nothing, got "D"`) |
380 | 296 | } | 318 | } |
381 | 297 | 319 | ||
382 | 298 | func (s *S) TestSchemaMap(c *C) { | 320 | func (s *S) TestSchemaMap(c *C) { |
Reviewers: mp+115863_ code.launchpad. net,
Message:
Please take a look.
Description:
schema: support defaults in FieldMap
https:/ /code.launchpad .net/~niemeyer/ juju-core/ schema- field-map- defaults/ +merge/ 115863
Requires: /code.launchpad .net/~niemeyer/ juju-core/ schema- string- map/+merge/ 115849
https:/
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/6419056/
Affected files: config/ config. go dummy/environs. go ec2/config. go schema_ test.go
A [revision details]
M charm/config.go
M charm/meta.go
M environs/
M environs/
M environs/
M schema/schema.go
M schema/