lib/time/task_timer.rb in bblib-0.3.0 vs lib/time/task_timer.rb in bblib-0.4.1

- old
+ new

@@ -1,107 +1,123 @@ module BBLib - - class TaskTimer < LazyClass - attr_hash :tasks, default: Hash.new + # Simple timer that can track tasks based on time. Also provides aggregated metrics + # and history for each task run. Generally useful for benchmarking or logging. + # + # @author Brandon Black + # @attr [Hash] tasks The information on all running tasks and history of all tasks up to the retention. + # @attr [Integer] retention The number of runs to collect per task before truncation. + class TaskTimer + include Effortless + attr_hash :tasks, default: {}, serialize: false attr_int_between -1, nil, :retention, default: 100 - def time task = :default, type = :current - return nil unless @tasks.keys.include? task - numbers = @tasks[task][:history].map{ |v| v[:time] } + # Returns an aggregated metric for a given type. + # + # @param [Symbol] task The key value of the task to retrieve + # @param [Symbol] type The metric to return. + # Options are :avg, :min, :max, :first, :last, :sum, :all and :count. + # @return [Float, Integer, Array] Returns either the aggregation (Numeric) or an Array in the case of :all. + def time(task = :default, type = :current) + return nil unless tasks.keys.include?(task) + numbers = tasks[task][:history].map { |v| v[:time] } case type when :current - return nil unless @tasks[task][:current] - return Time.now.to_f - @tasks[task][:current] - when :min - return numbers.min - when :max - return numbers.max + return nil unless tasks[task][:current] + Time.now.to_f - tasks[task][:current] + when :min, :max, :first, :last + numbers.send(type) when :avg - return numbers.inject{ |sum, n| sum + n }.to_f / numbers.size + numbers.size.zero? ? nil : numbers.inject { |sum, n| sum + n }.to_f / numbers.size when :sum - return numbers.inject{ |sum, n| sum + n } + numbers.inject { |sum, n| sum + n } when :all - return numbers - when :first - return numbers.first - when :last - return numbers.last + numbers when :count - return numbers.size + numbers.size end end - def clear task - return nil unless @tasks.keys.include?(task) + # Removes all history for a given task + # + # @param [Symbol] task The name of the task to clear history from. + # @return [NilClass] Returns nil + def clear(task = :default) + return nil unless tasks.keys.include?(task) stop task - @tasks[task][:history].clear + tasks[task][:history].clear end - def start task = :default - if !@tasks.keys.include?(task) then @tasks[task] = {history: [], current: nil} end - if @tasks[task][:current] then stop task end - @tasks[task][:current] = Time.now.to_f - return 0 + # Start a new timer for the referenced task. If a timer is already running for that task it will be stopped first. + # + # @param [Symbol] task The name of the task to start. + # @return [Integer] Returns 0 + def start(task = :default) + tasks[task] = { history: [], current: nil } unless tasks.keys.include?(task) + stop task if tasks[task][:current] + tasks[task][:current] = Time.now.to_f + 0 end - def stop task = :default - return nil unless @tasks.keys.include?(task) && active?(task) - time_taken = Time.now.to_f - @tasks[task][:current].to_f - @tasks[task][:history] << {start: @tasks[task][:current], stop: Time.now.to_f, time: time_taken} - @tasks[task][:current] = nil - if @retention && @tasks[task][:history].size > @retention then @tasks[task][:history].shift end + # Stop the referenced timer. + # + # @param [Symbol] task The name of the task to stop. + # @return [Float, NilClass] The amount of time the task had been running or nil if no matching task was found. + def stop(task = :default) + return nil unless tasks.keys.include?(task) && active?(task) + time_taken = Time.now.to_f - tasks[task][:current].to_f + tasks[task][:history] << { start: tasks[task][:current], stop: Time.now.to_f, time: time_taken } + tasks[task][:current] = nil + if retention && tasks[task][:history].size > retention then tasks[task][:history].shift end time_taken end - def restart task = :default + def restart(task = :default) start(task) unless stop(task).nil? end - def active? task - return false unless @tasks.keys.include? task - !@tasks[task][:current].nil? + def active?(task = :default) + return false unless tasks.keys.include?(task) + !tasks[task][:current].nil? end - def stats task, pretty: false - return nil unless @tasks.include?(task) - stats = "#{task}" + "\n" + '-'*30 + "\n" - TIMER_TYPES.each do |k,v| + def stats(task = :default, pretty: false) + return nil unless tasks.include?(task) + TIMER_TYPES.map do |k, _v| next if STATS_IGNORE.include?(k) - stats+= k.to_s.capitalize.ljust(10) + "#{self.send(k, task, pretty:pretty)}\n" - end - stats + [k, send(k, task, pretty: pretty)] + end.compact.to_h end - def method_missing *args, **named - temp = args.first.to_sym - pretty = named.delete :pretty - type, task = TIMER_TYPES.keys.find{ |k| k == temp || TIMER_TYPES[k].include?(temp) }, args[1] ||= :default + def method_missing(*args, **named) + temp = args.first.to_sym + type = TIMER_TYPES.keys.find { |k| k == temp || TIMER_TYPES[k].include?(temp) } return super unless type - t = time task, type - pretty && type != :count && t ? (t.is_a?(Array) ? t.map{|m| m.to_duration} : t.to_duration) : t + t = time(args[1] || :default, type) + return t if type == :count || !named[:pretty] + t.is_a?(Array) ? t.map(&:to_duration) : t.to_duration end - private + def respond_to_missing?(method, include_private = false) + TIMER_TYPES.keys.find { |k| k == method || TIMER_TYPES[k].include?(method) } || super + end - STATS_IGNORE = [:current, :all] + TIMER_TYPES = { + current: [], + count: [:total], + first: [:initial], + last: [:latest], + min: [:minimum, :smallest], + max: [:maximum, :largest], + avg: [:average, :av], + sum: [], + all: [:times] + }.freeze - TIMER_TYPES = { - current: [], - count: [:total], - first: [:initial], - last: [:latest], - min: [:minimum, :smallest], - max: [:maximum, :largest], - avg: [:average, :av], - sum: [], - all: [:times] - } + protected - def lazy_init *args - if args.first.is_a?(Symbol) - start(args.first) - end - end + STATS_IGNORE = [:all].freeze + def simple_init(*args) + start(args.first) if args.first.is_a?(Symbol) + end end - end