dontfidget/chartnado

View on GitHub
lib/chartnado/renderer.rb

Summary

Maintainability
D
1 day
Test Coverage
class Chartnado::Renderer
  include Chartnado::Series

  attr_accessor :context, :data_block, :render_block

  def initialize(context, data_block, &render_block)
    @context = context
    @data_block = data_block
    @render_block = render_block
  end

  delegate :controller, to: :context

  def render(*args, ** options)
    json_options = {}
    chartkick_options = options.dup

    if args.length > 1
      if args.last.is_a?(Hash)
        json_options = chartkick_options
        chartkick_options = args[1].dup
      end
      args.select { |arg| arg.is_a?(Symbol) }.each do |key|
        json_options[key] = true unless json_options.has_key?(key)
      end
    end

    if json_options[:percentage]
      chartkick_options.reverse_merge!(max: 100.0)
    end

    options = controller.chartnado_options if controller.respond_to?(:chartnado_options)
    options ||= {}

    chart_json_proc = -> (*args) {
      chart_json(Chartnado.with_chartnado_dsl(&data_block), *args)
    }
    renderer = -> {
      context.instance_exec(chartkick_options, json_options, chart_json_proc, &render_block)
    }

    if options[:wrapper_proc]
      context.instance_exec(*args, renderer, ** options, &options[:wrapper_proc])
    else
      renderer.call
    end
  end

  def chart_json(series, show_total: false, reverse_sort: false, percentage: false)
    series = Chartnado::Series::Wrap[series]
    series *= 100.0 if percentage
    if series.has_separate_named_series?
      series = series.to_a
      if series.first.second.respond_to?(:map)
        totals = Hash.new(0.0)
        new_series = series.sort_by { |item| item.first.to_s }
        new_series = new_series.reverse if reverse_sort

        new_series = new_series.map do |name, data|
          {
            name: name,
            data: data.map do |k, v|
              totals[k] += v if show_total
              [k, v]
            end
          }
        end

        if show_total
          [{name: 'Total',
            data: totals.map { |k, v| [k, 0] },
            tooltip: totals.map { |k, v| [k, v] }
           }] + new_series
        else
          new_series
        end
      else
        new_series = series.sort_by { |item| item.first.to_s }
        new_series = new_series.reverse if reverse_sort

        if show_total
          new_series << ['Total', new_series.map(&:second).reduce(0, :+)]
        else
          new_series
        end
      end
    elsif series.hash?
      if (key = series.keys.first) and key.is_a?(Array) and key.size == 2
        totals = Hash.new(0.0)
        new_series = series.group_by { |k, v| k[0] }.sort_by { |k| k.to_s }
        new_series = new_series.reverse if reverse_sort

        new_series = new_series.map do |name, data|
          {
            name: name,
            data: data.map do |k, v|
              totals[k[1]] += v if show_total
              [k[1], v]
            end
          }
        end

        if show_total
          [{name: 'Total',
            data: totals.map { |k, v| [k, 0] },
            tooltip: totals.map { |k, v| [k, v] }
           }] + new_series
        else
          new_series
        end
      else
        new_series = series.sort_by { |key| key.to_s }
        new_series = new_series.reverse if reverse_sort

        if show_total
          new_series << ['Total', new_series.map(&:second).reduce(0, :+)]
        else
          new_series
        end
      end
    elsif series.respond_to?(:map)
      series
    else
      [['Total', series]]
    end
  end
end