Class: Sycl::Hash
Overview
A Sycl::Hash is like a Hash, but creating one from an hash blesses any child Array or Hash objects into Sycl::Array or Sycl::Hash objects. All the normal Hash methods are supported, and automatically promote any inputs into Sycl equivalents. The following example illustrates this:
h = Sycl::Hash.new
h['a'] = { 'b' => { 'c' => 'Hello, world!' } }
puts h.a.b.c # outputs 'Hello, world!'
Hash contents can be accessed via “dot notation” (h.foo.bar means the same as h[‘bar’]). However, h.foo.bar dies if h does not exist, so get() and set() methods exist: h.get(‘foo.bar’) will return nil instead of dying if h does not exist. There is also a convenient deep_merge() that is like Hash#merge(), but also descends into and merges child nodes of the new hash.
A Sycl::Hash supports YAML preprocessing and postprocessing, and having individual nodes marked as being rendered in inline style. YAML output is also always sorted by key.
h = Sycl::Hash.from_hash({'b' => 'bravo', 'a' => 'alpha'})
h.render_inline!
h.yaml_preprocessor { |x| x.values.each { |e| e.capitalize! } }
h.yaml_postprocessor { |yaml| yaml.sub(/\A---\s+/, '') }
puts h['a'] # outputs 'alpha'
puts h.keys.first # outputs 'a' or 'b' depending on Hash order
puts h.to_yaml # outputs '{a: Alpha, b: Bravo}'
Defined Under Namespace
Classes: MockNativeType
Class Method Summary collapse
-
.[](*args) ⇒ Object
:nodoc:.
-
.from_hash(h) ⇒ Object
Create a Sycl::Array from a normal Hash or Hash-like object.
-
.load_file(f) ⇒ Object
Like Sycl::load_file(), a shortcut method to create a Sycl::Hash from loading and parsing YAML from a file.
Instance Method Summary collapse
-
#<=>(another) ⇒ Object
:nodoc:.
-
#[]=(k, v) ⇒ Object
Make sure that if we write to this hash, we promote any inputs to their Sycl equivalents.
-
#deep_merge(h) ⇒ Object
Deep merge two hashes (the new hash wins on conflicts).
-
#encode_with(coder) ⇒ Object
:nodoc:.
-
#get(path) ⇒ Object
Safe dotted notation reads: h.get(‘foo.bar’) == h[‘bar’].
-
#initialize(*args) ⇒ Hash
constructor
:nodoc:.
-
#merge!(h) ⇒ Object
:nodoc:.
-
#method(sym) ⇒ Object
:nodoc:.
- #method_missing(method_symbol, *args, &block) ⇒ Object
-
#render_inline! ⇒ Object
Make this hash, and its children, rendered in inline/flow style.
-
#render_values_inline! ⇒ Object
Keep rendering this hash in block (multi-line) style, but, make this array’s children rendered in inline/flow style.
-
#set(path, value) ⇒ Object
Dotted writes: h.set(‘foo.bar’ => ‘baz’) means h[‘bar’] = ‘baz’.
-
#store(k, v) ⇒ Object
:nodoc:.
-
#to_str ⇒ Object
:nodoc:.
-
#to_yaml(opts = {}) ⇒ Object
Render this object as YAML.
-
#update(h) ⇒ Object
:nodoc:.
-
#yaml_postprocess(yaml) ⇒ Object
:nodoc:.
-
#yaml_postprocessor(&block) ⇒ Object
Set a postprocessor hook which runs after YML is dumped, for example, via to_yaml() or Sycl::dump().
-
#yaml_preprocess! ⇒ Object
:nodoc:.
-
#yaml_preprocessor(&block) ⇒ Object
Set a preprocessor hook which runs before each time YAML is dumped, for example, via to_yaml() or Sycl::dump().
Constructor Details
#initialize(*args) ⇒ Hash
:nodoc:
412 413 414 415 416 417 |
# File 'lib/sycl.rb', line 412 def initialize(*args) # :nodoc: @yaml_preprocessor = nil @yaml_postprocessor = nil @yaml_style = nil super end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_symbol, *args, &block) ⇒ Object
475 476 477 478 479 480 481 482 483 484 485 |
# File 'lib/sycl.rb', line 475 def method_missing(method_symbol, *args, &block) key = method_symbol.to_s set = key.chomp!('=') if set self[key] = args.first elsif self.key?(key) self[key] else nil end end |
Class Method Details
.[](*args) ⇒ Object
:nodoc:
419 420 421 |
# File 'lib/sycl.rb', line 419 def self.[](*args) # :nodoc: Sycl::Hash.from_hash super end |
.from_hash(h) ⇒ Object
Create a Sycl::Array from a normal Hash or Hash-like object. Every child Array or Hash gets promoted to a Sycl::Array or Sycl::Hash.
433 434 435 436 437 |
# File 'lib/sycl.rb', line 433 def self.from_hash(h) retval = Sycl::Hash.new h.each { |k, v| retval[k] = Sycl::from_object(v) } retval end |
Instance Method Details
#<=>(another) ⇒ Object
:nodoc:
550 551 552 |
# File 'lib/sycl.rb', line 550 def <=>(another) # :nodoc: self.to_str <=> another.to_str end |
#[]=(k, v) ⇒ Object
Make sure that if we write to this hash, we promote any inputs to their Sycl equivalents. This lets dot notation, styled YAML, and other Sycl goodies continue.
444 445 446 447 448 449 |
# File 'lib/sycl.rb', line 444 def []=(k, v) # :nodoc: unless v.is_a?(Sycl::Hash) || v.is_a?(Sycl::Array) v = Sycl::from_object(v) end super end |
#deep_merge(h) ⇒ Object
Deep merge two hashes (the new hash wins on conflicts). Hash or and Array objects in the new hash are promoted to Sycl variants.
532 533 534 535 536 537 538 539 540 541 542 |
# File 'lib/sycl.rb', line 532 def deep_merge(h) self.merge(h) do |key, v1, v2| if v1.is_a?(::Hash) && v2.is_a?(Sycl::Hash) self[key].deep_merge(v2) elsif v1.is_a?(::Hash) && v2.is_a?(::Hash) self[key].deep_merge(Sycl::Hash.from_hash(v2)) else self[key] = Sycl::from_object(v2) end end end |
#encode_with(coder) ⇒ Object
:nodoc:
669 670 671 672 |
# File 'lib/sycl.rb', line 669 def encode_with(coder) # :nodoc: coder.style = Psych::Nodes::Mapping::FLOW if @yaml_style == :inline coder.represent_map nil, sort end |
#get(path) ⇒ Object
492 493 494 495 496 497 498 499 500 501 502 503 504 505 |
# File 'lib/sycl.rb', line 492 def get(path) path = path.split(/\./) if path.is_a?(String) candidate = self while !path.empty? key = path.shift if candidate[key] candidate = candidate[key] else candidate = nil last end end candidate end |
#merge!(h) ⇒ Object
:nodoc:
458 459 460 461 |
# File 'lib/sycl.rb', line 458 def merge!(h) # :nodoc: h = Sycl::Hash.from_hash(h) unless h.is_a?(Sycl::Hash) super end |
#method(sym) ⇒ Object
:nodoc:
638 639 640 |
# File 'lib/sycl.rb', line 638 def method(sym) # :nodoc: sym == :to_yaml ? MockNativeType.new : super end |
#render_inline! ⇒ Object
Make this hash, and its children, rendered in inline/flow style. The default is to render arrays in block (multi-line) style.
562 563 564 |
# File 'lib/sycl.rb', line 562 def render_inline! @yaml_style = :inline end |
#render_values_inline! ⇒ Object
Keep rendering this hash in block (multi-line) style, but, make this array’s children rendered in inline/flow style.
Example:
h = Sycl::Hash.new
h['one'] = 'two'
h['three'] = %w{four five}
h.yaml_postprocessor { |yaml| yaml.sub(/\A---\s+/, '') }
h.render_values_inline!
puts h.to_yaml # output: "one: two\nthree: [five four]"
h.render_inline!
puts h.to_yaml # output: '{one: two, three: [five four]}'
581 582 583 584 585 |
# File 'lib/sycl.rb', line 581 def render_values_inline! self.values.each do |v| v.render_inline! if v.respond_to?(:render_inline!) end end |
#set(path, value) ⇒ Object
Dotted writes: h.set(‘foo.bar’ => ‘baz’) means h[‘bar’] = ‘baz’.
This will auto-vivify any missing intervening hash keys, and also promote Hash and Array objects in the input to Scyl variants.
513 514 515 516 517 518 519 520 521 522 523 524 525 526 |
# File 'lib/sycl.rb', line 513 def set(path, value) path = path.split(/\./) if path.is_a?(String) target = self while path.size > 1 key = path.shift if !(target.key?(key) && target[key].is_a?(::Hash)) target[key] = Sycl::Hash.new else target[key] = Sycl::Hash.from_hash(target[key]) end target = target[key] end target[path.first] = value end |
#store(k, v) ⇒ Object
:nodoc:
451 452 453 454 455 456 |
# File 'lib/sycl.rb', line 451 def store(k, v) # :nodoc: unless v.is_a?(Sycl::Hash) || v.is_a?(Sycl::Array) v = Sycl::from_object(v) end super end |
#to_str ⇒ Object
:nodoc:
554 555 556 |
# File 'lib/sycl.rb', line 554 def to_str # :nodoc: self.keys.sort.first end |
#to_yaml(opts = {}) ⇒ Object
Render this object as YAML. Before rendering, run the object through any yaml_preprocessor() code block. After rendering, filter the YAML text through any yaml_postprocessor() code block.
Nodes marked with render_inline!() or render_values_inline!() will be output in flow/inline style, all hashes and arrays will be sorted, and we set a long line width to more or less support line wrap under the Psych library.
652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 |
# File 'lib/sycl.rb', line 652 def to_yaml(opts = {}) yaml_preprocess! if defined?(YAML::ENGINE) && YAML::ENGINE.yamler == 'psych' opts ||= {} opts[:line_width] ||= 999999 # Psych doesn't let you disable line wrap yaml = super else yaml = YAML::quick_emit(self, opts) do |out| out.map(nil, @yaml_style || to_yaml_style) do |map| sort.each { |k, v| map.add(k, v) } end end end yaml_postprocess yaml end |
#update(h) ⇒ Object
:nodoc:
463 464 465 466 |
# File 'lib/sycl.rb', line 463 def update(h) # :nodoc: h = Sycl::Hash.from_hash(h) unless h.is_a?(Sycl::Hash) super end |
#yaml_postprocess(yaml) ⇒ Object
:nodoc:
623 624 625 |
# File 'lib/sycl.rb', line 623 def yaml_postprocess(yaml) # :nodoc: @yaml_postprocessor ? @yaml_postprocessor.call(yaml) : yaml end |
#yaml_postprocessor(&block) ⇒ Object
Set a postprocessor hook which runs after YML is dumped, for example, via to_yaml() or Sycl::dump(). The hook is a block that gets the YAML text string as an argument, and returns a new, possibly different, YAML text string.
A common example use case is to suppress the initial document separator, which is just visual noise when humans are viewing or editing a single YAML file:
a.yaml_postprocessor { |yaml| yaml.sub(/\A---\s+/, '') }
Your conventions might also prohibit trailing whitespace, which at least the Syck library will tack on the end of YAML hash keys:
a.yaml_postprocessor { |yaml| yaml.gsub(/:\s+$/, '') }
615 616 617 |
# File 'lib/sycl.rb', line 615 def yaml_postprocessor(&block) @yaml_postprocessor = block if block_given? end |
#yaml_preprocess! ⇒ Object
:nodoc:
619 620 621 |
# File 'lib/sycl.rb', line 619 def yaml_preprocess! # :nodoc: @yaml_preprocessor.call(self) if @yaml_preprocessor end |
#yaml_preprocessor(&block) ⇒ Object
Set a preprocessor hook which runs before each time YAML is dumped, for example, via to_yaml() or Sycl::dump(). The hook is a block that gets the object itself as an argument. The hook can then set render_inline!() or similar style arguments, prune nil or empty leaf values from hashes, or do whatever other styling needs to be done before a Sycl object is rendered as YAML.
595 596 597 |
# File 'lib/sycl.rb', line 595 def yaml_preprocessor(&block) @yaml_preprocessor = block if block_given? end |