Notes posted by EdvardM
RSS feedPoor man's maybe
After creating a simple Maybe monad in Ruby, a colleaque noticed I could have just used try (I wasn’t aware try supports blocks). I think the method was even meant for such cases.
Why I mention this? Because it clarifies the whole ‘raises exception if method does not exist’ thing. It should not be crappy solution to exception handling, but allow for doing away with messy if statements. An example:
report = params[:query_type] .try { |qt| build_query(qt) } .try { |sql| run_query(sql) } .try { |res| format_result(res) }
If any of the expressions params[], build_query, run_query etc. returns nil, the chain is halted and nil is returned. It still throws exceptions if a values is not nil and method does not exist, which is just like it should.
Does not symbolize hashes in nested arrays
If you have a nested structure containing arrays of hashes, you still need to do that on your own, eg.
module SymbolizeHelper def symbolize_recursive(hash) {}.tap do |h| hash.each { |key, value| h[key.to_sym] = map_value(value) } end end def map_value(thing) case thing when Hash symbolize_recursive(thing) when Array thing.map { |v| map_value(v) } else thing end end end
Or, if you want to get really fancy with Ruby refinements (YMMV), one could do
module SymbolizeHelper extend self def symbolize_recursive(hash) {}.tap do |h| hash.each { |key, value| h[key.to_sym] = transform(value) } end end private def transform(thing) case thing when Hash; symbolize_recursive(thing) when Array; thing.map { |v| transform(v) } else; thing end end refine Hash do def deep_symbolize_keys SymbolizeHelper.symbolize_recursive(self) end end end
And later say
using SymbolizeHelper # augmented Hash#deep_symbolize_keys is now available
Long-wanted functional extension
This is pretty nice method allowing you to build stuff in a functional way.
Lets say you want to build a hash from an array, keyed by array object, where each value is the number of same objects in the array.
# imperative style :-P h = Hash.new(0) [1, 3, 2, 3, 1, 3].each { |i| h[i] += 1 } h # => {1=>2, 3=>3, 2=>1} # functional style, using inject. Note that you need to explicitly return the accumulator in the end [1, 3, 2, 3, 1, 3].inject(Hash.new(0)) { |a, i| a[i] += 1; a } # => {1=>2, 3=>3, 2=>1} # using each_with_object. Note the reversed block params - accumulator is the last parameter. # Mnemonic: consistent with each_with_index, where object is the first parameter [1, 3, 2, 3, 1, 3].each_with_object(Hash.new(0)) {|i, a| a[i] += 1} # => {1=>2, 3=>3, 2=>1}