# Best practice for simplifying cookbook attribute overrides

**URL:** <https://discourse.chef.io/t/best-practice-for-simplifying-cookbook-attribute-overrides/8486>\
**Category:** Chef Infra (archive)\
**Created:** [May 17, 2016, 2:33am UTC](https://discourse.chef.io/t/best-practice-for-simplifying-cookbook-attribute-overrides/8486 "2016-05-17T02:33:26Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![infrauto](https://avatars.discourse-cdn.com/v4/letter/i/8797f3/32.png) [@infrauto](https://discourse.chef.io/u/infrauto)\
**Post date:** [May 17, 2016, 2:33am UTC](https://discourse.chef.io/t/best-practice-for-simplifying-cookbook-attribute-overrides/8486/1 "2016-05-17T02:33:26Z")

</div>

I need help coming up with the best “interface” for consumers of my cookbook.

Let’s say I have the following in my cookbook’s **attributes/default.rb**

```auto
default['my_cookbook']['my_default_hashes'] = {
  'hash1' = {
    'property1_1' => 'value1_1',
    'property1_2' => 'value1_2'
  },
  'hash2' = {
    'property2_1' => 'value2_1',
    'property2_2' => 'value2_2'
  },
  'hash3' = {
    'property3_1' => 'value3_1',
    'property3_2' => 'value3_2'
  }
}

default['my_cookbook']['my_selected_hashes'] = {} # empty by default

```

I want to use `node['my_cookbook']['my_selected_hashes']` as input to one of my template .erb files. I have another cookbook, let’s call it **wrapper\_cookbook** , that consumes **my\_cookbook**. Before I converge **wrapper\_cookbook** , I want to set `node['my_cookbook']['my_selected_hashes']` with `hash1` and `hash3` (but not `hash2`) and ALSO add a brand-new hash called `hash4`. And to further complicate things, I want to use a different value for `property3_1` (from `hash3`), while keeping `node['my_cookbook']['my_default_hashes']` unmodified (I’m treating it as read-only).

What is the best way to do this?

Here are some ideas of what `node['my_cookbook']['my_selected_hashes']` would look like in **wrapper\_cookbook** :  
**Idea #1:**

```auto
node.set['my_cookbook']['my_selected_hashes'] = {
  'hash1' = {}, # left empty to indicate that I want the default values from node['my_cookbook']['my_default_hashes']['hash1']
  'hash3' = {
    'property3_1' = 'some_new_value' # this is the only attribute from hash3 I want to override 
  },
  'hash4' = {
    'property4_1' => 'value4_1',
    'property4_2' => 'value4_2'
  }
}

```

**Idea #2:**

```auto
# A new node attribute not in the original attributes/default.rb file: This is an array of my selected hashes from node['my_cookbook']['my_default_hashes']
node.set['my_cookbook']['predefined_hashes_i_want'] = ['hash1', 'hash3'] 

# This is also a new node attribute not in the original attributes/default.rb file.
node.set['my_cookbook']['my_default_hashes_overrides'] = {
  'hash3' = {
    'property3_1' = 'some_new_value' # this is the only attribute from hash3 I want to override 
  },
}

node.set['my_cookbook']['my_selected_hashes'] = {
  'hash4' = { 
    'property4_1' => 'value4_1',
    'property4_2' => 'value4_2'
  }
}

```

The idea above is that the hashes named in `node['my_cookbook']['predefined_hashes_i_want']` would be added to node[‘my\_cookbook’][‘my\_selected\_hashes’] while ensuring that hash3 in `node['my_cookbook']['my_selected_hashes']` has a new value for its `property3_1`.

Which of the 2 above is better? Is there a third option that would be the best approach? I appreciate the help!

---

<div class="post-metadata">

**Author:** ![chris\_sullivan](https://avatars.discourse-cdn.com/v4/letter/c/bc79bd/32.png) [@chris\_sullivan](https://discourse.chef.io/u/chris_sullivan)\
**Post date:** [May 17, 2016, 8:28am UTC](https://discourse.chef.io/t/best-practice-for-simplifying-cookbook-attribute-overrides/8486/2 "2016-05-17T08:28:04Z")

</div>

Hi,

You might be over thinking this.

The node object is a Hashie/Mash.

Your code can be written in your cookbook like this

```auto
default['my_cookbook']['my_default_hashes'] = {
  'hash1' => {
    'property1_1' => 'value1_1',
    'property1_2' => 'value1_2'
  },
  'hash2' => {
    'property2_1' => 'value2_1',
    'property2_2' => 'value2_2'
  },
  'hash3' => {
    'property3_1' => 'value3_1',
    'property3_2' => 'value3_2'
  }
}

```

And accessed in a wrapper cookbook like this

```auto
default['my_cookbook']['my_default_hashes']['hash2']['property2_1'] = "New Value"

```

My view would be to offer a single set of values that could be overwritten in the wrapper cookbook, your template then just needs to reference a single part of the Hashie/Mash.

```auto
node['my_cookbook']['my_default_hashes'].each do | hsh| ...

```

In your wrapper you just remove what you don’t want, for example if [“hash2”][“property2\_1”] needs to be removed it can be done with remove/delete

```auto
default[''my_cookbook']['my_default_hashes']['hash2'].delete('property2_1')

```

As regards a “Read Only” value, I’m not sure this is possible with node, normally in Ruby you will freeze an object.

EDIT: Ok, just quickly tried this, maybe you can freeze the sub hashes

```auto
default['my_cookbook']['my_default_hashes']['hash2'].freeze
default['my_cookbook']['my_default_hashes']['hash2']['property2_1'] = "New Value"

```

Results in

```auto
RuntimeError
------------
can't modify frozen Chef::Node::VividMash

Additional information:
-----------------------
      Ruby objects are often frozen to prevent further modifications
      when they would negatively impact the process (e.g. values inside
      Ruby's ENV class) or to prevent polluting other objects when default
      values are passed by reference to many instances of an object (e.g.
      the empty Array as a Chef resource default, passed by reference
      to every instance of the resource).

      Chef uses Object#freeze to ensure the default values of properties
      inside Chef resources are not modified, so that when a new instance
      of a Chef resource is created, and Object#dup copies values by
      reference, the new resource is not receiving a default value that
      has been by a previous instance of that resource.

      Instead of modifying an object that contains a default value for all
      instances of a Chef resource, create a new object and assign it to
      the resource's parameter, e.g.:

      fruit_basket = resource(:fruit_basket, 'default')

      # BAD: modifies 'contents' object for all new fruit_basket instances
      fruit_basket.contents << 'apple'

      # GOOD: allocates new array only owned by this fruit_basket instance
      fruit_basket.contents %w(apple)

```

---

<div class="post-metadata">

**Author:** ![Alex\_Vinyar](https://sea2.discourse-cdn.com/flex016/user_avatar/discourse.chef.io/alex_vinyar/32/1311_2.png) [@Alex\_Vinyar](https://discourse.chef.io/u/Alex_Vinyar)\
**Post date:** [May 17, 2016, 12:35pm UTC](https://discourse.chef.io/t/best-practice-for-simplifying-cookbook-attribute-overrides/8486/3 "2016-05-17T12:35:25Z")

</div>

Take a look at the Supermarket cookbook. I really dig how they handle  
merging attributes:  
[https://github.com/chef-cookbooks/supermarket-omnibus-cookbook/blob/master/libraries/supermarket\_server.rb#L86](https://github.com/chef-cookbooks/supermarket-omnibus-cookbook/blob/master/libraries/supermarket_server.rb#L86)

Maybe that’ll give you some ideas.

---

<div class="post-metadata">

**Author:** ![infrauto](https://avatars.discourse-cdn.com/v4/letter/i/8797f3/32.png) [@infrauto](https://discourse.chef.io/u/infrauto)\
**Post date:** [May 17, 2016, 2:55pm UTC](https://discourse.chef.io/t/best-practice-for-simplifying-cookbook-attribute-overrides/8486/4 "2016-05-17T14:55:52Z")

</div>

Thanks for the response, @chris_sullivan. Let me clarify my scenario a little bit.  
Let’s say **my\_cookbook** is an application server cookbook which allows multiple containers. The **wrapper\_cookbook** ’s goal is to create 2 containers.

**container1** wants to simply use `node[''my_cookbook']['my_default_hashes']['hash1']` and `node[''my_cookbook']['my_default_hashes']['hash2']` without making changes to the default hashes.

**container2** , on the other hand, is the scenario I described in my original post where it wants to use `hash1`, `hash3` (which should include overriding one of its properties) and add a new hash `hash4`.

Given the above, I do not want to make changes to `node[''my_cookbook']['my_default_hashes']` because it will affect both **container1** and **container2**.
