9

I'm fooling around with Ruby and basically I have

@trans = { :links => {
    :quick_notes => "aaaaaaa"
  }
}

I want to call something like

def t
  #...something
end
t('links.quick_notes')

to access

trans[:links][:quick_notes]

I'm basically trying to achieve the same functionality like when using Internationalizations

I18n.t('something.other.foo') 

sofar I came up with this approach

 def t(key)
   a=''
   key.to_s.split('.').each{|key|  a+="[:#{key}]" } 
   #now a == "[:links][:quick_notes]"
   #but I cant figure out how can I call it on  @trans variable

 end

 t('links.quick_notes')

Any ideas ? thanx

mu is too short
  • 426,620
  • 70
  • 833
  • 800
equivalent8
  • 13,754
  • 8
  • 81
  • 109

5 Answers5

13

You can get there with inject:

def t(key)
    key.to_s.split('.').inject(@trans) { |h, k| h[k.to_sym] }
end

Error checking and "no such entry" checking is left as an exercise.

mu is too short
  • 426,620
  • 70
  • 833
  • 800
1

If you are looking for something that interpolates %{variable}, including Array support, I've been using this:

def interpolate(string, hash)
  string.gsub(/%\{([^\}]*)\}/).each { |match| match[/^%{(.*)}$/, 1].split('.').inject(hash) { |h, k| h[(k.to_s == k.to_i.to_s) ? k.to_i : k.to_sym] } }
end
Nathaniel Ford
  • 20,545
  • 20
  • 91
  • 102
1

Since Ruby 2.3 Hash has a dig method. "no such entry" checking is built in: if any of the keys is missing, then the whole expression returns nil. Usage:

@trans = { :links => {
    :quick_notes => "aaaaaaa"
  }
}

def t (str)
  syms = str.split(".").map(&:to_sym)
  @trans.dig(*syms)
end
p t('links.quick_notes')  # => "aaaaaaa"
steenslag
  • 79,051
  • 16
  • 138
  • 171
1

For what it's worth, here is some manky code that shows the use of recursion.

def getKeyChain (obj, parts)
  if !obj || parts.size == 0
    # this is the base case
    obj
  else
    # this is the recursive case
    key = parts[0]
    if key.match(/^:/)
      key = key[1..key.size].to_sym
    end
    # each time recursing, pass a new state.
    # in this case, the value for the "key" and the remaining parts
    # that need resolving
    getKeyChain(obj[key], parts[1..parts.size])
  end
end

def getCompoundKey (obj, compound)
  # helper makes it easier to call while not making the
  # recursive function more complex.
  getKeyChain(obj, compound.split("."))
end

h0 = {:x => "hello"}
h1 = {"a" => {:b => "world"}}
puts getCompoundKey(h0, ":x")   # => hello
puts getCompoundKey(h1, "a.:b") # => world

Many improvements can be made... "use at own peril".

Happy coding.

0

Another addition - If you have json or a hash that includes arrays as well as hashes - maybe something like this:

@trans = { :links => [ {
    :quick_notes => "aaaaaaa"
  },
  {
    :quick_notes => "bbbbbbb"
  } ],
  :page => 1
}

You can include a test of position in an array, you can do this sort of thing.

def t(key)
  key.split('.').inject(@trans) { |h,v| h.send('[]',v.to_i.to_s.eql?(v) ? v.to_i : v) }
end

This would allow you to pass a key like this

t('links.1.quick_notes')

and get the answer 'bbbbbb'

Mark Swardstrom
  • 17,217
  • 6
  • 62
  • 70