-
Notifications
You must be signed in to change notification settings - Fork 6
bucket
- A
Go::Bucket
fulfills the contract ofGo::Model
Overriding method_missing
is uncouth. It screws up your stack traces, muddies your interface and leads to unassertive code:
```ruby
Settings.defcon = 3
# ... elsewhere ...
if Settings.def_con.to_i <= 1
WOPR.launch_nukes!
end
```
Here, mispelling def_con
as defcon
(along with being wussy about the attribute's type and negligent about prescribing a default value) leads to the destruction of humanity.
Nonetheless, for several reasons (prudent laziness, handling configuration of an external component, etc) it's important to handle arbitrary attributes uniformly.
So the rule is that you can get or set anything you like using []
and []=
respectively, no need to define it first:
Settings[:whatever] = 3
Settings[:whatever] #=> 3
You don't get any magic, and the ugly accessor leaves you in no doubt that you're being lazy (ain't judging, just sayin'). When you define a setting you get accessors for that attribute and all associated magic:
Settings.option :defcon, Integer, :doc => 'Current NORAD defense condition', :default => 5, :validates => { :in => 1..5 }
Settings.defcon #=> 5
Settings.defcon = 0
Settings.validate! # raises a validation exception
Defined fields' magic works whichever form of access you use -- here, type-converting the value on assignment:
Settings[:defcon] = '5' #=> 5
Settings.defcon = '5' #=> 5
Settings.receive_defcon('5') #=> 5
Keys be lower-cased identifiers: they should match /\A([a-z][a-z0-9\_]*)\z/
.
Hash.new {|h,k| h[k] = Hash.new(&h.default_proc)}
Can retrieve keys as 'x.y.z' meaning 'foo[x][y][z]'.
-
On a
get
, will- return the value, if
foo[:x][:y][:z]
exists - return nil, if either
foo[:x]
orfoo[:x][:y]
is unset. It will not createfoo[:x]
orfoo[:x][:y]
. - raise an error if either
foo[:x]
orfoo[:x][:y]
is set to a value that does not respond to[]
- return the value, if
-
On a
set
, will- raise an error if either
foo[:x]
orfoo[:x][:y]
is set to a value that doesn't respond to[]
- set and return the value; any intermediate buckets (
foo[:x]
andfoo[:x][:y]
) will be created if they don't exist.
- raise an error if either
-
obj.deep_get(:foo, :bar, :baz) #
-
obj.deep_set(:foo, :bar, :baz, val) # sets foo bar baz.
- where property has a type, it uses that type
- otherwise it uses Mash, and calls [] on it
-
TD votes NO on magic recursive hash behavior
{ :buck1 => { :buck2 => { :k3 => 33, :ehsh => {}, :arr => [11, 12, 13] }, :cars => [{ :model => 'ford', :cylinders => 8 }, { :model => 'buick', :cylinders => 6 }], } :buck3 => { :buck4 => { :k4 => 44 } :k5 => nil, } :k6 => 69 }
-
obj[:buck3]
# b{ :buck4=>b{ :k4=>44 },:k5=>nil } -- it's a bucket -
obj[:buck5] = Hash.new
-
obj[:buck5].class
# Bucket -- it's converted to bucket -
obj[:xxx]
# nil -- it's not there -
obj[:xxx][:yyy][:zzz]
# fails -- it doesn't try to index into the nilobj[:xxx]
cell. -
obj[:xxx]
# nil -- it didn't create theobj[:xxx]
object when we tried to read on the previous line.c.options_for 'wheels', 'interior.fabric', 'interior.carpeting'
c[:wheels] # c{ } c[:wheels][:whitewall] # nil c[:wheels][:whitewall] = true # true
c.interior.fabric.color
-
obj[:k6][:bomb]
# raises ArgumentError;obj[:k6]
is not a bucket. -
obj[:'hello-there'] # undefined;
'hello-there'` is not a valid identifier. -
obj[:buck3][:buck4][:k5] = 55
obj[:buck3]
# b{ :buck4=>b{ :k4=>44 },:k5 => 55} -
obj[:f][:g][:h] = 7
obj[:f]
# b{ :g => b{ :h => 7 } } -
obj[:foo][:bar][:baz] ||= 1
-- hard ?I think? -
obj[:foo]
-- nil -
obj[:foo][:bar]
-- raise -
obj[:foo][:bar] = 3
--- now obj[:foo][:bar] is 3
- and obj[:foo] is a ?dsl_object?? but
obj[:foo][:bar]
Suppose obj[:foo]
is set to a
Seems clear these should do the right thing:
obj.merge
obj.reverse_merge
obj.keys
obj.values
- ... and a few more
Also:
-
obj.to_a
? -
obj.each
? - other crazy Enumerable properties?
- figure out the method structure for
- read/write/unset of attributes when Hash vs Accessors vs Instance Variables
- reader/writer: raw vs. hooks, dirty, etc.
Configliere lets you define arbitrary attributes of a param, notably:
[:description] Documentation for the param, used in the --help message
[:default] Sets a default value (applied immediately)
[:env_var] Environment variable to adopt (applied immediately, and after +:default+)
[:type] Converts param's value to the given type, just before the finally block is called
[:finally] Block of code to postprocess settings or handle complex configuration.
[:required] Raises an error if, at the end of calling resolve!, the param's value is nil.