0

How can I define a ruby Hash by this way?

default['mgmt']['query'] = {
    'default_interval' => {'diff' => 3600, 'snapshot' => 86400 * 7},
    'tables' => {
        'deb_packages' => default_interval,
        'rpm_packages' => default_interval,
        ...
    }
}

EDIT: above code is for Chef recipe's attributes/default.rb

I am looking for a way to define default_interval inside the Hash yet be able to referenced by other field so that the default_interval can be overwritten by other Chef means such as environment json.

That is the reason why I do not choose simply define a global default_interval var.

Currently, I use following definition to represent default_interval

default['mgmt']['query'] = {
    'default_interval' => {'diff' => 3600, 'snapshot' => 86400 * 7},
    'tables' => {
        'deb_packages' => {} # default_interval,
        'rpm_packages' => {} # default_interval,
        ...
    }
}

The empty {} can be overwritten by other chef means such as environment json to change to {diff: 600, snapshot:86400}

Any better cool way?

osexp2000
  • 2,910
  • 30
  • 29
  • I have changed all of your tags to get you into a better visibility location since this question is very chef specific – engineersmnky Mar 30 '18 at 13:13
  • removed the ruby tag, this a chef-specific question about the node object and not a ruby Hash object – lamont Apr 06 '18 at 15:46
  • @lamont Although I am using these code in chef, but all chef code is actually Ruby code! I have confirmed that this question and answer can be applied to non-chef Ruby environment, so would you please re-add the ruby tag? – osexp2000 Apr 09 '18 at 00:52
  • 1
    The node object that you are manipulating is very specific to the chef environment and you are getting answers to this question which very clearly mistake it for a Hash, which is is not. It doesn't even inherit from Hash and `Chef::Node < Hash` as an expression is falsey. – lamont Apr 13 '18 at 02:38
  • @lamont got it. thank you. – osexp2000 Apr 13 '18 at 03:02

2 Answers2

1

That is the reason why I do not choose simply define a global default_interval var.

No need, you can define a local variable.

default['mgmt']['query'] = {
    'default_interval' => (default_interval = {'diff' => 3600, 'snapshot' => 86400 * 7}),
    'tables' => {
        'deb_packages' => default_interval,
        'rpm_packages' => default_interval,
    }
}

This style is not very common in ruby and is guaranteed to raise a few eyebrows, but it does what you want.

Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
  • Thank you! This is what I want. But unfortunately, as I tested, the `default_interval` is still treated as a global variable. I call `puts default_interval` after this statement, it print out "{"diff"=>3600, "snapshot"=>604800}", maybe it is a chef specific result. Anyway, this is the most elegant way I ever know. – osexp2000 Apr 04 '18 at 03:32
  • @osexp2003: no, it's not global, it's local to the scope. Now if you don't have any method or class definitions, the scope is likely the whole rest of the file. – Sergio Tulentsev Apr 04 '18 at 07:14
  • You are right. I'v confirmed that the variable is file-level. So no problem now. thank you again. – osexp2000 Apr 05 '18 at 01:29
  • 1
    you can write more conventional code by just defining the local variable above the whole codeblock, and then using it in all three places. – lamont Apr 13 '18 at 02:35
-1

Declare node as

node = Hash.new { |h, k| h[k] = {} }

and your code will just be executed as is, producing a structure you need. The latter is named “hash” in ruby btw.

More info on Hash#new accepting a block.

SRack
  • 11,495
  • 5
  • 47
  • 60
Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
  • Excuse me what do you mean “interesting”? Have you expected some more php-ish answer, like 20 lines of code? – Aleksei Matiushkin Mar 28 '18 at 08:12
  • I could not open your link. But it seems useful sometimes. I am just expecting some usage like self['default_interval']. The above code is a piece of chef attributes/default.rb so seems nobody write ruby statement (such as function call or something). – osexp2000 Mar 28 '18 at 08:32
  • I found a description at https://docs.ruby-lang.org/en/2.0.0/Hash.html, it is a cool field getter call. I will try it – osexp2000 Mar 28 '18 at 08:37
  • @osexp2003 are you saying `node` is a `Chef::Node`? That is slightly different than a ruby `Hash`. While it does define a `#[]` method this method is actually delegated to `attributes` which is closer to a `Hash` but not quite (it's a `Mash` *Chef's subclass of ruby's native `Hash`) I think what you are actually looking for is a simple as `def default_interval; {'diff' => 3600, 'snapshot' => 86400 * 7}; end` then your references will work as method calls to that method. – engineersmnky Mar 29 '18 at 19:07
  • @engineersmnky thank you. I'v added more description to the question. Actually the `node` should be `default` which might still be slightly different as ruby `Hash`? Yes, I am looking for a answer let me define field default_interval yet can be referenced by another field. – osexp2000 Mar 30 '18 at 03:39
  • Yeah node in this context is a Chef::Node object which is something that smells a bit like a Hash but is overly complicated, and already supports autovivification and converting symbol keys to strings, along with a somewhat overly complicated multiple precedence level structure. – lamont Apr 06 '18 at 15:49