Commit c823b0b7 authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Extract Columns class from Categories.

Let it manage the columns splitting logic so we can also share it
with new items list.
parent 4b2a228c
Loading
Loading
Loading
Loading
+5 −43
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ require 'jsduck/logger'
require 'jsduck/file_categories'
require 'jsduck/auto_categories'
require 'jsduck/categories_class_name'
require 'jsduck/columns'

module JsDuck

@@ -19,6 +20,7 @@ module JsDuck
    def initialize(categories, doc_formatter, relations={})
      @categories = categories
      @class_name = CategoriesClassName.new(doc_formatter, relations)
      @columns = Columns.new("classes")
    end

    # Returns HTML listing of classes divided into categories
@@ -40,10 +42,12 @@ module JsDuck
      EOHTML
    end

    private

    def render_columns(groups)
      align = ["left-column", "middle-column", "right-column"]
      i = -1
      return split(groups, 3).map do |col|
      return @columns.split(groups, 3).map do |col|
        i += 1
        [
          "<div class='#{align[i]}'>",
@@ -64,48 +68,6 @@ module JsDuck
      end
    end

    # Splits the array of items into n chunks so that the sum of
    # largest chunk is as small as possible.
    #
    # This is a brute-force implementation - we just try all the
    # combinations and choose the best one.
    def split(items, n)
      if n == 1
        [items]
      elsif items.length <= n
        Array.new(n) {|i| items[i] ? [items[i]] : [] }
      else
        min_max = nil
        min_arr = nil
        i = 0
        while i <= items.length-n
          i += 1
          # Try placing 1, 2, 3, ... items to first chunk.
          # Calculate the remaining chunks recursively.
          cols = [items[0,i]] + split(items[i, items.length], n-1)
          max = max_sum(cols)
          # Is this the optimal solution so far? Remember it.
          if !min_max || max < min_max
            min_max = max
            min_arr = cols
          end
        end
        min_arr
      end
    end

    def max_sum(cols)
      cols.map {|col| sum(col) }.max
    end

    # Finds the total size of items in array
    #
    # The size of one item is it's number of classes + the space for header
    def sum(arr)
      header_size = 3
      arr.reduce(0) {|sum, item| sum + item["classes"].length + header_size }
    end

  end

end

lib/jsduck/columns.rb

0 → 100644
+56 −0
Original line number Diff line number Diff line
module JsDuck

  # Splits array of items with subitems into roughly equal size groups.
  class Columns
    # Initialized with the name of subitems field.
    def initialize(subitems_field)
      @header_size = 3
      @subitems_field = subitems_field
    end

    # Splits the array of items into n chunks so that the sum of
    # largest chunk is as small as possible.
    #
    # This is a brute-force implementation - we just try all the
    # combinations and choose the best one.
    def split(items, n)
      if n == 1
        [items]
      elsif items.length <= n
        Array.new(n) {|i| items[i] ? [items[i]] : [] }
      else
        min_max = nil
        min_arr = nil
        i = 0
        while i <= items.length-n
          i += 1
          # Try placing 1, 2, 3, ... items to first chunk.
          # Calculate the remaining chunks recursively.
          cols = [items[0,i]] + split(items[i, items.length], n-1)
          max = max_sum(cols)
          # Is this the optimal solution so far? Remember it.
          if !min_max || max < min_max
            min_max = max
            min_arr = cols
          end
        end
        min_arr
      end
    end

    private

    def max_sum(cols)
      cols.map {|col| sum(col) }.max
    end

    # Finds the total size of items in array
    #
    # The size of one item is it's number of classes + the space for header
    def sum(arr)
      arr.reduce(0) {|sum, item| sum + item[@subitems_field].length + @header_size }
    end

  end

end
+11 −11
Original line number Diff line number Diff line
require "jsduck/categories"
require "jsduck/columns"

describe JsDuck::Categories do
describe JsDuck::Columns do

  # Small helper to check the sums
  def sum(arr)
@@ -9,7 +9,7 @@ describe JsDuck::Categories do

  # Replace the sum method with the one that simply sums the numbers,
  # so we can use simpler test-data.
  class JsDuck::Categories
  class JsDuck::Columns
    def sum(arr)
      arr.reduce(0) {|sum,x| sum + x }
    end
@@ -17,30 +17,30 @@ describe JsDuck::Categories do

  describe "#split" do
    before do
      @categories = JsDuck::Categories.new([], {}, {})
      @columns = JsDuck::Columns.new("classes")
    end

    it "split(1 item by 1)" do
      @cols = @categories.split([2], 1)
      @cols = @columns.split([2], 1)
      @cols.length.should == 1
      sum(@cols[0]).should == 2
    end

    it "split(3 items by 1)" do
      @cols = @categories.split([1, 2, 3], 1)
      @cols = @columns.split([1, 2, 3], 1)
      @cols.length.should == 1
      sum(@cols[0]).should == 6
    end

    it "split(3 items to two equal-height columns)" do
      @cols = @categories.split([1, 2, 3], 2)
      @cols = @columns.split([1, 2, 3], 2)
      @cols.length.should == 2
      sum(@cols[0]).should == 3
      sum(@cols[1]).should == 3
    end

    it "split(1 item by 3)" do
      @cols = @categories.split([2], 3)
      @cols = @columns.split([2], 3)
      @cols.length.should == 3
      sum(@cols[0]).should == 2
      sum(@cols[1]).should == 0
@@ -48,7 +48,7 @@ describe JsDuck::Categories do
    end

    it "split(3 items by 3)" do
      @cols = @categories.split([1, 2, 3], 3)
      @cols = @columns.split([1, 2, 3], 3)
      @cols.length.should == 3
      sum(@cols[0]).should == 1
      sum(@cols[1]).should == 2
@@ -56,7 +56,7 @@ describe JsDuck::Categories do
    end

    it "split(6 items by 3)" do
      @cols = @categories.split([5, 8, 4, 2, 1, 3], 3)
      @cols = @columns.split([5, 8, 4, 2, 1, 3], 3)
      @cols.length.should == 3
      sum(@cols[0]).should <= 10
      sum(@cols[1]).should <= 10
@@ -64,7 +64,7 @@ describe JsDuck::Categories do
    end

    it "split(8 items by 3)" do
      @cols = @categories.split([1, 3, 5, 2, 1, 4, 2, 3], 3)
      @cols = @columns.split([1, 3, 5, 2, 1, 4, 2, 3], 3)
      @cols.length.should == 3
      sum(@cols[0]).should <= 9
      sum(@cols[1]).should <= 9