styles 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. data/.gitignore +18 -0
  2. data/Gemfile +3 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +725 -0
  5. data/Rakefile +9 -0
  6. data/bin/styles +11 -0
  7. data/lib/styles.rb +18 -0
  8. data/lib/styles/application.rb +190 -0
  9. data/lib/styles/colors.rb +289 -0
  10. data/lib/styles/core_ext.rb +12 -0
  11. data/lib/styles/engine.rb +73 -0
  12. data/lib/styles/line.rb +55 -0
  13. data/lib/styles/properties.rb +34 -0
  14. data/lib/styles/properties/background_color.rb +15 -0
  15. data/lib/styles/properties/base.rb +68 -0
  16. data/lib/styles/properties/border.rb +147 -0
  17. data/lib/styles/properties/color.rb +16 -0
  18. data/lib/styles/properties/display.rb +10 -0
  19. data/lib/styles/properties/font_weight.rb +13 -0
  20. data/lib/styles/properties/function.rb +7 -0
  21. data/lib/styles/properties/margin.rb +83 -0
  22. data/lib/styles/properties/match_background_color.rb +28 -0
  23. data/lib/styles/properties/match_color.rb +21 -0
  24. data/lib/styles/properties/match_font_weight.rb +23 -0
  25. data/lib/styles/properties/match_text_decoration.rb +36 -0
  26. data/lib/styles/properties/padding.rb +81 -0
  27. data/lib/styles/properties/text_align.rb +10 -0
  28. data/lib/styles/properties/text_decoration.rb +20 -0
  29. data/lib/styles/properties/width.rb +11 -0
  30. data/lib/styles/rule.rb +67 -0
  31. data/lib/styles/stylesheet.rb +103 -0
  32. data/lib/styles/sub_engines.rb +4 -0
  33. data/lib/styles/sub_engines/base.rb +16 -0
  34. data/lib/styles/sub_engines/color.rb +115 -0
  35. data/lib/styles/sub_engines/layout.rb +158 -0
  36. data/lib/styles/sub_engines/pre_processor.rb +19 -0
  37. data/lib/styles/version.rb +3 -0
  38. data/styles.gemspec +26 -0
  39. data/test/application_test.rb +92 -0
  40. data/test/colors_test.rb +162 -0
  41. data/test/engine_test.rb +59 -0
  42. data/test/integration_test.rb +136 -0
  43. data/test/line_test.rb +24 -0
  44. data/test/properties/background_color_test.rb +36 -0
  45. data/test/properties/base_test.rb +11 -0
  46. data/test/properties/border_test.rb +154 -0
  47. data/test/properties/color_test.rb +28 -0
  48. data/test/properties/display_test.rb +26 -0
  49. data/test/properties/font_weight_test.rb +24 -0
  50. data/test/properties/function_test.rb +28 -0
  51. data/test/properties/margin_test.rb +98 -0
  52. data/test/properties/match_background_color_test.rb +71 -0
  53. data/test/properties/match_color_test.rb +79 -0
  54. data/test/properties/match_font_weight_test.rb +34 -0
  55. data/test/properties/match_text_decoration_test.rb +38 -0
  56. data/test/properties/padding_test.rb +87 -0
  57. data/test/properties/text_align_test.rb +107 -0
  58. data/test/properties/text_decoration_test.rb +25 -0
  59. data/test/properties/width_test.rb +41 -0
  60. data/test/rule_test.rb +39 -0
  61. data/test/stylesheet_test.rb +245 -0
  62. data/test/sub_engines/color_test.rb +144 -0
  63. data/test/sub_engines/layout_test.rb +110 -0
  64. data/test/test_helper.rb +5 -0
  65. metadata +184 -0
@@ -0,0 +1,4 @@
1
+ require 'styles/sub_engines/base'
2
+ require 'styles/sub_engines/pre_processor'
3
+ require 'styles/sub_engines/color'
4
+ require 'styles/sub_engines/layout'
@@ -0,0 +1,16 @@
1
+ module Styles
2
+ module SubEngines
3
+ class Base
4
+
5
+ private
6
+
7
+ def extract_sub_engine_properties(properties)
8
+ properties.select { |prop| prop.class.sub_engines.include? self.class }
9
+ end
10
+
11
+ def colors
12
+ ::Styles::Colors
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,115 @@
1
+ module Styles
2
+ module SubEngines
3
+ class Color < Base
4
+
5
+ def process(line)
6
+ color_sub_engine_properties = extract_sub_engine_properties line.applicable_properties
7
+ line_properties, match_properties = color_sub_engine_properties.partition do |p|
8
+ property_type(p) == :line
9
+ end
10
+
11
+ line_colors = get_line_colors(line_properties)
12
+
13
+ colored_line = line.to_s.chomp
14
+
15
+ match_properties.each do |prop|
16
+ next unless prop.valid_value?
17
+ if prop.selector.is_a? String
18
+ colored_line = apply_string_match_property(prop, line_colors, colored_line)
19
+ elsif prop.selector.is_a? Regexp
20
+ colored_line = apply_regex_match_property(prop, line_colors, colored_line)
21
+ end
22
+ end
23
+
24
+ line.text = line_colors.any? ? "#{colors[line_colors]}#{colored_line}#{colors[:reset]}" : colored_line
25
+ line
26
+ end
27
+
28
+ private
29
+
30
+ # Get the property type (:line or :match) of a property instance
31
+ def property_type(property)
32
+ klass = property.class
33
+ if klass.constants.include?(:COLOR_PROPERTY_TYPE) && klass::COLOR_PROPERTY_TYPE == :match
34
+ :match
35
+ else
36
+ :line
37
+ end
38
+ end
39
+
40
+ def get_line_colors(line_properties)
41
+ line_properties.map(&:color_to_use).reject { |c| !colors.valid?(c) }.sort
42
+ end
43
+
44
+ # Apply a match property that has a String selector to the given line. Takes into account
45
+ # line property colors and makes sure the rest of the line has those applied to it.
46
+ def apply_string_match_property(property, line_colors, line)
47
+ before_match_colors, after_match_colors = colors.line_substring_color_transitions(
48
+ line_colors, property.color_to_use
49
+ )
50
+ line.gsub(property.selector, "#{before_match_colors}\\0#{after_match_colors}")
51
+ end
52
+
53
+ def apply_regex_match_property(property, line_colors, line)
54
+ selector, value = property.selector, property.value
55
+ if value.is_a? Array
56
+ return line unless selector.is_a?(Regexp)
57
+ apply_colors_to_multiple_matches(property, line_colors, line)
58
+ elsif value.is_a? Symbol
59
+ before_match_colors, after_match_colors = colors.line_substring_color_transitions(
60
+ line_colors, property.color_to_use
61
+ )
62
+ line.gsub(selector) { |match| "#{before_match_colors}#{match}#{after_match_colors}" }
63
+ else
64
+ line
65
+ end
66
+ end
67
+
68
+ # Use the Regexp selector to determine match groups on the given line. Then apply the array
69
+ # of colors to the each match group, first color to first group and so on. Ignore the last
70
+ # match groups if they do not have corresponding colors and vice versa.
71
+ #
72
+ # If there are color that should be applied to the entire line then make sure to turn them
73
+ # off before applying match colors and back on after the match.
74
+ def apply_colors_to_multiple_matches(property, line_colors, line)
75
+ selector, match_colors = property.selector, [property.color_to_use].flatten
76
+ match_data = selector.match(line)
77
+ return line unless match_data && (match_data.size > 1)
78
+ colored_line = line.dup
79
+
80
+ offsets = (1...(match_data.size)).to_a.map { |idx| match_data.offset(idx) }
81
+
82
+ # Work backward through the matches because working forward would throw off indicies.
83
+ # Determine the original index of the match and apply the corresponding color. If we
84
+ # do not have enough colors for the last match(es) then skip them.
85
+ # TODO: clean this up, it just feels hacky
86
+ offsets.reverse.each_with_index do |offset, index|
87
+ orig_match_index = offsets.size - index - 1
88
+ match_color = match_colors[orig_match_index]
89
+ next unless match_color
90
+
91
+ before_match_colors, after_match_colors = colors.line_substring_color_transitions(
92
+ line_colors, match_color
93
+ )
94
+
95
+ beg_idx, end_idx = offset
96
+ colored_line.insert(end_idx, after_match_colors)
97
+ colored_line.insert(beg_idx, before_match_colors)
98
+ end
99
+
100
+ colored_line
101
+ end
102
+
103
+ module PropertyMixin
104
+ # Can be overridden if the color to use should be derived differently
105
+ def color_to_use
106
+ value
107
+ end
108
+
109
+ def valid_value?
110
+ self.class::VALUES.include?(value)
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,158 @@
1
+ require 'ostruct'
2
+
3
+ module Styles
4
+ module SubEngines
5
+ class Layout < Base
6
+
7
+ def process(line)
8
+ layout_sub_engine_properties = extract_sub_engine_properties line.applicable_properties
9
+
10
+ if should_hide?(line)
11
+ line.text = nil
12
+ return line
13
+ end
14
+
15
+ apply_width_and_text_align(line)
16
+ apply_padding_border_margin(line)
17
+
18
+ line
19
+ end
20
+
21
+ private
22
+
23
+ def should_hide?(line)
24
+ display_property = line.applicable_properties.find { |prop| prop.class == ::Styles::Properties::Display }
25
+ display_property && ::Styles::Properties::Display::HIDE_VALUES.include?(display_property.value)
26
+ end
27
+
28
+ def apply_width_and_text_align(line)
29
+ width_prop, text_align_prop = line.prop(:width), line.prop(:text_align)
30
+ size_no_color = colors.uncolor(line.text).size
31
+ width = width_prop ? width_prop.width : terminal_width
32
+
33
+ return if size_no_color >= width
34
+ diff = width - size_no_color
35
+
36
+ bg_color = if bg_color_prop = line.prop(:background_color)
37
+ bg_color_prop.color_to_use
38
+ else
39
+ :none
40
+ end
41
+
42
+ if text_align_prop
43
+ case text_align_prop.value
44
+ when :left
45
+ # Pad right only if width explicitly set
46
+ line.text = "#{line.text}#{colors.color(' ' * diff, bg_color)}" if width_prop
47
+ when :right
48
+ pad = ' ' * diff
49
+ pad = colors.color(pad, bg_color) if width_prop
50
+ line.text = "#{pad}#{line.text}"
51
+ when :center
52
+ before, after = (' ' * (diff/2)), (' ' * (diff/2 + diff%2))
53
+ before, after = [before, after].map { |pad| colors.color(pad, bg_color)} if width_prop
54
+ line.text = "#{before}#{line.text}#{after}"
55
+ end
56
+ else
57
+ # Assume left align, pad right only if width explicitly set
58
+ line.text = "#{line.text}#{colors.color(' ' * diff, bg_color)}" if width_prop
59
+ end
60
+ end
61
+
62
+ def apply_padding_border_margin(line)
63
+ padding, margin = (line.prop(:padding) || blank_space_property), (line.prop(:margin) || blank_space_property)
64
+ border = line.prop(:border) || ::Styles::Properties::Border.new(:any, :border, :none)
65
+
66
+ return unless padding || margin || border
67
+
68
+ bg_color = if bg_color_prop = line.prop(:background_color)
69
+ bg_color_prop.valid_value? ? bg_color_prop.color_to_use : :none
70
+ else
71
+ :none
72
+ end
73
+
74
+ # First establish the main line padding and border
75
+ line.left = colors.force_color("#{border.left_char}#{' ' * padding.left}", bg_color)
76
+ line.right = colors.force_color("#{' ' * padding.right}#{border.right_char}", bg_color)
77
+
78
+ # Calculate margins and add to the main line
79
+ margin_left, margin_right =
80
+ if margin.left == :auto || margin.right == :auto
81
+ diff = terminal_width - line.total_width
82
+ if diff > 0
83
+ [(' ' * (diff/2)), (' ' * (diff/2 + diff%2))]
84
+ else
85
+ ['', '']
86
+ end
87
+ else
88
+ [' ' * margin.left, ' ' * margin.right]
89
+ end
90
+
91
+ line.left = margin_left + line.left
92
+ line.right = line.right + margin_right
93
+
94
+ content_width = line.content_width
95
+ border_width = padding.left + content_width + padding.right
96
+
97
+ margin_line = "#{' ' * line.total_width}\n"
98
+ padding_line = "#{margin_left}#{colors.color(' ' * border_width, bg_color)}#{margin_right}\n"
99
+
100
+ line.top = margin_line * margin.top if margin.top.is_a?(Integer)
101
+
102
+ extender_line = margin_left +
103
+ colors.force_color("#{border.left_char}#{' ' * border_width}#{border.right_char}", bg_color) +
104
+ margin_right + "\n"
105
+
106
+ if border.top == :none
107
+ line.top << padding_line * padding.top
108
+ else
109
+ line.top << margin_left +
110
+ colors.force_color("#{border.top_line_chars(border_width)}", bg_color) +
111
+ margin_right + "\n"
112
+ line.top << (extender_line * padding.top) if padding.top > 0
113
+ end
114
+
115
+ if border.bottom == :none
116
+ line.bottom = padding_line * padding.bottom
117
+ else
118
+ line.bottom = ''
119
+ line.bottom << (extender_line * padding.bottom) if padding.bottom > 0
120
+ line.bottom << margin_left +
121
+ colors.force_color("#{border.bottom_line_chars(border_width)}", bg_color) +
122
+ margin_right + "\n"
123
+ end
124
+
125
+ line.bottom << margin_line * margin.bottom if margin.bottom.is_a?(Integer)
126
+ end
127
+
128
+ def apply_margin(line)
129
+ margin = line.prop(:margin)
130
+ return unless margin
131
+
132
+ if margin.left > 0
133
+ line.left = ' ' * margin.left
134
+ end
135
+ end
136
+
137
+ # Tries to determine terminal width, returns 80 by default
138
+ def terminal_width
139
+ require 'io/console'
140
+ IO.console.winsize[1]
141
+ rescue LoadError
142
+ begin
143
+ `tput co`.to_i
144
+ rescue
145
+ 80
146
+ end
147
+ end
148
+
149
+ def blank_space_property
150
+ OpenStruct.new(top: 0, right: 0, bottom: 0, left: 0)
151
+ end
152
+
153
+ def blank_border_property
154
+ OpenStruct.new(top: :none, right: :none, bottom: :none, left: :none)
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,19 @@
1
+ module Styles
2
+ module SubEngines
3
+ class PreProcessor < Base
4
+ def process(line)
5
+ apply_function(line)
6
+
7
+ line
8
+ end
9
+
10
+ private
11
+
12
+ def apply_function(line)
13
+ if (fn = line.prop(:function)) && fn.value.respond_to?(:call)
14
+ line.text = fn.value.call(line.text)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module Styles
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'styles/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = 'styles'
8
+ gem.version = Styles::VERSION
9
+ gem.authors = ['Aaron Royer']
10
+ gem.email = ['aaronroyer@gmail.com']
11
+ gem.description = %q{plain text stylesheets}
12
+ gem.summary = %q{A utility for processing text that provides a Ruby DSL leveraging CSS concepts}
13
+ gem.homepage = 'http://github.com/aaronroyer/styles'
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ['lib']
19
+
20
+ gem.required_ruby_version = '>= 1.9.0'
21
+
22
+ gem.add_dependency 'term-ansicolor', '>= 1.1.0'
23
+
24
+ gem.add_development_dependency 'timecop'
25
+ gem.add_development_dependency 'mocha'
26
+ end
@@ -0,0 +1,92 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+ require 'term/ansicolor'
3
+ require 'tmpdir'
4
+ require 'stringio'
5
+ require 'fileutils'
6
+ require 'timecop'
7
+
8
+ class ApplicationTest < MiniTest::Unit::TestCase
9
+ def setup
10
+ ENV['STYLES_DIR'] = @stylesheets_dir = Dir.mktmpdir
11
+ end
12
+
13
+ def teardown
14
+ FileUtils.remove_entry_secure(@stylesheets_dir) if File.directory?(@stylesheets_dir)
15
+ ENV['STYLES_DIR'] = nil
16
+ end
17
+
18
+ def test_run_with_default_stylesheet
19
+ File.open(File.join(stylesheets_dir, 'default.rb'), 'w') do |f|
20
+ f.write " 'hide' - { display: none } "
21
+ end
22
+
23
+ input = StringIO.new "hide this\nnot this\n", 'r'
24
+ output = StringIO.new
25
+
26
+ app = ::Styles::Application.new input_stream: input, output_stream: output
27
+ app.send :read_stylesheets
28
+ app.send :process
29
+
30
+ assert_equal "not this\n", output.string
31
+ end
32
+
33
+ def test_auto_reload_updated_stylesheet
34
+ default_stylesheet = File.join stylesheets_dir, 'default.rb'
35
+ File.open(default_stylesheet, 'w') { |f| f.write " 'word' - { color: green } " }
36
+
37
+ input = StringIO.new "has a word\nhas a word\n", 'r'
38
+ output = StringIO.new
39
+ app = ::Styles::Application.new input_stream: input, output_stream: output
40
+
41
+ app.send :read_stylesheets
42
+ app.send :process_next_line
43
+ assert_equal "#{color.green}has a word#{color.reset}\n", output.string
44
+
45
+ orig_time = Time.now
46
+
47
+ output.truncate(output.rewind)
48
+
49
+ File.open(default_stylesheet, 'w') { |f| f.write " 'word' - { color: red } " }
50
+ FileUtils.touch(default_stylesheet, mtime: orig_time + 2)
51
+
52
+ Timecop.freeze(orig_time + 4) do
53
+ app.send :process_next_line
54
+ end
55
+ assert_equal "#{color.red}has a word#{color.reset}\n", output.string
56
+ end
57
+
58
+ def test_does_not_auto_reload_updated_stylesheet_inside_check_interval
59
+ interval = ::Styles::Application::STYLESHEET_CHECK_INTERVAL_SECONDS
60
+
61
+ default_stylesheet = File.join stylesheets_dir, 'default.rb'
62
+ File.open(default_stylesheet, 'w') { |f| f.write " 'word' - { color: green } " }
63
+
64
+ input = StringIO.new "has a word\nhas a word\n", 'r'
65
+ output = StringIO.new
66
+ app = ::Styles::Application.new input_stream: input, output_stream: output
67
+
68
+ app.send :read_stylesheets
69
+ app.send :process_next_line
70
+ assert_equal "#{color.green}has a word#{color.reset}\n", output.string
71
+
72
+ orig_time = Time.now
73
+
74
+ output.truncate(output.rewind)
75
+
76
+ File.open(default_stylesheet, 'w') { |f| f.write " 'word' - { color: red } " }
77
+ FileUtils.touch(default_stylesheet, mtime: orig_time + (interval - 1))
78
+
79
+ Timecop.freeze(orig_time + (interval - 1)) do
80
+ app.send :process_next_line
81
+ end
82
+ assert_equal "#{color.green}has a word#{color.reset}\n", output.string
83
+ end
84
+
85
+ private
86
+
87
+ attr_reader :stylesheets_dir
88
+
89
+ def color
90
+ ::Term::ANSIColor
91
+ end
92
+ end
@@ -0,0 +1,162 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+ require 'term/ansicolor'
3
+
4
+ class ColorsTest < MiniTest::Unit::TestCase
5
+
6
+ def test_can_access_basic_colors_with_brackets
7
+ assert_equal ansi.red, c[:red]
8
+ assert_equal ansi.blue, c[:blue]
9
+ assert_equal ansi.on_cyan, c[:on_cyan]
10
+
11
+ assert_nil c[:bogus]
12
+ end
13
+
14
+ def test_can_access_compound_colors_with_brackets
15
+ assert_equal ansi.on_white + ansi.red , c[:red_on_white]
16
+ assert_equal ansi.blue + ansi.on_blue, c[:blue_on_blue]
17
+
18
+ assert_nil c[:red_on_bogus]
19
+ assert_nil c[:bogus_on_bogus]
20
+ assert_nil c[:bogus_on_red]
21
+ end
22
+
23
+ def test_can_access_multiple_colors_with_brackets
24
+ assert_equal ansi.red + ansi.white, c[:red, :white]
25
+ assert_equal ansi.blue + ansi.red + ansi.white, c[:red, :white, :blue]
26
+ assert_equal ansi.on_blue + ansi.red + ansi.white, c[:red, :white_on_blue]
27
+ end
28
+
29
+ def test_can_use_arrays_of_colors_with_brackets
30
+ assert_equal ansi.red + ansi.white, c[[:red, :white]]
31
+ assert_equal ansi.blue + ansi.red + ansi.white, c[[:red, :white, :blue]]
32
+ assert_equal ansi.on_blue + ansi.red + ansi.white, c[[:red, :white_on_blue]]
33
+ end
34
+
35
+ def test_maps_certain_css_values_to_ansi
36
+ assert_equal ansi.strikethrough, c[:line_through]
37
+ end
38
+
39
+ def test_basic_colors_are_valid
40
+ assert c.valid?(:red)
41
+ assert c.valid?(:on_red)
42
+ assert c.valid?(:green)
43
+ assert c.valid?(:on_green)
44
+ assert c.valid?(:magenta)
45
+ assert c.valid?(:on_magenta)
46
+
47
+ assert !c.valid?(:bogus)
48
+ assert !c.valid?(:on_bogus)
49
+ end
50
+
51
+ def test_compound_colors_are_valid
52
+ assert c.valid?(:red_on_white)
53
+ assert c.valid?(:blue_on_blue)
54
+ assert c.valid?(:black_on_blue)
55
+
56
+ assert !c.valid?(:blue_on_bogus)
57
+ assert !c.valid?(:bogus_on_blue)
58
+ end
59
+
60
+ def test_various_other_ansi_escape_codes_are_valid
61
+ assert c.valid?(:bold)
62
+ assert c.valid?(:line_through), 'Mapped values are also valid'
63
+ end
64
+
65
+ def test_hard_color_transitions
66
+ assert_equal ansi.red, c.color_transition([:blue], [:red])
67
+ assert_equal ansi.red, c.color_transition(:blue, :red)
68
+ assert_equal ansi.reset + ansi.red, c.color_transition(:on_blue, :red)
69
+ assert_equal ansi.red + ansi.on_white, c.color_transition([:green, :on_blue], [:red, :on_white])
70
+ assert_equal ansi.reset + ansi.underline, c.color_transition(:blue, :underline)
71
+ assert_equal ansi.reset + ansi.blue, c.color_transition(:underline, :blue)
72
+ end
73
+
74
+ def test_soft_color_transitions
75
+ assert_equal ansi.red, c.color_transition([:blue], [:red], false)
76
+ assert_equal ansi.red, c.color_transition([:on_blue], [:red], false)
77
+ assert_equal ansi.on_red, c.color_transition([:blue], [:on_red], false)
78
+ assert_equal ansi.red, c.color_transition([:green, :on_blue], [:red], false)
79
+ assert_equal ansi.underline, c.color_transition(:blue, :underline, false)
80
+ assert_equal ansi.blue, c.color_transition(:underline, :blue, false)
81
+
82
+ assert_equal ansi.red, c.color_transition([:blue], [:red], false)
83
+ assert_equal '', c.color_transition([:blue], [:blue], false)
84
+ end
85
+
86
+ def test_color_transitions_blank_when_not_necessary
87
+ assert_equal '', c.color_transition([:blue], [:blue])
88
+ assert_equal '', c.color_transition([:on_white], [:on_white])
89
+ assert_equal '', c.color_transition([:underline], [:underline])
90
+ assert_equal '', c.color_transition([:blue, :on_white], [:blue, :on_white])
91
+ assert_equal '', c.color_transition([:blue, :on_white, :underline], [:blue, :on_white, :underline])
92
+ assert_equal '', c.color_transition([:blue, :on_white], [:on_white, :blue])
93
+ end
94
+
95
+ def test_color_transition_with_negations
96
+ assert_equal ansi.reset, c.color_transition(:blue, :no_fg_color)
97
+ assert_equal ansi.reset, c.color_transition(:on_blue, :no_bg_color)
98
+ assert_equal ansi.reset, c.color_transition(:blue, :no_bg_color)
99
+
100
+ assert_equal ansi.reset, c.color_transition([:blue, :on_white], :no_bg_color)
101
+ assert_equal ansi.reset + ansi.red, c.color_transition([:blue, :on_white], [:red, :no_bg_color])
102
+ assert_equal ansi.reset + ansi.blue, c.color_transition([:blue, :on_white], [:blue, :no_bg_color])
103
+
104
+ assert_equal ansi.reset + ansi.red, c.color_transition([:blue, :on_white], [:red, :no_bg_color])
105
+ assert_equal ansi.reset + ansi.blue, c.color_transition([:blue, :on_white], [:blue, :no_bg_color])
106
+
107
+ assert_equal ansi.blue, c.color_transition(:no_fg_color, :blue)
108
+ assert_equal ansi.on_red, c.color_transition(:no_bg_color, :on_red)
109
+ assert_equal '', c.color_transition([:blue, :no_bg_color], :blue)
110
+ assert_equal ansi.on_blue, c.color_transition(:no_fg_color, :on_blue)
111
+ assert_equal '', c.color_transition([:no_fg_color, :on_blue], :on_blue)
112
+
113
+
114
+ assert_equal ansi.reset, c.color_transition(:blue, :no_fg_color, false)
115
+ assert_equal ansi.reset, c.color_transition(:on_blue, :no_bg_color, false)
116
+ assert_equal '', c.color_transition(:blue, :no_bg_color, false)
117
+
118
+ assert_equal ansi.reset + ansi.blue, c.color_transition([:blue, :on_white], :no_bg_color, false)
119
+ assert_equal ansi.reset + ansi.red, c.color_transition([:blue, :on_white], [:red, :no_bg_color], false)
120
+ assert_equal ansi.reset + ansi.blue, c.color_transition([:blue, :on_white], [:blue, :no_bg_color], false)
121
+
122
+ assert_equal ansi.reset + ansi.red, c.color_transition([:blue, :on_white], [:red, :no_bg_color], false)
123
+ assert_equal ansi.reset + ansi.blue, c.color_transition([:blue, :on_white], [:blue, :no_bg_color], false)
124
+
125
+ assert_equal ansi.blue, c.color_transition(:no_fg_color, :blue, false)
126
+ assert_equal ansi.on_red, c.color_transition(:no_bg_color, :on_red, false)
127
+ assert_equal '', c.color_transition([:blue, :no_bg_color], :blue, false)
128
+ assert_equal ansi.on_blue, c.color_transition(:no_fg_color, :on_blue, false)
129
+ assert_equal '', c.color_transition([:no_fg_color, :on_blue], :on_blue, false)
130
+ end
131
+
132
+ def test_can_color_strings_with_auto_reset
133
+ assert_equal ansi.blue + 'hello' + ansi.reset, c.color('hello', :blue)
134
+ assert_equal ansi.blue + ansi.on_green + 'hello' + ansi.reset, c.color('hello', :blue, :on_green)
135
+ assert_equal ansi.blue + ansi.on_green + 'hello' + ansi.reset, c.color('hello', [:blue, :on_green])
136
+ assert_equal ansi.underline + 'hello' + ansi.reset, c.color('hello', :underline)
137
+ assert_equal 'hello', c.color('hello', :invalid)
138
+ assert_equal 'hello', c.color('hello', :none)
139
+ assert_equal ansi.red + 'hello' + ansi.reset, c.color('hello', :none, :red)
140
+ assert_equal '', c.color('', :red)
141
+ end
142
+
143
+ def test_force_color
144
+ str = "has #{ansi.red}some#{ansi.reset} red"
145
+ assert_equal "#{ansi.on_blue}has #{ansi.red}some#{ansi.reset}#{ansi.on_blue} red#{ansi.reset}",
146
+ c.force_color(str, :on_blue)
147
+
148
+ assert_equal ansi.blue + 'hello' + ansi.reset, c.force_color('hello', :blue)
149
+ assert_equal '', c.force_color('', :red)
150
+ end
151
+
152
+ private
153
+
154
+ def c
155
+ ::Styles::Colors
156
+ end
157
+
158
+ def ansi
159
+ ::Term::ANSIColor
160
+ end
161
+ end
162
+