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,107 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+ require 'term/ansicolor'
3
+
4
+ class TextAlignTest < MiniTest::Unit::TestCase
5
+ def setup
6
+ @sub_engine = ::Styles::SubEngines::Layout.new
7
+ end
8
+
9
+ def test_left_align
10
+ test_line = 'this is a test line that should be left aligned'
11
+
12
+ sub_engine.stubs(:terminal_width).returns(80)
13
+ assert_equal test_line, process('test', :left, test_line)
14
+
15
+ sub_engine.stubs(:terminal_width).returns(10)
16
+ assert_equal test_line, process('test', :left, test_line)
17
+ end
18
+
19
+ def test_right_align
20
+ test_line = 'this is a test line that should be right aligned'
21
+ test_line_color = "this is a test line that #{color.red}should#{color.reset} be right aligned"
22
+
23
+ sub_engine.stubs(:terminal_width).returns(80)
24
+ assert_equal "#{' ' * 32}#{test_line}", process('test', :right, test_line)
25
+ assert_equal "#{' ' * 32}#{test_line_color}", process('test', :right, test_line_color)
26
+
27
+ sub_engine.stubs(:terminal_width).returns(10)
28
+ assert_equal test_line, process('test', :right, test_line)
29
+ assert_equal test_line_color, process('test', :right, test_line_color)
30
+ end
31
+
32
+ def test_center_align
33
+ test_line = 'this is a test'
34
+ test_line_color = "this is a #{color.blue}test#{color.reset}"
35
+
36
+ sub_engine.stubs(:terminal_width).returns(80)
37
+ assert_equal "#{' ' * 33}#{test_line}#{' ' * 33}", process('test', :center, test_line)
38
+ assert_equal "#{' ' * 33}#{test_line_color}#{' ' * 33}", process('test', :center, test_line_color)
39
+
40
+ odd_test_line = 'odd'
41
+ sub_engine.stubs(:terminal_width).returns(10)
42
+ assert_equal ' odd ', process('odd', :center, odd_test_line)
43
+
44
+ sub_engine.stubs(:terminal_width).returns(10)
45
+ assert_equal test_line, process('test', :center, test_line)
46
+ assert_equal test_line_color, process('test', :center, test_line_color)
47
+ end
48
+
49
+ def test_works_with_explicit_width
50
+ test_line = 'this is a test'
51
+ test_line_color = "this is a #{color.blue}test#{color.reset}"
52
+
53
+ sub_engine.stubs(:terminal_width).returns(80)
54
+
55
+ assert_equal "#{test_line}#{' ' * 6}", process_with_width('test', :left, 20, test_line)
56
+ assert_equal "#{' ' * 6}#{test_line}", process_with_width('test', :right, 20, test_line)
57
+ assert_equal " #{test_line} ", process_with_width('test', :center, 20, test_line)
58
+
59
+ assert_equal test_line, process_with_width('test', :left, 10, test_line)
60
+ assert_equal test_line, process_with_width('test', :right, 10, test_line)
61
+ assert_equal test_line, process_with_width('test', :center, 10, test_line)
62
+ end
63
+
64
+ def test_applies_background_color_to_full_width
65
+ test_line = "#{color.on_blue}test line#{color.reset}"
66
+ assert_equal "#{test_line}#{color.on_blue}#{(' ' * 11)}#{color.reset}",
67
+ process_with_width_and_bg_color('test', :left, 20, :blue, test_line)
68
+ assert_equal "#{color.on_blue}#{(' ' * 11)}#{color.reset}#{test_line}",
69
+ process_with_width_and_bg_color('test', :right, 20, :blue, test_line)
70
+ assert_equal "#{color.on_blue}#{(' ' * 5)}#{color.reset}#{test_line}#{color.on_blue}#{(' ' * 6)}#{color.reset}",
71
+ process_with_width_and_bg_color('test', :center, 20, :blue, test_line)
72
+ end
73
+
74
+ private
75
+
76
+ attr_reader :sub_engine
77
+
78
+ def process(selector, value, line)
79
+ line = ::Styles::Line.new(line, [::Styles::Properties::TextAlign.new(selector, :text_align, value)])
80
+ result = sub_engine.process(line)
81
+ result.to_s
82
+ end
83
+
84
+ def process_with_width(selector, text_align_value, width_value, line)
85
+ line = ::Styles::Line.new(line, [
86
+ ::Styles::Properties::TextAlign.new(selector, :text_align, text_align_value),
87
+ ::Styles::Properties::Width.new(selector, :width, width_value),
88
+ ])
89
+ result = sub_engine.process(line)
90
+ result.to_s
91
+ end
92
+
93
+ def process_with_width_and_bg_color(selector, text_align_value, width_value, bg_color_value, line)
94
+ line = ::Styles::Line.new(line, [
95
+ ::Styles::Properties::TextAlign.new(selector, :text_align, text_align_value),
96
+ ::Styles::Properties::Width.new(selector, :width, width_value),
97
+ ::Styles::Properties::BackgroundColor.new(selector, :background_color, bg_color_value)
98
+ ])
99
+ result = sub_engine.process(line)
100
+ result.to_s
101
+ end
102
+
103
+ def color
104
+ ::Term::ANSIColor
105
+ end
106
+ end
107
+
@@ -0,0 +1,25 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+ require 'term/ansicolor'
3
+
4
+ class TextDecorationTest < MiniTest::Unit::TestCase
5
+ def test_decorate_text
6
+ test_line = 'this is a test line'
7
+
8
+ assert_equal "#{color.underline}#{test_line}#{color.reset}", process('test', :underline, test_line)
9
+ assert_equal "#{color.strikethrough}#{test_line}#{color.reset}", process('test', :strikethrough, test_line)
10
+ assert_equal "#{color.strikethrough}#{test_line}#{color.reset}", process('test', :line_through, test_line)
11
+ assert_equal test_line, process('test', :none, test_line)
12
+ end
13
+
14
+ private
15
+
16
+ def process(selector, value, line)
17
+ sub_engine = ::Styles::SubEngines::Color.new
18
+ line = ::Styles::Line.new(line, [::Styles::Properties::TextDecoration.new(selector, :text_decoration, value)])
19
+ sub_engine.process(line).to_s
20
+ end
21
+
22
+ def color
23
+ ::Term::ANSIColor
24
+ end
25
+ end
@@ -0,0 +1,41 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+ require 'term/ansicolor'
3
+
4
+ class WidthTest < MiniTest::Unit::TestCase
5
+ def setup
6
+ @sub_engine = ::Styles::SubEngines::Layout.new
7
+ end
8
+
9
+ def test_basic_width
10
+ test_line = 'test line'
11
+ assert_equal test_line + (' ' * 11), process('test', 20, test_line)
12
+ end
13
+
14
+ def test_applies_background_color_to_full_width
15
+ test_line = "#{color.on_blue}test line#{color.reset}"
16
+ assert_equal "#{test_line}#{color.on_blue}#{(' ' * 11)}#{color.reset}", process_with_bg_color('test', 20, :blue, test_line)
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :sub_engine
22
+
23
+ def process(selector, value, line)
24
+ line = ::Styles::Line.new(line, [::Styles::Properties::Width.new(selector, :text_align, value)])
25
+ result = sub_engine.process(line)
26
+ result.to_s
27
+ end
28
+
29
+ def process_with_bg_color(selector, width_value, bg_color_value, line)
30
+ line = ::Styles::Line.new(line, [
31
+ ::Styles::Properties::Width.new(selector, :width, width_value),
32
+ ::Styles::Properties::BackgroundColor.new(selector, :background_color, bg_color_value)
33
+ ])
34
+ result = sub_engine.process(line)
35
+ result.to_s
36
+ end
37
+
38
+ def color
39
+ ::Term::ANSIColor
40
+ end
41
+ end
@@ -0,0 +1,39 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+ require 'term/ansicolor'
3
+
4
+ class RuleTest < MiniTest::Unit::TestCase
5
+ def test_colored_lines_do_not_cause_false_positives
6
+ line = "#{color.green}This is green!#{color.reset}"
7
+ properties = { display: :none }
8
+ string_rule = Styles::Rule.new('32', properties)
9
+ regex_rule = Styles::Rule.new(/\d\d/, properties)
10
+
11
+ assert !string_rule.applicable?(line), 'string selector does not match against color code'
12
+ assert !regex_rule.applicable?(line), 'regex selector does not match against color code'
13
+ end
14
+
15
+ def test_colored_lines_do_not_cause_false_negatives
16
+ line = "I am ec#{color.yellow}static#{color.reset} right now"
17
+ properties = { display: :none }
18
+ string_rule = Styles::Rule.new('ecstatic', properties)
19
+ regex_rule = Styles::Rule.new(/e\w+c/, properties)
20
+
21
+ assert string_rule.applicable?(line), 'string selector does not match against color code'
22
+ assert regex_rule.applicable?(line), 'regex selector does not match against color code'
23
+ end
24
+
25
+ def test_unrecognized_properties_are_collected
26
+ properties = { color: :red, display: :none, bogus: :value }
27
+ rule = Styles::Rule.new('test', properties)
28
+
29
+ assert_equal 2, rule.properties.size
30
+ assert_equal 1, rule.unrecognized_properties.keys.size
31
+ assert_equal :value, rule.unrecognized_properties[:bogus]
32
+ end
33
+
34
+ private
35
+
36
+ def color
37
+ ::Term::ANSIColor
38
+ end
39
+ end
@@ -0,0 +1,245 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+ require 'term/ansicolor'
3
+ require 'tempfile'
4
+ require 'timecop'
5
+
6
+ class StylesheetTest < MiniTest::Unit::TestCase
7
+ def teardown
8
+ tmp_stylesheet_files.each do |f|
9
+ f.close
10
+ f.unlink
11
+ end
12
+ end
13
+
14
+ def test_can_create_a_stylesheet_from_a_string
15
+ stylesheet_text = <<-STYLESHEET
16
+ 'good' - {
17
+ color: green
18
+ }
19
+
20
+ /bad/ - {
21
+ color: red
22
+ }
23
+
24
+ :blank - {
25
+ display: none
26
+ }
27
+ STYLESHEET
28
+
29
+ sheet = Styles::Stylesheet.from_string(stylesheet_text)
30
+ assert_equal 3, sheet.rules.size
31
+
32
+ rules = {}
33
+ ['good', /bad/, :blank].each do |selector|
34
+ rules[selector] = sheet.rules.find { |rule| rule.selector == selector }
35
+ end
36
+
37
+ good_prop = rules['good'].properties.first
38
+ assert_equal Styles::Properties::Color, good_prop.class
39
+ assert_equal :green, good_prop.value
40
+
41
+ bad_prop = rules[/bad/].properties.first
42
+ assert_equal Styles::Properties::Color, bad_prop.class
43
+ assert_equal :red, bad_prop.value
44
+
45
+ blank_prop = rules[:blank].properties.first
46
+ assert_equal Styles::Properties::Display, blank_prop.class
47
+ assert_equal :none, blank_prop.value
48
+ end
49
+
50
+ def test_raises_stylesheet_load_error_when_loading_fails
51
+ bad_stylesheet_text = <<-STYLESHEET
52
+ 'ugh' - {
53
+ color: green
54
+ STYLESHEET
55
+
56
+ assert_raises(::Styles::StylesheetLoadError) do
57
+ Styles::Stylesheet.from_string bad_stylesheet_text
58
+ end
59
+
60
+ assert_raises(::Styles::StylesheetLoadError) do
61
+ Styles::Stylesheet.new('somebogusfilenamethatdoesnotexistanywhere.rb')
62
+ end
63
+ end
64
+
65
+ def test_can_create_a_stylesheet_from_a_file
66
+ stylesheet_text = <<-STYLESHEET
67
+ 'good' - {
68
+ color: green
69
+ }
70
+
71
+ /bad/ - {
72
+ color: red
73
+ }
74
+
75
+ :blank - {
76
+ display: none
77
+ }
78
+ STYLESHEET
79
+
80
+ file = tmp_stylesheet_file(stylesheet_text)
81
+ sheet = Styles::Stylesheet.new(file.path)
82
+ assert_equal 3, sheet.rules.size
83
+ end
84
+
85
+ def test_can_reload_rules_from_a_file
86
+ stylesheet_text = <<-STYLESHEET
87
+ 'good' - {
88
+ color: green,
89
+ font_weight: bold
90
+ }
91
+
92
+ /bad/ - {
93
+ color: red
94
+ }
95
+ STYLESHEET
96
+
97
+ file = tmp_stylesheet_file(stylesheet_text)
98
+ sheet = Styles::Stylesheet.new(file.path)
99
+ assert_equal 2, sheet.rules.size
100
+
101
+ good_rule = sheet.rules.find { |rule| rule.selector == 'good' }
102
+ bad_rule = sheet.rules.find { |rule| rule.selector == /bad/ }
103
+
104
+ assert_equal 2, good_rule.properties.size
105
+ color_prop = good_rule.properties.find { |p| p.class == ::Styles::Properties::Color }
106
+ assert_equal :green, color_prop.value
107
+ assert good_rule.properties.find { |p| p.class == ::Styles::Properties::FontWeight }
108
+
109
+ assert 1, bad_rule.properties.size
110
+
111
+ new_stylesheet_text = <<-NEW_STYLESHEET
112
+ 'good' - {
113
+ color: cyan,
114
+ text_decoration: underline,
115
+ match_color: blue
116
+ }
117
+
118
+ /other/ - {
119
+ color: yellow
120
+ }
121
+
122
+ :blank - {
123
+ display: none
124
+ }
125
+ NEW_STYLESHEET
126
+
127
+ File.open(file.path, 'w') { |f| f.write new_stylesheet_text }
128
+
129
+ sheet.reload
130
+
131
+ assert_equal 3, sheet.rules.size
132
+
133
+ good_rule = sheet.rules.find { |rule| rule.selector == 'good' }
134
+ assert_equal 3, good_rule.properties.size
135
+ color_prop = good_rule.properties.find { |p| p.class == ::Styles::Properties::Color }
136
+ assert_equal :cyan, color_prop.value
137
+ assert good_rule.properties.find { |p| p.class == ::Styles::Properties::TextDecoration }
138
+ assert good_rule.properties.find { |p| p.class == ::Styles::Properties::MatchColor }
139
+
140
+ assert_nil sheet.rules.find { |rule| rule.selector == /bad/ }
141
+ assert sheet.rules.find { |rule| rule.selector == :blank }
142
+ end
143
+
144
+ def test_reload_if_outdated
145
+ stylesheet_text = <<-STYLESHEET
146
+ 'good' - {
147
+ color: green
148
+ }
149
+ STYLESHEET
150
+
151
+ new_stylesheet_text = <<-STYLESHEET
152
+ 'good' - {
153
+ color: green
154
+ }
155
+ 'other' - {
156
+ color: cyan
157
+ }
158
+ STYLESHEET
159
+
160
+ file = tmp_stylesheet_file(stylesheet_text)
161
+ sheet = nil
162
+
163
+ Timecop.freeze(File.mtime(file.path) + 5) do
164
+ sheet = Styles::Stylesheet.new(file.path)
165
+ assert_equal 1, sheet.rules.size
166
+ assert !sheet.outdated?
167
+ sheet.reload_if_outdated
168
+ assert_equal 1, sheet.rules.size
169
+ end
170
+
171
+ Timecop.freeze(Time.now - 5) do
172
+ sheet = Styles::Stylesheet.new(file.path)
173
+ end
174
+
175
+ File.open(file.path, 'w') { |f| f.write new_stylesheet_text }
176
+
177
+ assert_equal 1, sheet.rules.size
178
+ assert sheet.outdated?
179
+
180
+ sheet.reload_if_outdated
181
+ assert_equal 2, sheet.rules.size
182
+ end
183
+
184
+ def test_unrecognized_property_names
185
+ stylesheet_text = <<-STYLESHEET
186
+ 'good' - {
187
+ color: green,
188
+ bogus: value,
189
+ match_color: yellow
190
+ }
191
+ 'other' - { display: none }
192
+ STYLESHEET
193
+
194
+ sheet = Styles::Stylesheet.from_string(stylesheet_text)
195
+ assert_equal 2, sheet.rules.size
196
+ assert_equal [:bogus], sheet.unrecognized_property_names
197
+ end
198
+
199
+ def test_old_rules_are_retained_when_reload_crashes
200
+ stylesheet_text = <<-STYLESHEET
201
+ 'good' - {
202
+ color: green,
203
+ font_weight: bold
204
+ }
205
+
206
+ /bad/ - {
207
+ color: red
208
+ }
209
+ STYLESHEET
210
+
211
+ file = tmp_stylesheet_file(stylesheet_text)
212
+ sheet = Styles::Stylesheet.new(file.path)
213
+ assert_equal 2, sheet.rules.size
214
+ assert sheet.rules.find { |rule| rule.selector == 'good' }
215
+ assert sheet.rules.find { |rule| rule.selector == /bad/ }
216
+
217
+ new_stylesheet_text = <<-NEW_STYLESHEET
218
+ 'wat' - {
219
+ display: none
220
+ NEW_STYLESHEET
221
+
222
+ File.open(file.path, 'w') { |f| f.write new_stylesheet_text }
223
+
224
+ assert_raises(::Styles::StylesheetLoadError) { sheet.reload }
225
+
226
+ assert_equal 2, sheet.rules.size
227
+ assert sheet.rules.find { |rule| rule.selector == 'good' }
228
+ assert sheet.rules.find { |rule| rule.selector == /bad/ }
229
+ end
230
+
231
+ private
232
+
233
+ def tmp_stylesheet_files
234
+ @tmp_stylesheet_files ||= []
235
+ end
236
+
237
+ def tmp_stylesheet_file(text, name=nil)
238
+ name ||= 'stylesheet'
239
+ file = Tempfile.new [name, '.rb']
240
+ file.write text
241
+ file.close
242
+ tmp_stylesheet_files << file
243
+ file
244
+ end
245
+ end