diff --git a/README.md b/README.md index 612c169..399814a 100644 --- a/README.md +++ b/README.md @@ -5,18 +5,22 @@ text_chart demonstration Show you how cool this is - 10 |'''''''''### - 9 |'''''''''###'''''''''''''''''''''''''''''''''''''''### - 8 |'''''''''###'''''''''''''''''''''''''''''''''### ### - 7 |'''''''''###'''### ### ### - 6 |'''''''''###'''###'''''''''''''''''''''''''''###'''###'''### - 5 |'''''''''###'''###'''### ### ### ### - 4 |'''''''''###'''###'''###'''''''''### ### ### ### - 3 |'''### ### ### ### ### ### ### ### - 2 |'''###'''###'''###'''###'''''''''###'''### ### ### ### - 1 |'''###'''###'''###'''###'''### ### ### ### ### ### - 0 | ### ### ### ### ### ### ### ### ### ### - ---------------------------------------------------------------- + 73| ### + | ### + 67| ### ### ### ### + | ### ### ### ### + 54| ### ### ### ### ### ### ### + | ### ### ### ### ### ### ### + 44| ### ### ### ### ### ### ### ### ### ### + | ### ### ### ### ### ### ### ### ### ### + 33| ### ### ### ### ### ### ### ### ### ### ### + | ### ### ### ### ### ### ### ### ### ### ### + 27| ### ### ### ### ### ### ### ### ### ### ### ### ### ### + 20| ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### + 14| ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### + 10| ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### + 5| ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### + ---------------------------------------------------------------------------------- ``` ## Why? @@ -42,16 +46,25 @@ Or install it yourself as: This snippet will generate the text chart at the top of the readme. ```ruby -TextChart.new("text_chart demonstration", "Show you how cool this is", [*1..10].shuffle(random: Random.new(1)).to_s +TextChart.new( + "text_chart demonstration", + "Show you how cool this is", + [17, 54, 47, 64, 27, 56, 33, 66, 14, 45, 10, 21, 24, 27, 73, 54, 20, 67, 44, 5] +).to_s ``` You can enable colors: ```ruby -TextChart.new("text_chart demonstration", "Show you how cool this is", [*1..10].shuffle(random: Random.new(1), true).to_s +TextChart.new( + "text_chart demonstration", + "Show you how cool this is", + [17, 54, 47, 64, 27, 56, 33, 66, 14, 45, 10, 21, 24, 27, 73, 54, 20, 67, 44, 5], + true +).to_s ``` ## Limitations -Right now `TextChart` only supports integers. +Right now `TextChart` only supports positive integers. ## Development diff --git a/lib/text_chart.rb b/lib/text_chart.rb index 1a3f9cd..8f198e1 100644 --- a/lib/text_chart.rb +++ b/lib/text_chart.rb @@ -10,18 +10,22 @@ # text_chart demonstration # Show you how cool this is # -# 10 |'''''''''### -# 9 |'''''''''###'''''''''''''''''''''''''''''''''''''''### -# 8 |'''''''''###'''''''''''''''''''''''''''''''''### ### -# 7 |'''''''''###'''### ### ### -# 6 |'''''''''###'''###'''''''''''''''''''''''''''###'''###'''### -# 5 |'''''''''###'''###'''### ### ### ### -# 4 |'''''''''###'''###'''###'''''''''### ### ### ### -# 3 |'''### ### ### ### ### ### ### ### -# 2 |'''###'''###'''###'''###'''''''''###'''### ### ### ### -# 1 |'''###'''###'''###'''###'''### ### ### ### ### ### -# 0 | ### ### ### ### ### ### ### ### ### ### -# ---------------------------------------------------------------- +# 73| ### +# | ### +# 67| ### ### ### ### +# | ### ### ### ### +# 54| ### ### ### ### ### ### ### +# | ### ### ### ### ### ### ### +# 44| ### ### ### ### ### ### ### ### ### ### +# | ### ### ### ### ### ### ### ### ### ### +# 33| ### ### ### ### ### ### ### ### ### ### ### +# | ### ### ### ### ### ### ### ### ### ### ### +# 27| ### ### ### ### ### ### ### ### ### ### ### ### ### ### +# 20| ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### +# 14| ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### +# 10| ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### +# 5| ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### +# ---------------------------------------------------------------------------------- class TextChart class Error < StandardError; end @@ -30,16 +34,17 @@ class Error < StandardError; end # @param [Array] data # @param [Boolean] colors def initialize(title, subtitle, data, colors = false) + raise Error, "`data` cannot be empty" if data.empty? + @title = title @subtitle = subtitle - @data = data.empty? ? [0] : data + @data = data.empty? ? [0] : data.filter(&:positive?) @colors = colors - @refs = define_references @size_calculator = SizeCalculator.new(self) @designer = Designer.new(self, @size_calculator) end - attr_reader :title, :subtitle, :refs, :data, :size_calculator, :designer + attr_reader :title, :subtitle, :data, :size_calculator, :designer # @return [String] def to_s @@ -65,11 +70,7 @@ def size_config(key) bar_margin: 1, bar_width: 3, x_axis_height: 1, - reference_row_height: 1 + bar_row_height: 1, + max_bar_height: 60 } - - def define_references - r = [*@data.min..@data.max].reverse - r.map(&:to_s) - end end diff --git a/lib/text_chart/designer.rb b/lib/text_chart/designer.rb index 488c3ad..169f75d 100644 --- a/lib/text_chart/designer.rb +++ b/lib/text_chart/designer.rb @@ -111,22 +111,24 @@ def draw_y_axis end def draw_references - references = @text_chart.refs + references = @text_chart.data width = @size_calc.calculate_reference_width - number_of_references = references.size - ref_size = ref_start = ref_end = nil + y_axis_size = @size_calc.calculate_y_axis_size + ref_size = ref_start = ref_end = ref_str = nil - number_of_references.times do |i| - ref_size = references[i].size + references.each do |r| + row = y_axis_size - @size_calc.calculate_bar_height(r) + ref_str = r.to_s + ref_size = ref_str.size if ref_size == width ref_start = 0 ref_end = ref_size - 1 - @chart_canvas[i][ref_start..ref_end] = references[i] else - ref_start = ref_size - @chart_canvas[i][ref_start] = references[i] + ref_start = width - ref_size + ref_end = [ref_start, ref_size].max end + @chart_canvas[row][ref_start..ref_end] = ref_str end end @@ -146,6 +148,6 @@ def colorize(str, format) # @return height of each sample item [Array] def define_height_of_bars - @text_chart.data.map { |i| @text_chart.refs.size - @text_chart.refs.index(i.to_s) } + @text_chart.data.map { |i| @size_calc.calculate_bar_height(i) } end end diff --git a/lib/text_chart/size_calculator.rb b/lib/text_chart/size_calculator.rb index 67d46ab..5ef7cad 100644 --- a/lib/text_chart/size_calculator.rb +++ b/lib/text_chart/size_calculator.rb @@ -50,14 +50,23 @@ def calculate_number_of_rows x_axis_row = @text_chart.size_config(:x_axis_height) result += x_axis_row - reference_row = @text_chart.size_config(:reference_row_height) - number_of_references = @text_chart.refs.size - number_of_references.times { result += reference_row } + calculate_bar_height(@text_chart.data.max).times do + result += @text_chart.size_config(:bar_row_height) + end result end end + # @param bar [Integer] + # @return the number of rows that we should render [Integer] + def calculate_bar_height(bar) + return 0 if bar.zero? + + min_bar_height = 1 + (calculate_increase_percentile(@text_chart.data.min, bar) / bar_height_limiter).round + min_bar_height + end + # @return [Integer] def calculate_x_axis_size @x_axis_size ||= calculate_number_of_columns - calculate_reference_width @@ -71,4 +80,27 @@ def calculate_y_axis_size calculate_number_of_rows - x_axis_row end end + + private + + # @param initial value [Numeric] + # @param final value [Numeric] + # @return increase percentile [Float] + def calculate_increase_percentile(initial, final) + float_initial = initial.to_f + float_final = final.to_f + diff = float_final - float_initial + (diff / float_initial) * 100.0 + end + + # @return the denominator to assure the `max_rows` config [Integer] + def bar_height_limiter + @bar_height_limiter ||= + begin + limiter = 10.0 + max_diff = calculate_increase_percentile(@text_chart.data.min, @text_chart.data.max) + limiter *= 10.0 until (max_diff / limiter).round <= @text_chart.size_config(:max_bar_height) + limiter + end + end end diff --git a/test/text_chart/designer_test.rb b/test/text_chart/designer_test.rb index c9cc080..d1b6708 100644 --- a/test/text_chart/designer_test.rb +++ b/test/text_chart/designer_test.rb @@ -4,7 +4,7 @@ class TextChart::DesignerTest < Test::Unit::TestCase test "#draw_header" do - text_chart = TextChart.new("This is a nice title", "This is a nice subtitle", []) + text_chart = TextChart.new("This is a nice title", "This is a nice subtitle", [1]) result = text_chart.designer.draw_header.join @@ -18,20 +18,11 @@ class TextChart::DesignerTest < Test::Unit::TestCase end test "#draw_axis" do - no_sample_designer = TextChart.new("No sample", "Testing", []).designer small_sample_designer = TextChart.new("Small sample", "Testing", [*1..10]).designer with_gaps_designer = TextChart.new("With gaps", "Testing", [1, 5, 10]).designer - with_negative_number = TextChart.new("With negative number", "Testing", [*-3..3]).designer - no_sample_result = no_sample_designer.draw_axis.join small_sample_result = small_sample_designer.draw_axis.join with_gaps_result = with_gaps_designer.draw_axis.join - with_negative_number_result = with_negative_number.draw_axis.join - - assert_equal no_sample_result, <<~END - 0| - ------ - END assert_equal small_sample_result, <<~END 10| @@ -46,31 +37,19 @@ class TextChart::DesignerTest < Test::Unit::TestCase 1| ------------------------------------------ END - assert_equal with_gaps_result, <<~END 10| - 9| - 8| - 7| - 6| + | + | + | + | 5| - 4| - 3| - 2| + | + | + | 1| -------------- END - - assert_equal with_negative_number_result, <<~END - 3| - 2| - 1| - 0| - -1| - -2| - -3| - ------------------------------ - END end test "#draw_bars" do @@ -78,21 +57,13 @@ class TextChart::DesignerTest < Test::Unit::TestCase random_order_designer = TextChart.new( "Random order", "Testing", [*1..10].shuffle(random: Random.new(1)) ).designer - with_zero_designer = TextChart.new( - "With zero", "Testing", [*0..5].shuffle(random: Random.new(1)) - ).designer gaps = TextChart.new( "Duplicated and gaps", "Testing", [*1..3, 6, 12].shuffle(random: Random.new(1)) ).designer - negative = TextChart.new( - "Negative", "Testing", [*-3..3].shuffle(random: Random.new(1)) - ).designer sorted_result = sorted_designer.draw_bars.join random_order_result = random_order_designer.draw_bars.join - with_zero_result = with_zero_designer.draw_bars.join gaps_result = gaps.draw_bars.join - negative_result = negative.draw_bars.join assert_equal sorted_result, <<-END ### @@ -107,7 +78,6 @@ class TextChart::DesignerTest < Test::Unit::TestCase ### ### ### ### ### ### ### ### ### ### END - assert_equal random_order_result, <<-END ### ### ### @@ -121,17 +91,6 @@ class TextChart::DesignerTest < Test::Unit::TestCase ### ### ### ### ### ### ### ### ### ### END - - assert_equal with_zero_result, <<-END - ### - ### ### - ### ### ### - ### ### ### ### - ### ### ### ### ### - ### ### ### ### ### ### - - END - assert_equal gaps_result, <<-END ### ### @@ -147,16 +106,5 @@ class TextChart::DesignerTest < Test::Unit::TestCase ### ### ### ### ### END - - assert_equal negative_result, <<-END - ### - ### ### - ### ### ### - ### ### ### ### - ### ### ### ### ### - ### ### ### ### ### ### - ### ### ### ### ### ### ### - - END end end diff --git a/test/text_chart/size_calculator_test.rb b/test/text_chart/size_calculator_test.rb index 6240495..aab4a98 100644 --- a/test/text_chart/size_calculator_test.rb +++ b/test/text_chart/size_calculator_test.rb @@ -4,36 +4,35 @@ class TextChart::SizeCalculatorTest < Test::Unit::TestCase test "#calculate_reference_width" do - no_sample = TextChart.new("", "", []).size_calculator small_sample = TextChart.new("", "", [*1..10]).size_calculator medium_sample = TextChart.new("", "", [*1..100]).size_calculator big_sample = TextChart.new("", "", [*1..1000]).size_calculator - with_negative_number = TextChart.new("", "", [*-11..3]).size_calculator - no_sample_result = no_sample.calculate_reference_width small_sample_result = small_sample.calculate_reference_width medium_sample_result = medium_sample.calculate_reference_width big_sample_result = big_sample.calculate_reference_width - with_negative_number_result = with_negative_number.calculate_reference_width - assert_equal no_sample_result, 1 assert_equal small_sample_result, 2 assert_equal medium_sample_result, 3 assert_equal big_sample_result, 4 - assert_equal with_negative_number_result, 3 end test "#calculate_number_of_rows" do - no_sample = TextChart.new("", "", []).size_calculator - small_sample = TextChart.new("", "", [*1..10]).size_calculator - - no_sample_result = no_sample.calculate_number_of_rows - small_sample_result = small_sample.calculate_number_of_rows - - # Example: - # 0| 1 - # ------ 2 - assert_equal no_sample_result, 2 + sequencial_sample = TextChart.new("", "", [*1..10]).size_calculator + small_gaps_sample = TextChart.new( + "", + "", + [16, 1, 18, 24, 7, 10, 5, 16, 7, 15] + ).size_calculator + medium_gaps_sample = TextChart.new( + "", + "", + [36, 34, 15, 18, 37, 18, 16, 12, 26, 9] + ).size_calculator + + sequencial_sample_result = sequencial_sample.calculate_number_of_rows + small_gaps_result = small_gaps_sample.calculate_number_of_rows + medium_gaps_result = medium_gaps_sample.calculate_number_of_rows # Example: # 10| 1 @@ -47,22 +46,76 @@ class TextChart::SizeCalculatorTest < Test::Unit::TestCase # 2| 9 # 1| 10 # ------ 11 - assert_equal small_sample_result, 11 + assert_equal sequencial_sample_result, 11 + # Example: + # 24| 1 + # | 2 + # | 3 + # | 4 + # | 5 + # | 6 + # 18| 7 + # | 8 + # 16| 9 + # 15| 10 + # | 11 + # | 12 + # | 13 + # | 14 + # 10| 15 + # | 16 + # | 17 + # 7| 18 + # | 19 + # 5| 20 + # | 21 + # | 22 + # | 23 + # 1| 24 + # ------ 25 + assert_equal small_gaps_result, 25 + # Example: + # 37| 1 + # 36| 2 + # | 3 + # 34| 4 + # | 5 + # | 6 + # | 7 + # | 8 + # | 9 + # | 10 + # | 11 + # | 12 + # 26| 13 + # | 14 + # | 15 + # | 16 + # | 17 + # | 18 + # | 19 + # | 20 + # | 21 + # 18| 22 + # | 23 + # 16| 24 + # 15| 25 + # | 26 + # | 27 + # | 28 + # 12| 29 + # | 30 + # | 31 + # 9| 32 + # ------ 33 + assert_equal medium_gaps_result, 33 end test "#calculate_number_of_columns" do - no_sample = TextChart.new("", "", []).size_calculator small_sample = TextChart.new("", "", [*1..10]).size_calculator - no_sample_result = no_sample.calculate_number_of_columns small_sample_result = small_sample.calculate_number_of_columns - # Example: - # 0| - # ------ - # ccccccc = 7 - assert_equal no_sample_result, 7 - # Example: # 10| # . @@ -75,18 +128,10 @@ class TextChart::SizeCalculatorTest < Test::Unit::TestCase end test "#calculate_x_axis_size" do - no_sample = TextChart.new("", "", []).size_calculator small_sample = TextChart.new("", "", [*1..10]).size_calculator - no_sample_result = no_sample.calculate_x_axis_size small_sample_result = small_sample.calculate_x_axis_size - # Example: - # 0| - # ------ - # cccccc = 6 - assert_equal no_sample_result, 6 - # Example: # 10| # . @@ -99,17 +144,10 @@ class TextChart::SizeCalculatorTest < Test::Unit::TestCase end test "#calculate_y_axis_size" do - no_sample = TextChart.new("", "", []).size_calculator small_sample = TextChart.new("", "", [*1..10]).size_calculator - no_sample_result = no_sample.calculate_y_axis_size small_sample_result = small_sample.calculate_y_axis_size - # Example: - # 0| y = 1 - # ------ - assert_equal no_sample_result, 1 - # Example: # 10| y # 9| y diff --git a/test/text_chart_test.rb b/test/text_chart_test.rb index a103058..1f6784c 100644 --- a/test/text_chart_test.rb +++ b/test/text_chart_test.rb @@ -9,8 +9,11 @@ class TextChartTest < Test::Unit::TestCase end end + test "#initialize" do + assert_raise_message("`data` cannot be empty") { TextChart.new("Title", "Subtitle", []) } + end + test "#to_s" do - no_sample = TextChart.new("No sample", "Testing", []) sorted_sample = TextChart.new("Sorted sample", "Testing", [*5..10]) random_order_sample = TextChart.new( "Random order sample", "Testing", [*1..10].shuffle(random: Random.new(1)) @@ -18,36 +21,33 @@ class TextChartTest < Test::Unit::TestCase duplicated_and_gaps = TextChart.new( "Duplicated and gaps sample", "Testing", [*0..3, 3, 6, 12].shuffle(random: Random.new(1)) ) - with_negative_numbers = TextChart.new( - "With negative numbers sample", "Testing", [*-3..3].shuffle(random: Random.new(1)) - ) with_colors = TextChart.new( - "With colors", "Testing", [*-3..3].shuffle(random: Random.new(1)), true + "With colors", "Testing", [*0..3, 3, 6, 12].shuffle(random: Random.new(1)), true + ) + nearby_numbers = TextChart.new( + "Nearby numbers", "Numbers between 90 and 120", [116, 114, 115, 102, 104, 96, 103, 113, 119, 94] ) - no_sample_result = no_sample.to_s sorted_result = sorted_sample.to_s random_order_result = random_order_sample.to_s duplicated_and_gaps_result = duplicated_and_gaps.to_s - with_negative_numbers_result = with_negative_numbers.to_s with_colors_result = with_colors.to_s + nearby_numbers_result = nearby_numbers.to_s - assert_equal no_sample_result, <<~EXPECTED - No sample - Testing - - 0| ### - ------ - EXPECTED assert_equal sorted_result, <<~EXPECTED Sorted sample Testing 10| ### + | ### 9| ### ### + | ### ### 8| ### ### ### + | ### ### ### 7| ### ### ### ### + | ### ### ### ### 6| ### ### ### ### ### + | ### ### ### ### ### 5| ### ### ### ### ### ### -------------------------- EXPECTED @@ -71,46 +71,48 @@ class TextChartTest < Test::Unit::TestCase Duplicated and gaps sample Testing - 12| ### - 11| ### - 10| ### - 9| ### - 8| ### - 7| ### - 6| ### ### - 5| ### ### - 4| ### ### - 3| ### ### ### ### - 2| ### ### ### ### ### - 1| ### ### ### ### ### ### - 0| ### ### ### ### ### ### ### - ------------------------------ - EXPECTED - assert_equal with_negative_numbers_result, <<~EXPECTED - With negative numbers sample - Testing - - 3| ### - 2| ### ### - 1| ### ### ### - 0| ### ### ### ### - -1| ### ### ### ### ### - -2| ### ### ### ### ### ### - -3| ### ### ### ### ### ### ### - ------------------------------ + 12| ### + | ### + | ### + | ### + | ### + | ### + 6| ### ### + | ### ### + | ### ### + 3| ### ### ### ### + 2| ### ### ### ### ### + 1| ### ### ### ### ### ### + -------------------------- EXPECTED assert_equal with_colors_result, <<~EXPECTED \e[1mWith colors\e[22m Testing + + \e[36m1\e[0m\e[36m2\e[0m| \e[34m###\e[0m + | \e[34m###\e[0m + | \e[34m###\e[0m + | \e[34m###\e[0m + | \e[34m###\e[0m + | \e[34m###\e[0m + \e[36m6\e[0m| \e[34m###\e[0m \e[34m###\e[0m + | \e[34m###\e[0m \e[34m###\e[0m + | \e[34m###\e[0m \e[34m###\e[0m + \e[36m3\e[0m| \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m + \e[36m2\e[0m| \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m + \e[36m1\e[0m| \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m + -------------------------- + EXPECTED + # [116, 114, 115, 102, 104, 96, 103, 113, 119, 94] + assert_equal nearby_numbers_result, <<~EXPECTED + Nearby numbers + Numbers between 90 and 120 - \e[36m3\e[0m| \e[34m###\e[0m - \e[36m2\e[0m| \e[34m###\e[0m \e[34m###\e[0m - \e[36m1\e[0m| \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m - \e[36m0\e[0m| \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m - \e[36m-1\e[0m| \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m - \e[36m-2\e[0m| \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m - \e[36m-3\e[0m| \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m \e[34m###\e[0m - ------------------------------ + 119| ### + 113| ### ### ### ### ### + 103| ### ### ### ### ### ### ### ### + 94| ### ### ### ### ### ### ### ### ### ### + ------------------------------------------ EXPECTED end end