Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how does default_proc work

Tags:

ruby

The following code was taken from a RubyTapas screencast, but it wasn't explained sufficiently for a person of my experience. Setting up a hash like this

config = Hash.new do |h,k|
  h[k] = Hash.new(&h.default_proc)
end

allows one to set and access values in the following way

config[:production][:database][:adapter] = 'mysql'
puts config[:production][:database][:adapter] # => "mysql"

When I do

puts config.inspect

It reveals a nested hash.

{:production=>{:database=>{:adapter=>"mysql"}}}

However, if I modify the code (to just play around with it) to remove &h.default_proc

    config = Hash.new do |h,k|
      h[k] = Hash.new()      #&h.default_proc removed from parameter
    end

I get this error

 undefined method `[]=' for nil:NilClass (NoMethodError)

I don't get what is happening with Hash.new(&h.default_proc), when, for example, I do this

    config[:production][:database][:adapter] = 'mysql'
    puts config[:production][:database][:adapter] # => "mysql"

Can you break down that, explaining in detail how the block &hdefault_proc is working in this context. Please also explain if you can when the proc is getting called. Why is the proc necessary to make this work

    config[:production][:database][:adapter] = 'mysql'

and how does the proc get called? I thought you have to do proc.call to actually call the proc.

Thanks in advance

like image 283
BrainLikeADullPencil Avatar asked Dec 05 '12 23:12

BrainLikeADullPencil


People also ask

Are Ruby hashes ordered?

Hashes are inherently unordered. Hashes provide amortized O(1) insertion and retrieval of elements by key, and that's it. If you need an ordered set of pairs, use an array of arrays.

What is a Ruby hash?

A Hash is a dictionary-like collection of unique keys and their values. Also called associative arrays, they are similar to Arrays, but where an Array uses integers as its index, a Hash allows you to use any object type. Hashes enumerate their values in the order that the corresponding keys were inserted.


1 Answers

You are taking it the wrong way. &h is not a meaningful unit in this context. It is & attached to h.default_proc. That is a proc h.default_proc turned into a block. Similar thing applies to the splat operator *. The associative precedence of & and * is always the last. The reason is clear: it does not make sense otherwise.


Suppose you have:

config = Hash.new() # or `{}`

then, whenever you access config with an un-existing key, it will return nil, so

config[:production][:database]

will return an error because the method [] is not defined on config[:production], which equals nil. When you do:

config = Hash.new do |h,k|
  h[k] = Hash.new()
end

then, it will be ensured that when you access config with an un-existing key, it creates a new hash as a value. So

config[:production][:database]

will now be okay because the method [] is defined on config[:production], which equals the newly created hash {}. But still,

config[:production][:database][:adapter]

will return an error because the method [] is not defined on config[:production][:database], which equals nil. If you instead have:

config = Hash.new do |h,k|
  h[k] = Hash.new(&h.default_proc)
end

then, that means that, when you access config with an un-existing key like:

config[:production]

the hash that is created this time will have a default_proc set to the same one as config, which means that

config[:procuction][:database]

is not a hash that returns nil to a non-existing key call, but a hash that returns another hash to a non-existing key call. So this time,

config[:production][:database][:adapter]

will not return an error. Similar thing happens with an arbitrary depth within the hash.

like image 112
sawa Avatar answered Oct 16 '22 07:10

sawa