Skip to content
This repository was archived by the owner on Mar 12, 2023. It is now read-only.

Commit d387523

Browse files
authored
Merge pull request #56 from nguyenquangminh0711/bug/better-edge-cases-handle
Better edge cases handle
2 parents cbba19b + ccae3b3 commit d387523

15 files changed

+412
-34
lines changed

.github/workflows/rspec.yml

+6-2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ jobs:
5151
run: ruby spec/wait_for_tmux.rb
5252
- name: Install dependencies
5353
run: bundle install
54+
- name: Kill tmux
55+
run: tmux kill-server || true
5456
- name: Run tests
5557
run: bundle exec parallel_rspec spec/
5658
test-macos:
@@ -78,13 +80,13 @@ jobs:
7880
- name: Run tests
7981
run: bundle exec parallel_rspec -n 2 spec/
8082
test-byebug:
81-
runs-on: ubuntu-latest
8283
strategy:
8384
fail-fast: false
8485
matrix:
8586
byebug: [9.1.0, 10.0.2]
8687
env:
8788
BUNDLE_GEMFILE: "./spec/gemfiles/Gemfile-byebug-${{ matrix.byebug }}"
89+
runs-on: ubuntu-latest
8890
steps:
8991
- uses: actions/checkout@v2
9092
- name: Set up Ruby
@@ -104,11 +106,13 @@ jobs:
104106
run: tmux -V
105107
- name: Start tmux
106108
run: tmux start-server
107-
- name: start dummy tmux session
109+
- name: Start dummy tmux session
108110
run: tmux new-session -t dummy -d
109111
- name: Wait for tmux
110112
run: ruby spec/wait_for_tmux.rb
111113
- name: Install dependencies
112114
run: bundle install
115+
- name: Kill tmux
116+
run: tmux kill-server || true
113117
- name: Run tests
114118
run: bundle exec parallel_rspec spec/

Gemfile

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ gem 'rubocop', '~> 0.89.1'
1717
gem 'rubocop-rspec', '~> 1.43.1', require: false
1818

1919
group :test do
20+
gem 'activerecord'
2021
gem 'parallel_tests'
2122
gem 'rspec-retry'
23+
gem 'sqlite3'
2224
end

lib/ruby_jard/console.rb

+9-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ module RubyJard
88
# Wrapper for utilities to control screen
99
class Console
1010
class << self
11+
def attachable?
12+
return false unless output.tty?
13+
14+
width, height = screen_size(output)
15+
width != 0 && height != 0
16+
end
17+
1118
def redirected?
1219
output != $stdout
1320
end
@@ -22,7 +29,7 @@ def output
2229
begin
2330
File.open('/dev/tty', 'w+')
2431
rescue StandardError
25-
STDOUT # Give up now. TODO: should warn, and let program continues
32+
STDOUT # Give up now.
2633
end
2734
end
2835
end
@@ -37,7 +44,7 @@ def input
3744
begin
3845
File.open('/dev/tty', 'r+')
3946
rescue StandardError
40-
STDIN # Give up. TODO: should warn, and let program continues
47+
STDIN # Give up.
4148
end
4249
end
4350
end

lib/ruby_jard/decorators/color_decorator.rb

+13-4
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,28 @@ def initialize(color_scheme)
3737

3838
def decorate(style_names, content)
3939
attributes = nil
40+
foreground = nil
41+
background = nil
42+
4043
if style_names.is_a?(Symbol)
4144
styles = @color_scheme.styles_for(style_names)
4245
else
4346
attributes = translate_styles(style_names)
4447
styles = @color_scheme.styles_for(style_names.shift)
4548
end
46-
foreground = translate_color(styles.shift, true)
47-
background = translate_color(styles.shift, false)
49+
50+
if styles.is_a?(Array)
51+
foreground = translate_color(styles.shift, true)
52+
background = translate_color(styles.shift, false)
53+
elsif !styles.nil?
54+
raise RubyJard::Error, "Styles for #{style_names.inspect} must be an array"
55+
end
56+
4857
"#{foreground}#{background}#{attributes}#{content}#{CSI_RESET}"
4958
end
5059

60+
private
61+
5162
def translate_color(color, foreground)
5263
if (matches = HEX_PATTERN_6.match(color.to_s))
5364
red = matches[1].to_i(16)
@@ -70,8 +81,6 @@ def translate_color(color, foreground)
7081
end
7182
end
7283

73-
private
74-
7584
def translate_styles(styles = [])
7685
styles.map { |key| STYLES_CSI_MAP[key] }.compact.join
7786
end

lib/ruby_jard/decorators/rails_decorator.rb

+8-2
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,9 @@ def decorate_singleline(variable, line_limit:, depth: 0)
8989
if variable.respond_to?(:loaded?) && variable.loaded?
9090
spans = []
9191
label = RubyJard::Span.new(
92-
content: RubyJard::Reflection.call_to_s(variable).chomp('>'), styles: :text_primary
92+
content: RubyJard::Reflection.call_to_s(variable).chomp('>'),
93+
styles: :text_primary,
94+
margin_right: variable.length >= 1 ? 1 : 0
9395
)
9496
spans << label
9597
spans += @attributes_decorator.inline_values(
@@ -99,6 +101,10 @@ def decorate_singleline(variable, line_limit:, depth: 0)
99101
)
100102
spans << RubyJard::Span.new(content: '>', styles: :text_primary)
101103

104+
if variable.length <= 0
105+
spans << RubyJard::Span.new(content: '(empty)', margin_left: 1, styles: :text_primary)
106+
end
107+
102108
spans
103109
else
104110
relation_summary(variable, line_limit)
@@ -138,7 +144,7 @@ def relation_summary(variable, line_limit)
138144
width = overview.length + 1 + 12
139145
spans = [RubyJard::Span.new(content: overview, styles: :text_primary)]
140146
if RubyJard::Reflection.call_respond_to?(variable, :to_sql) && width < line_limit
141-
detail = variable.to_sql
147+
detail = variable.to_sql.inspect
142148
detail = detail[0..line_limit - width - 2] + '…' if width + detail.length < line_limit
143149
spans << RubyJard::Span.new(content: detail, styles: :text_dim, margin_left: 1)
144150
end

lib/ruby_jard/session.rb

+8
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ def instance
2424
end
2525

2626
def attach
27+
unless RubyJard::Console.attachable?
28+
$stdout.puts 'Failed to attach. Jard could not detect a valid tty device.'
29+
$stdout.puts 'This bug occurs when the process Jard trying to access is a non-interactive environment '\
30+
' such as docker, daemon, sub-processes, etc.'
31+
$stdout.puts 'If you are confused, please submit an issue in https://github.com/nguyenquangminh0711/ruby_jard/issues.'
32+
return
33+
end
34+
2735
instance.start unless instance.started?
2836

2937
Byebug.attach

lib/ruby_jard/span.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def initialize(content: '', content_length: nil, margin_left: 0, margin_right: 0
1515
content += ' ' * margin_right if margin_right > 0
1616
end
1717

18-
@content = content.to_s
18+
@content = content.to_s.gsub(/\r\n/, '\n').gsub(/\n/, '\n')
1919
@content_length = content_length || @content.length
2020
@styles = styles
2121
end

spec/gemfiles/Gemfile-byebug-10.0.2

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ gem 'rubocop', '~> 0.88'
1717
gem 'rubocop-rspec', require: false
1818

1919
group :test do
20+
gem 'activerecord'
2021
gem 'parallel_tests'
2122
gem 'rspec-retry'
23+
gem 'sqlite3'
2224
end

spec/gemfiles/Gemfile-byebug-11.1.0

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ gem 'rubocop', '~> 0.89.1'
1717
gem 'rubocop-rspec', '~> 1.43.1', require: false
1818

1919
group :test do
20+
gem 'activerecord'
2021
gem 'parallel_tests'
2122
gem 'rspec-retry'
23+
gem 'sqlite3'
2224
end

spec/gemfiles/Gemfile-byebug-9.1.0

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ gem 'rubocop', '~> 0.88'
1717
gem 'rubocop-rspec', require: false
1818

1919
group :test do
20+
gem 'activerecord'
2021
gem 'parallel_tests'
2122
gem 'rspec-retry'
23+
gem 'sqlite3'
2224
end

spec/helpers/active_record_helper.rb

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# frozen_string_literal: true
2+
3+
require 'active_record'
4+
require 'sqlite3'
5+
6+
ActiveRecord::Base.establish_connection(
7+
adapter: 'sqlite3',
8+
database: ':memory:'
9+
)
10+
11+
# Set up database tables and columns
12+
ActiveRecord::Schema.define do
13+
create_table :ar_pets, force: true do |t|
14+
t.string :name
15+
t.integer :age
16+
end
17+
18+
create_table :ar_posts, force: true do |t|
19+
t.string :title
20+
t.string :description
21+
t.datetime :created_at
22+
end
23+
end
24+
25+
# Set up model classes
26+
class ArPet < ActiveRecord::Base; end
27+
class ArPost < ActiveRecord::Base; end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# frozen_string_literal: true
2+
3+
def color_decorator_test_case_name(test_case)
4+
"when translated style is #{test_case[:input][0].inspect} "\
5+
"and content is #{test_case[:input][1].inspect}"
6+
end
7+
8+
RSpec.describe RubyJard::Decorators::ColorDecorator do
9+
subject(:color_decorator) { described_class.new(color_scheme) }
10+
11+
let(:color_scheme) do
12+
Class.new(RubyJard::ColorScheme) do
13+
const_set(
14+
:STYLES,
15+
{
16+
color0: [],
17+
color1: ['234'],
18+
color2: ['#aaa'], # 170, 170, 170
19+
color3: ['#80b57b'], # 128, 181, 123
20+
color4: %w[234 245],
21+
color5: ['#bbb', '#aaa'], # 187, 187, 187 and 170, 170, 170
22+
color6: ['#80b57b', '#78b5ff'], # 128, 181, 123 and 120, 181, 255
23+
color_not_supported_1: ['rgb(0, 255, 255)'],
24+
color_not_supported_2: ['rgb(0, 255, 255)', 'rgb(0, 255, 255)'],
25+
color_invalid: 'invalid'
26+
}.freeze
27+
)
28+
end.new
29+
end
30+
31+
[
32+
# Input style is a symbol
33+
{ input: [:color0, nil], output: "\e[0m" },
34+
{ input: [:color0, 'hello'], output: "hello\e[0m" },
35+
{ input: [:color1, nil], output: "\e[38;5;234m\e[0m" },
36+
{ input: [:color1, 'hello'], output: "\e[38;5;234mhello\e[0m" },
37+
{ input: [:color2, nil], output: "\e[38;2;170;170;170m\e[0m" },
38+
{ input: [:color2, 'hello'], output: "\e[38;2;170;170;170mhello\e[0m" },
39+
{ input: [:color3, nil], output: "\e[38;2;128;181;123m\e[0m" },
40+
{ input: [:color3, 'hello'], output: "\e[38;2;128;181;123mhello\e[0m" },
41+
{ input: [:color4, nil], output: "\e[38;5;234m\e[48;5;245m\e[0m" },
42+
{ input: [:color4, 'hello'], output: "\e[38;5;234m\e[48;5;245mhello\e[0m" },
43+
{ input: [:color5, nil], output: "\e[38;2;187;187;187m\e[48;2;170;170;170m\e[0m" },
44+
{ input: [:color5, 'hello'], output: "\e[38;2;187;187;187m\e[48;2;170;170;170mhello\e[0m" },
45+
{ input: [:color6, nil], output: "\e[38;2;128;181;123m\e[48;2;120;181;255m\e[0m" },
46+
{ input: [:color6, 'hello'], output: "\e[38;2;128;181;123m\e[48;2;120;181;255mhello\e[0m" },
47+
{ input: [:color_not_supported_1, nil], output: "\e[0m" },
48+
{ input: [:color_not_supported_1, 'hello'], output: "hello\e[0m" },
49+
{ input: [:color_not_supported_2, nil], output: "\e[0m" },
50+
{ input: [:color_not_supported_2, 'hello'], output: "hello\e[0m" },
51+
{ input: [:color_invalid, nil], error: /must be an array/ },
52+
{ input: [:color_not_found, nil], output: "\e[0m" },
53+
# Input style is an array with single symbol
54+
{ input: [[:color0], nil], output: "\e[0m" },
55+
{ input: [[:color0], 'hello'], output: "hello\e[0m" },
56+
{ input: [[:color1], nil], output: "\e[38;5;234m\e[0m" },
57+
{ input: [[:color1], 'hello'], output: "\e[38;5;234mhello\e[0m" },
58+
{ input: [[:color2], nil], output: "\e[38;2;170;170;170m\e[0m" },
59+
{ input: [[:color2], 'hello'], output: "\e[38;2;170;170;170mhello\e[0m" },
60+
{ input: [[:color3], nil], output: "\e[38;2;128;181;123m\e[0m" },
61+
{ input: [[:color3], 'hello'], output: "\e[38;2;128;181;123mhello\e[0m" },
62+
{ input: [[:color4], nil], output: "\e[38;5;234m\e[48;5;245m\e[0m" },
63+
{ input: [[:color4], 'hello'], output: "\e[38;5;234m\e[48;5;245mhello\e[0m" },
64+
{ input: [[:color5], nil], output: "\e[38;2;187;187;187m\e[48;2;170;170;170m\e[0m" },
65+
{ input: [[:color5], 'hello'], output: "\e[38;2;187;187;187m\e[48;2;170;170;170mhello\e[0m" },
66+
{ input: [[:color6], nil], output: "\e[38;2;128;181;123m\e[48;2;120;181;255m\e[0m" },
67+
{ input: [[:color6], 'hello'], output: "\e[38;2;128;181;123m\e[48;2;120;181;255mhello\e[0m" },
68+
{ input: [[:color_not_supported_1], nil], output: "\e[0m" },
69+
{ input: [[:color_not_supported_1], 'hello'], output: "hello\e[0m" },
70+
{ input: [[:color_not_supported_2], nil], output: "\e[0m" },
71+
{ input: [[:color_not_supported_2], 'hello'], output: "hello\e[0m" },
72+
{ input: [[:color_invalid], nil], error: /must be an array/ },
73+
{ input: [[:color_not_found], nil], output: "\e[0m" },
74+
# Input style includes text attributes
75+
{ input: [[:color0, :underline], nil], output: "\e[4m\e[0m" },
76+
{ input: [[:color0, :underline], 'hello'], output: "\e[4mhello\e[0m" },
77+
{ input: [[:color1, :underline], nil], output: "\e[38;5;234m\e[4m\e[0m" },
78+
{ input: [[:color1, :underline], 'hello'], output: "\e[38;5;234m\e[4mhello\e[0m" },
79+
{ input: [[:color2, :underline], nil], output: "\e[38;2;170;170;170m\e[4m\e[0m" },
80+
{ input: [[:color2, :underline], 'hello'], output: "\e[38;2;170;170;170m\e[4mhello\e[0m" },
81+
{ input: [[:color3, :underline], nil], output: "\e[38;2;128;181;123m\e[4m\e[0m" },
82+
{ input: [[:color3, :underline], 'hello'], output: "\e[38;2;128;181;123m\e[4mhello\e[0m" },
83+
{ input: [[:color4, :underline], nil], output: "\e[38;5;234m\e[48;5;245m\e[4m\e[0m" },
84+
{ input: [[:color4, :underline], 'hello'], output: "\e[38;5;234m\e[48;5;245m\e[4mhello\e[0m" },
85+
{ input: [[:color5, :underline], nil], output: "\e[38;2;187;187;187m\e[48;2;170;170;170m\e[4m\e[0m" },
86+
{ input: [[:color5, :underline], 'hello'], output: "\e[38;2;187;187;187m\e[48;2;170;170;170m\e[4mhello\e[0m" },
87+
{ input: [[:color6, :underline], nil], output: "\e[38;2;128;181;123m\e[48;2;120;181;255m\e[4m\e[0m" },
88+
{ input: [[:color6, :underline], 'hello'], output: "\e[38;2;128;181;123m\e[48;2;120;181;255m\e[4mhello\e[0m" },
89+
{ input: [[:color_not_supported_1, :underline], nil], output: "\e[4m\e[0m" },
90+
{ input: [[:color_not_supported_1, :underline], 'hello'], output: "\e[4mhello\e[0m" },
91+
{ input: [[:color_not_supported_2, :underline], nil], output: "\e[4m\e[0m" },
92+
{ input: [[:color_not_supported_2, :underline], 'hello'], output: "\e[4mhello\e[0m" },
93+
{ input: [[:color_invalid, :underline], nil], error: /must be an array/ },
94+
{ input: [[:color_not_found, :underline], nil], output: "\e[4m\e[0m" },
95+
# Completed example
96+
{
97+
input: [[:color4, :underline, :bold, :italic], '#<Thread:0x000055fcf5f64c38@Super thread test1.rb:78 sleep>'],
98+
output: "\e[38;5;234m\e[48;5;245m\e[4m\e[1m\e[3m#<Thread:0x000055fcf5f64c38@Super thread test1.rb:78 sleep>\e[0m"
99+
}
100+
].each do |test_case|
101+
context color_decorator_test_case_name(test_case) do
102+
it do
103+
if test_case[:error]
104+
expect do
105+
color_decorator.decorate(*test_case[:input])
106+
end.to raise_error(RubyJard::Error, test_case[:error])
107+
else
108+
expect(color_decorator.decorate(*test_case[:input])).to eql(test_case[:output])
109+
end
110+
end
111+
end
112+
end
113+
end

0 commit comments

Comments
 (0)