Merging

One feature of JayAPI::Configuration is that it can be ‘merged’ with another JayAPI::Configuration object. More precisely, merging in a ‘selective’ way, which differs from the standard merge behaviour of Hash objects. Merging in this case can be defined as follows:

If config_a and config_b are two JayAPI::Configuration objects, they are merged like this:

require 'jay_api/mergeables/merge_selector' # Note that this must be required to add the merging functionality to JayAPI::Configuration

config_a = config_a.with_merge_selector
config_a.merge_select(config_b)

Merging Rules

The merging will follow the following rules:

  1. All nodes in config_b will completely overwrite nodes in config_a (Just like the standard Hash merging behaviour). Example:

    # config_a
    one:
      two: 3
    
    # config_b
    one:
      two: 4
    
    # config_a.merge_select(config_b)
    one:
      two: 4 # <== note that the 'b' node overwrites the 'a' node.
    
  2. All nodes in config_a that are not found in config_b will be ignored in the result.

    # config_a
    one: 1
    two: 2
    three: 3
    
    # config_b
    four: 4
    
    # config_a.merge_select(config_b)
    four: 4 # <= note that only the node from 'config_b' shows up in the result.
    
  3. If a node value is ‘nil’ in config_b and the node matches a node in config_a then the matching node in config_a will be ‘selected’ to be in the result.

    # config_a
    one:
      two: 3
    
    # config_b
    one: ~ # The tilde is a YAML nil.
    
    # config_a.merge_select(config_b)
    one:
      two: 3 # <= note that config_b 'one: ~' acts as a 'selector' for everything under 'one:' in config_a.
    

Note

Note that internally the Hashes are converted to HashWithIndifferentAccess objects. This means that for example { ‘c’ => 1 } will override a Hash like { c: 1 }, because it treats Symbols and Strings as the same entity.

Realistic Example

To see how this can realistically be used in some configuration, the following examples can be studied:

config_a

 1htmls:
 2  index:
 3    render_config:
 4      config: ~
 5    template_data:
 6      breadcrumbs: Start
 7  all_internal:
 8    overall:
 9      template_data:
10        filter: { category: ['software', 'architecture', 'module'] }
11        breadcrumbs: ['SWE Specs', 'Overall']
12    off_target:
13      template_data:
14        filter: { test_setups: off_target, category: ['software', 'architecture', 'module'] }
15        breadcrumbs: ['SWE Specs', 'Off Target']
16    on_target:
17      template_data:
18        filter: { test_setups: on_target, category: ['software', 'architecture', 'module'] }
19        breadcrumbs: ['SWE Specs', 'On Target']
20    manual:
21      template_data:
22        filter: { test_setups: manual, category: ['software', 'architecture', 'module'] }
23        breadcrumbs: ['SWE Specs', 'Manual']

config_b

 1htmls:
 2  index: ~
 3  all_internal:
 4    overall:
 5      template_data: ~
 6      some_new: attribute
 7    off_target:
 8      template_data:
 9        breadcrumbs: ['New', 'Breadcrumbs']
10    on_target: ~

config_a.merge_select(config_b)

 1htmls:
 2index:
 3  render_config:
 4    config: ~
 5  template_data:
 6    breadcrumbs: Start
 7all_internal:
 8  overall:
 9    template_data:
10      filter: { category: ['software', 'architecture', 'module'] }
11      breadcrumbs: ['SWE Specs', 'Overall']
12    some_new: attribute
13  off_target:
14    template_data:
15      breadcrumbs: ['New', 'Breadcrumbs']
16  on_target:
17    template_data:
18      filter: { test_setups: on_target, category: ['software', 'architecture', 'module'] }
19      breadcrumbs: ['SWE Specs', 'On Target']

Notice:

  • Due to rule number 3, notice that config_a entries belonging to ‘htmls -> index’ (lines 3-6) are in the result, because the config_b’s ‘htmls -> index’ value is ‘~’, in effect instructing a ‘selection’.

  • Due to rule number 1, notice that config_a’s’ ‘htmls -> all_internal -> off_target -> template_data -> breacrumbs’ (line 15) is overwritten by the corresponding new node value in config_b.

  • Due to rule number 2, notice that the ‘htmls -> all_internal -> manual’ node (lines 20-23) in config_a does not show up in the result, because config_b does not contain it.