Search code examples
rubychef-infrachef-recipeaws-opsworks

Using slice! on a variable is modifying the node attribute that populated the variable


In OpsWorks Stacks, I have set a layer attribute using the custom JSON field:

{
  "layer_apps" : [
    "app_manager"
  ]
}

The app_ portion of the attribute is necessary for the workflow. At times, I need to temporarily remove the app_ portion within a cookbook. To do this, I use slice!:

node['layer_apps'].each do |app_name|

  install_certs_app_name = app_name
  install_certs_app_name.slice!('app_') # 'app_manager' => 'manager'
  # snip
end

However, once this is done, even though app_name isn't being directly modified, each node['layer_apps'] attribute gets sliced, which carries on to subsequent cookbooks and causes failures. The behaviour I expected was that slice! would modify app_name, and not the current node['layer_apps'] attribute. Thinking that app_name was a link to the attribute rather than being it's own variable, I tried assigning its value to a separate variable (install_certs_app_name and similar in other cookbooks), but the behaviour persisted.

Is this expected behaviour in Ruby/Chef? Is there a better way to be excluding the app_ prefix from the attribute?


Solution

  • app_name is being directly modified. That's the reason for the bang ! after the method... so that you're aware that the method mutates the object.

    and app_name and install_certs_app_name are referencing the same object.

    Note that slice and slice! both return "app_" but the bang object mutates the caller by removing the sliced text.

    If you did

    result = install_certs_app_name.slice!('app_') puts result ==> app_ puts install_certs_app_name --> manager

    Try (instead)

      install_certs_app_name = app_name.dup
      install_certs_app_name.slice!('app_')
    

    So you have two separate objects.

    Alternatively,

      install_certs_app_name = app_name.sub('app_', '')