Reputation: 11736
I use this pattern repeatedly:
pb= ProgressBar.new("blah_map", wtf.count)
newresult= cache("blah_map") do
result.map{ |r| pb.inc; { blah: r[:x] } }
end
pb.finish
or
pb= ProgressBar.new("blah_group", wtf.count)
newresult= cache("blah_group") do
result.group_by{ |r| pb.inc; "#{r[:x]}&#{r[:y]}" }
end
pb.finish
So naturally I would like to be able to do
def progress_cache(name, count, &block)
pb= ProgressBar.new(name, count)
inject_pb_inc(block) # how??
# do some caching with yield if cache doesn't exist, don't mind this
pb.finish
end
And use it as such:
newresult= progress_cache("lol", result.count) do
result.map do |r|
# progress_cache magically inserted a pb.inc here for pretty animation!
r[:omg]
end
end
The question is, how to inject the pb.inc call into the block (map, group_by etc) inside progress_cache block?
Edit: rephrased the question
Upvotes: 2
Views: 2359
Reputation: 7403
There are a few ways to accomplish this, with various tradeoffs in terms of expressiveness:
Send the progress bar as a block param
def progress_cache(name, count &block)
pb = ProgressBar.new(name, count)
result = block.call(pb)
pb.finish
result
end
and use it like
newresult = progress_cache("lol", result.count) do |pb|
result.map{|r| pb.inc; r[:omg]}
end
Create a new map function that automatically increments the progress bar (you could overwrite result.map directly, or provide result.map_with_progress, but I'll leave that up to you)
def map_with_progress(container, name, &block)
pb = ProgressBar.new(name, container.count)
result = container.map{|obj| block.call(obj)}
pb.finish
result
end
and then use it like
newresult = map_with_progress(result, "lol") { |r| r[:omg] }
of course, since you're doing both a map and a group_by, you'd have to have two helper methods here, which might start getting messy.
Use a higher-order function
def function_with_progress(obj, func_name, name, count, &block)
pb = ProgressBar.new(name, count)
result = obj.__send__(func_name) do |param|
pb.inc
block.call(param)
end
pb.finish
result
end
and then use it like
newresult = function_with_progress(result, "map", "lol", result.count) do |r|
r[:omg]
end
but I wouldn't recommend this approach as it's too abstract. It would work in a functional language like javascript or clojure, but I don't think it's appropriate for ruby.
Upvotes: 2