megingiard 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 (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +3 -0
  6. data/Gemfile +11 -0
  7. data/Gemfile.devtools +71 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +30 -0
  10. data/Rakefile +6 -0
  11. data/config/devtools.yml +4 -0
  12. data/config/flay.yml +3 -0
  13. data/config/flog.yml +4 -0
  14. data/config/mutant.yml +3 -0
  15. data/config/reek.yml +104 -0
  16. data/config/rubocop.yml +49 -0
  17. data/config/yardstick.yml +2 -0
  18. data/lib/megingiard.rb +6 -0
  19. data/lib/megingiard/ansi_colors.rb +21 -0
  20. data/lib/megingiard/ansi_escape_code_validator.rb +20 -0
  21. data/lib/megingiard/ansi_escape_sequence.rb +16 -0
  22. data/lib/megingiard/bold_node.rb +16 -0
  23. data/lib/megingiard/bold_sequence.rb +7 -0
  24. data/lib/megingiard/centered_canvas.rb +45 -0
  25. data/lib/megingiard/centered_node.rb +13 -0
  26. data/lib/megingiard/color_node.rb +17 -0
  27. data/lib/megingiard/emoji_node.rb +16 -0
  28. data/lib/megingiard/emojis.rb +12 -0
  29. data/lib/megingiard/hash_wrapper.rb +16 -0
  30. data/lib/megingiard/node.rb +13 -0
  31. data/lib/megingiard/reset_sequence.rb +7 -0
  32. data/lib/megingiard/terminal_width.rb +11 -0
  33. data/lib/megingiard/text_color_sequence.rb +17 -0
  34. data/lib/megingiard/version.rb +4 -0
  35. data/megingiard.gemspec +23 -0
  36. data/spec/integration/nodes_spec.rb +23 -0
  37. data/spec/spec_helper.rb +2 -0
  38. data/spec/unit/ansi_escape_code_validator/validate_spec.rb +20 -0
  39. data/spec/unit/ansi_escape_sequence/initialize_spec.rb +18 -0
  40. data/spec/unit/ansi_escape_sequence/to_s_spec.rb +27 -0
  41. data/spec/unit/bold_sequence/to_s_spec.rb +24 -0
  42. data/spec/unit/centered_canvas/draw_centered_row_spec.rb +50 -0
  43. data/spec/unit/centered_canvas/draw_left_column_spec.rb +40 -0
  44. data/spec/unit/centered_canvas/draw_right_column_spec.rb +54 -0
  45. data/spec/unit/centered_canvas/initialize_spec.rb +11 -0
  46. data/spec/unit/color_node/to_s_spec.rb +29 -0
  47. data/spec/unit/emoji_node/to_s_spec.rb +20 -0
  48. data/spec/unit/hash_wrapper/fetch_spec.rb +34 -0
  49. data/spec/unit/node/to_s_spec.rb +29 -0
  50. data/spec/unit/text_color_sequence/initialize_spec.rb +24 -0
  51. data/spec/unit/text_color_sequence/to_s_spec.rb +26 -0
  52. metadata +139 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ae5d046438bb7e1363e26703dd5b52d3eb6e7005
4
+ data.tar.gz: edcb50b4fe7753b5a56063abb983704bdd96da48
5
+ SHA512:
6
+ metadata.gz: 13070715b9ebb020440d20a2610de16a981fc04d0bc1525cb53399029b79855273a525d0483dc2bb1ae86fdcf8beda89008740cc9f46f94ecd3d778e193b63f3
7
+ data.tar.gz: 37c6bc40b8eb538c9b07aa74d2fd604b495407482c264e9d8e7f975da6733516888e90712de68f4dd789dfba88c52984433d072894edd449f21f5ce02ba84d52
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ inspiration
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0.0-p451
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ # -*- encoding : utf-8 -*-
2
+ source 'https://rubygems.org'
3
+
4
+ # Specify your gem's dependencies in megingiard.gemspec
5
+ gemspec
6
+
7
+ group :development, :test do
8
+ gem 'devtools', git: 'https://github.com/rom-rb/devtools.git'
9
+ end
10
+
11
+ eval_gemfile 'Gemfile.devtools'
data/Gemfile.devtools ADDED
@@ -0,0 +1,71 @@
1
+ # encoding: utf-8
2
+
3
+ group :development do
4
+ gem 'rake', '~> 10.2.1'
5
+ gem 'rspec', '~> 2.14.1'
6
+ gem 'rspec-core', '~> 2.14.8'
7
+ gem 'yard', '~> 0.8.7.4'
8
+
9
+ platform :rbx do
10
+ gem 'rubysl-singleton', '~> 2.0.0'
11
+ end
12
+ end
13
+
14
+ group :yard do
15
+ gem 'kramdown', '~> 1.3.3'
16
+ end
17
+
18
+ group :guard do
19
+ gem 'guard', '~> 2.6.0'
20
+ gem 'guard-bundler', '~> 2.0.0'
21
+ gem 'guard-rspec', '~> 4.2.8'
22
+ gem 'guard-rubocop', '~> 1.0.2'
23
+
24
+ # file system change event handling
25
+ gem 'listen', '~> 2.7.1'
26
+ gem 'rb-fchange', '~> 0.0.6', require: false
27
+ gem 'rb-fsevent', '~> 0.9.4', require: false
28
+ gem 'rb-inotify', '~> 0.9.3', require: false
29
+
30
+ # notification handling
31
+ gem 'libnotify', '~> 0.8.2', require: false
32
+ gem 'rb-notifu', '~> 0.0.4', require: false
33
+ gem 'terminal-notifier-guard', '~> 1.5.3', require: false
34
+ end
35
+
36
+ group :metrics do
37
+ gem 'coveralls', '~> 0.7.0'
38
+ gem 'flay', '~> 2.4.0'
39
+ gem 'flog', '~> 4.2.0'
40
+ gem 'reek', '~> 1.3.7'
41
+ gem 'rubocop', '~> 0.19.1'
42
+ gem 'simplecov', '~> 0.8.2'
43
+ gem 'yardstick', '~> 0.9.9'
44
+
45
+ platforms :mri do
46
+ gem 'mutant', '~> 0.5.9'
47
+ gem 'mutant-rspec', '~> 0.5.3'
48
+ end
49
+
50
+ platforms :ruby_19, :ruby_20 do
51
+ gem 'yard-spellcheck', '~> 0.1.5'
52
+ end
53
+
54
+ platform :rbx do
55
+ gem 'json', '~> 1.8.1'
56
+ gem 'racc', '~> 1.4.11'
57
+ gem 'rubysl-logger', '~> 2.0.0'
58
+ gem 'rubysl-open-uri', '~> 2.0.0'
59
+ gem 'rubysl-prettyprint', '~> 2.0.3'
60
+ end
61
+ end
62
+
63
+ group :benchmarks do
64
+ gem 'rbench', '~> 0.2.3'
65
+ end
66
+
67
+ platform :jruby do
68
+ group :jruby do
69
+ gem 'jruby-openssl', '~> 0.8.5'
70
+ end
71
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Lucas Dohmen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # Megingiard
2
+
3
+ Tools for creating beautiful CLI output.
4
+
5
+ > Megingjörð is a belt worn by the god [Thor](https://en.wikipedia.org/wiki/Thor). According to the [Prose Edda](https://en.wikipedia.org/wiki/Prose_Edda), the belt is one of Thor's three main possessions, along with the hammer Mjölnir and the iron gloves Járngreipr. When worn, the belt is described as doubling Thor's already prodigious strength.
6
+ – Wikipedia
7
+
8
+ This is a work-in-progress library to create nice reporting output on the terminal. It will be used in exogenesis, but with later versions will hopefully be useful to other libraries as well. It is written with the help of [devtools](https://github.com/rom-rb/devtools) – this is mainly an experiment on which metrics I like for everyday development.
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ gem 'megingiard'
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install megingiard
23
+
24
+ ## Contributing
25
+
26
+ 1. Fork it
27
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
28
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
29
+ 4. Push to the branch (`git push origin my-new-feature`)
30
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'devtools'
3
+
4
+ Devtools.init_rake_tasks
5
+
6
+ task :default => "spec:unit"
@@ -0,0 +1,4 @@
1
+ ---
2
+ unit_test_timeout: 0.1
3
+ fail_on_branch:
4
+ - "master"
data/config/flay.yml ADDED
@@ -0,0 +1,3 @@
1
+ ---
2
+ threshold: 6
3
+ total_score: 48
data/config/flog.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ threshold: 5.0
3
+ enabled_platforms:
4
+ - 'mri-2.0.0'
data/config/mutant.yml ADDED
@@ -0,0 +1,3 @@
1
+ ---
2
+ name: megingiard
3
+ namespace: Megingiard
data/config/reek.yml ADDED
@@ -0,0 +1,104 @@
1
+ ---
2
+ Attribute:
3
+ enabled: true
4
+ exclude: []
5
+ BooleanParameter:
6
+ enabled: true
7
+ exclude: []
8
+ ClassVariable:
9
+ enabled: true
10
+ exclude: []
11
+ ControlParameter:
12
+ enabled: true
13
+ exclude:
14
+ - Megingiard::AnsiEscapeCodeValidator#validate
15
+ DataClump:
16
+ enabled: true
17
+ exclude: []
18
+ max_copies: 2
19
+ min_clump_size: 2
20
+ DuplicateMethodCall:
21
+ enabled: true
22
+ exclude: []
23
+ max_calls: 1
24
+ allow_calls: []
25
+ FeatureEnvy:
26
+ enabled: true
27
+ exclude: []
28
+ IrresponsibleModule:
29
+ enabled: true
30
+ exclude: []
31
+ LongParameterList:
32
+ enabled: true
33
+ exclude: []
34
+ max_params: 2
35
+ overrides:
36
+ initialize:
37
+ max_params: 3
38
+ LongYieldList:
39
+ enabled: true
40
+ exclude: []
41
+ max_params: 2
42
+ NestedIterators:
43
+ enabled: true
44
+ exclude: []
45
+ max_allowed_nesting: 1
46
+ ignore_iterators: []
47
+ NilCheck:
48
+ enabled: true
49
+ exclude: []
50
+ RepeatedConditional:
51
+ enabled: true
52
+ exclude: []
53
+ max_ifs: 1
54
+ TooManyInstanceVariables:
55
+ enabled: true
56
+ exclude: []
57
+ max_instance_variables: 3
58
+ TooManyMethods:
59
+ enabled: true
60
+ exclude: []
61
+ max_methods: 10
62
+ TooManyStatements:
63
+ enabled: true
64
+ exclude:
65
+ - each
66
+ max_statements: 3
67
+ UncommunicativeMethodName:
68
+ enabled: true
69
+ exclude: []
70
+ reject:
71
+ - !ruby/regexp /^[a-z]$/
72
+ - !ruby/regexp /[0-9]$/
73
+ - !ruby/regexp /[A-Z]/
74
+ accept: []
75
+ UncommunicativeModuleName:
76
+ enabled: true
77
+ exclude: []
78
+ reject:
79
+ - !ruby/regexp /^.$/
80
+ - !ruby/regexp /[0-9]$/
81
+ accept: []
82
+ UncommunicativeParameterName:
83
+ enabled: true
84
+ exclude: []
85
+ reject:
86
+ - !ruby/regexp /^.$/
87
+ - !ruby/regexp /[0-9]$/
88
+ - !ruby/regexp /[A-Z]/
89
+ accept: []
90
+ UncommunicativeVariableName:
91
+ enabled: true
92
+ exclude: []
93
+ reject:
94
+ - !ruby/regexp /^.$/
95
+ - !ruby/regexp /[0-9]$/
96
+ - !ruby/regexp /[A-Z]/
97
+ accept: []
98
+ UnusedParameters:
99
+ enabled: true
100
+ exclude: []
101
+ UtilityFunction:
102
+ enabled: true
103
+ exclude: []
104
+ max_helper_calls: 0
@@ -0,0 +1,49 @@
1
+ AllCops:
2
+ Includes:
3
+ - '**/*.rake'
4
+ - 'Gemfile'
5
+ - 'Gemfile.devtools'
6
+ Excludes:
7
+ - '**/vendor/**'
8
+ - '**/benchmarks/**'
9
+
10
+ # Avoid parameter lists longer than five parameters.
11
+ ParameterLists:
12
+ Max: 3
13
+ CountKeywordArgs: true
14
+
15
+ # Avoid more than `Max` levels of nesting.
16
+ BlockNesting:
17
+ Max: 3
18
+
19
+ # Align with the style guide.
20
+ CollectionMethods:
21
+ PreferredMethods:
22
+ collect: 'map'
23
+ inject: 'reduce'
24
+ find: 'detect'
25
+ find_all: 'select'
26
+
27
+ # Limit line length
28
+ LineLength:
29
+ Max: 100
30
+
31
+ # Disable documentation checking until a class needs to be documented once
32
+ Documentation:
33
+ Enabled: false
34
+
35
+ # Do not favor modifier if/unless usage when you have a single-line body
36
+ IfUnlessModifier:
37
+ Enabled: false
38
+
39
+ # Allow case equality operator (in limited use within the specs)
40
+ CaseEquality:
41
+ Enabled: false
42
+
43
+ # Constants do not always have to use SCREAMING_SNAKE_CASE
44
+ ConstantName:
45
+ Enabled: false
46
+
47
+ # Not all trivial readers/writers can be defined with attr_* methods
48
+ TrivialAccessors:
49
+ Enabled: false
@@ -0,0 +1,2 @@
1
+ ---
2
+ threshold: 100
data/lib/megingiard.rb ADDED
@@ -0,0 +1,6 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/version'
3
+
4
+ module Megingiard
5
+ # Your code goes here...
6
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/hash_wrapper'
3
+
4
+ module Megingiard
5
+ # The given ANSI escape sequence was not valid
6
+ class InvalidAnsiColorCode < ArgumentError
7
+ end
8
+
9
+ ANSI_COLOR_MAP = {
10
+ red: 1,
11
+ green: 2,
12
+ yellow: 3,
13
+ blue: 4,
14
+ magenta: 5,
15
+ cyan: 6,
16
+ white: 7
17
+ }
18
+
19
+ # Color codes for ANSI color names
20
+ ANSI_COLORS = HashWrapper.new(ANSI_COLOR_MAP, InvalidAnsiColorCode)
21
+ end
@@ -0,0 +1,20 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'singleton'
3
+
4
+ module Megingiard
5
+ # The given ANSI escape sequence was not valid
6
+ class InvalidAnsiEscapeCode < ArgumentError
7
+ end
8
+
9
+ # Validator for ANSI escape sequence
10
+ class AnsiEscapeCodeValidator
11
+ include Singleton
12
+
13
+ VALID_ESCAPE_CODES = 0..55
14
+
15
+ # Validate an ANSI escape sequence
16
+ def validate(code)
17
+ fail InvalidAnsiEscapeCode unless VALID_ESCAPE_CODES.include?(code)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/ansi_escape_code_validator'
3
+
4
+ module Megingiard
5
+ # As the name suggests
6
+ class AnsiEscapeSequence
7
+ def initialize(code)
8
+ AnsiEscapeCodeValidator.instance.validate(code)
9
+ @code = code
10
+ end
11
+
12
+ def to_s
13
+ "\e[#{@code}m"
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/bold_sequence'
3
+ require 'megingiard/reset_sequence'
4
+
5
+ module Megingiard
6
+ # A node that makes all its children bold
7
+ class BoldNode
8
+ def initialize(child)
9
+ @child = child
10
+ end
11
+
12
+ def to_s
13
+ "#{BOLD_SEQUENCE}#{@child}#{RESET_SEQUENCE}"
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/ansi_escape_sequence'
3
+
4
+ module Megingiard
5
+ # Sequence for making text bold
6
+ BOLD_SEQUENCE = AnsiEscapeSequence.new(1)
7
+ end
@@ -0,0 +1,45 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/terminal_width'
3
+ require 'megingiard/node'
4
+ require 'megingiard/centered_node'
5
+
6
+ module Megingiard
7
+ # An area that can be drawn on, everything is centered
8
+ class CenteredCanvas
9
+ def initialize(output)
10
+ @output = output
11
+ @left_column_drawn = false
12
+ end
13
+
14
+ # Check if a left column has been drawn
15
+ def left_column_drawn?
16
+ @left_column_drawn
17
+ end
18
+
19
+ # The left half of a full-width row with right aligned content
20
+ def draw_left_column(node)
21
+ right_adjusted_text = node.to_s.rjust(CELL_WIDTH)
22
+ @output.print right_adjusted_text
23
+ @left_column_drawn = true
24
+ end
25
+
26
+ # The right half of a full-width row
27
+ def draw_right_column(node)
28
+ node = Node.new(EMPTY_CELL, node) unless left_column_drawn?
29
+ end_line_with(node)
30
+ end
31
+
32
+ # A full-width row where the content is centered
33
+ def draw_centered_row(node)
34
+ node = CenteredNode.new(TERMINAL_WIDTH, node)
35
+ end_line_with(node)
36
+ end
37
+
38
+ private
39
+
40
+ def end_line_with(element)
41
+ @output.puts(element.to_s)
42
+ @left_column_drawn = false
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,13 @@
1
+ module Megingiard
2
+ # A node with centered content
3
+ class CenteredNode
4
+ def initialize(width, text)
5
+ @width = width
6
+ @text = text
7
+ end
8
+
9
+ def to_s
10
+ @text.to_s.center(@width)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/text_color_sequence'
3
+ require 'megingiard/reset_sequence'
4
+
5
+ module Megingiard
6
+ # A node that colors all its children in a given color
7
+ class ColorNode
8
+ def initialize(color_name, child)
9
+ @color_sequence = TextColorSequence.new(color_name)
10
+ @child = child
11
+ end
12
+
13
+ def to_s
14
+ "#{@color_sequence}#{@child}#{RESET_SEQUENCE}"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/emojis'
3
+ require 'megingiard/reset_sequence'
4
+
5
+ module Megingiard
6
+ # A leaf node representing an emoji
7
+ class EmojiNode
8
+ def initialize(emoji_name)
9
+ @emoji = EMOJIS.fetch(emoji_name)
10
+ end
11
+
12
+ def to_s
13
+ @emoji
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,12 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'utf8_emoji'
3
+ require 'megingiard/hash_wrapper'
4
+
5
+ module Megingiard
6
+ # There is no Emoji known with this name
7
+ class InvalidEmojiName < ArgumentError
8
+ end
9
+
10
+ # Name to Emoji Wrapper
11
+ EMOJIS = HashWrapper.new(Utf8Emoji.emojis, InvalidEmojiName)
12
+ end
@@ -0,0 +1,16 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Megingiard
3
+ # Only reveal fetch and translates the Error Message
4
+ class HashWrapper
5
+ def initialize(hash, error_class)
6
+ @hash = hash
7
+ @error_class = error_class
8
+ end
9
+
10
+ def fetch(name)
11
+ @hash.fetch(name)
12
+ rescue
13
+ raise @error_class
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Megingiard
3
+ # A node with arbitrary many children
4
+ class Node
5
+ def initialize(*children)
6
+ @children = children
7
+ end
8
+
9
+ def to_s
10
+ @children.map(&:to_s).reduce(:+)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/ansi_escape_sequence'
3
+
4
+ module Megingiard
5
+ # Sequence for a Reset
6
+ RESET_SEQUENCE = AnsiEscapeSequence.new(0)
7
+ end
@@ -0,0 +1,11 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Megingiard
3
+ # The width of the terminal
4
+ TERMINAL_WIDTH = Integer(`tput cols`)
5
+
6
+ # A Cell has half of the width of the terminal
7
+ CELL_WIDTH = TERMINAL_WIDTH / 2
8
+
9
+ # A Cell filled with whitespace
10
+ EMPTY_CELL = ' ' * CELL_WIDTH
11
+ end
@@ -0,0 +1,17 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/ansi_colors'
3
+ require 'megingiard/ansi_escape_sequence'
4
+
5
+ module Megingiard
6
+ # ANSI Escape Sequence for a Text Color
7
+ class TextColorSequence
8
+ def initialize(name)
9
+ color_code = ANSI_COLORS.fetch(name)
10
+ @escape_sequence = AnsiEscapeSequence.new(color_code + 30)
11
+ end
12
+
13
+ def to_s
14
+ @escape_sequence.to_s
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Megingiard
3
+ VERSION = '0.0.1'
4
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'megingiard/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "megingiard"
8
+ spec.version = Megingiard::VERSION
9
+ spec.authors = ["moonglum"]
10
+ spec.email = ["moonglum@moonbeamlabs.com"]
11
+ spec.description = %q{Tools for creating beautiful CLI output}
12
+ spec.summary = %q{This gem provides beautiful output for your CLI}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "utf8_emoji", "~> 0.1.0"
22
+ spec.add_development_dependency "bundler", "~> 1.6.0"
23
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/node'
3
+ require 'megingiard/color_node'
4
+ require 'megingiard/bold_node'
5
+ require 'megingiard/emoji_node'
6
+
7
+ describe 'Nodes' do
8
+ it 'should combine color, bold and normal nodes' do
9
+ red_node = Megingiard::ColorNode.new(:red, 'rot')
10
+ text_node = Megingiard::Node.new('Dieser Text ist ', red_node)
11
+ bold_node = Megingiard::BoldNode.new(text_node)
12
+
13
+ expect(bold_node.to_s).to eq "\e[1mDieser Text ist \e[31mrot\e[0m\e[0m"
14
+ end
15
+
16
+ it 'should add an Emoji to colored text' do
17
+ emoji_node = Megingiard::EmojiNode.new(:rocket)
18
+ node = Megingiard::Node.new('This is ', emoji_node, ' science')
19
+ green_node = Megingiard::ColorNode.new(:green, node)
20
+
21
+ expect(green_node.to_s).to eq("\e[32mThis is 🚀 science\e[0m")
22
+ end
23
+ end
@@ -0,0 +1,2 @@
1
+ # -*- encoding : utf-8 -*-
2
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
@@ -0,0 +1,20 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/ansi_escape_code_validator'
3
+
4
+ describe Megingiard::AnsiEscapeCodeValidator do
5
+ subject { Megingiard::AnsiEscapeCodeValidator.instance }
6
+
7
+ describe 'validate' do
8
+ it 'should throw an exception if the sequence is too high' do
9
+ expect do
10
+ subject.validate(56)
11
+ end.to raise_error(Megingiard::InvalidAnsiEscapeCode)
12
+ end
13
+
14
+ it 'should not throw an exception if the code is ok' do
15
+ expect do
16
+ subject.validate(0)
17
+ end.not_to raise_error
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/ansi_escape_sequence'
3
+
4
+ describe Megingiard::AnsiEscapeSequence do
5
+ subject { Megingiard::AnsiEscapeSequence }
6
+ let(:validator) { double }
7
+ let(:code) { double }
8
+
9
+ describe 'initialize' do
10
+ it 'should validate the escape sequence' do
11
+ allow(Megingiard::AnsiEscapeCodeValidator).to receive(:instance)
12
+ .and_return(validator)
13
+ expect(validator).to receive(:validate).with(code)
14
+
15
+ subject.new(code)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/ansi_escape_sequence'
3
+
4
+ describe Megingiard::AnsiEscapeSequence do
5
+ let(:code) { double('Integer', to_s: code_as_string) }
6
+ let(:code_as_string) { '1' }
7
+ let(:validator) { double }
8
+
9
+ subject { Megingiard::AnsiEscapeSequence.new(code) }
10
+
11
+ before do
12
+ allow(Megingiard::AnsiEscapeCodeValidator).to receive(:instance)
13
+ .and_return(validator)
14
+ allow(validator).to receive(:validate)
15
+ end
16
+
17
+ describe 'to_s' do
18
+ it 'should convert the code into a string' do
19
+ expect(code).to receive(:to_s)
20
+ subject.to_s
21
+ end
22
+
23
+ it 'should return the code as an escape sequence' do
24
+ expect(subject.to_s).to eq "\e[#{code_as_string}m"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/bold_node'
3
+
4
+ describe Megingiard::BoldNode do
5
+ subject { Megingiard::BoldNode.new(child) }
6
+ let(:child) { double(String, to_s: child_string) }
7
+ let(:child_string) { double }
8
+ let(:reset_sequence_string) { double }
9
+ let(:bold_sequence_string) { double }
10
+ let(:expected_sequence) { "#{Megingiard::BOLD_SEQUENCE}#{child}#{Megingiard::RESET_SEQUENCE}" }
11
+
12
+ before do
13
+ allow(Megingiard::RESET_SEQUENCE).to receive(:to_s)
14
+ .and_return(reset_sequence_string)
15
+ allow(Megingiard::BOLD_SEQUENCE).to receive(:to_s)
16
+ .and_return(bold_sequence_string)
17
+ end
18
+
19
+ describe 'to_s' do
20
+ it 'should wrap the child in a bold and a reset sequence' do
21
+ expect(subject.to_s).to eq expected_sequence
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,50 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/centered_canvas'
3
+
4
+ describe Megingiard::CenteredCanvas do
5
+ subject { Megingiard::CenteredCanvas.new(output) }
6
+ let(:node) { double }
7
+ let(:node_as_string) { double }
8
+ let(:centered_text) { double }
9
+ let(:output) { double }
10
+ let(:terminal_width) { double }
11
+
12
+ before do
13
+ stub_const('Megingiard::TERMINAL_WIDTH', terminal_width)
14
+ allow(node).to receive(:to_s)
15
+ .and_return(node_as_string)
16
+ allow(node_as_string).to receive(:center)
17
+ .and_return(centered_text)
18
+ allow(centered_text).to receive(:to_s)
19
+ .and_return(centered_text)
20
+ allow(output).to receive(:puts)
21
+ end
22
+
23
+ describe 'draw_centered_row' do
24
+ context 'left column was not drawn' do
25
+ before { subject.instance_variable_set('@left_column_drawn', false) }
26
+
27
+ it 'should to_s and center the text' do
28
+ expect(node_as_string).to receive(:center)
29
+ .with(terminal_width)
30
+ subject.draw_centered_row(node)
31
+ end
32
+ end
33
+
34
+ context 'left column was drawn' do
35
+ before { subject.instance_variable_set('@left_column_drawn', true) }
36
+
37
+ it 'should know that it has not drawn a left column' do
38
+ expect do
39
+ subject.draw_centered_row(node)
40
+ end.to change { subject.left_column_drawn? }.to(false)
41
+ end
42
+
43
+ it 'should put the resulting text to the output' do
44
+ expect(output).to receive(:puts)
45
+ .with(centered_text)
46
+ subject.draw_centered_row(node)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,40 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/centered_canvas'
3
+
4
+ describe Megingiard::CenteredCanvas do
5
+ subject { Megingiard::CenteredCanvas.new(output) }
6
+ let(:node) { double }
7
+ let(:node_as_string) { double }
8
+ let(:right_adjusted_text) { double }
9
+ let(:output) { double }
10
+ let(:cell_width) { double }
11
+
12
+ before do
13
+ stub_const('Megingiard::CELL_WIDTH', cell_width)
14
+ allow(node).to receive(:to_s)
15
+ .and_return(node_as_string)
16
+ allow(node_as_string).to receive(:rjust)
17
+ .and_return(right_adjusted_text)
18
+ allow(output).to receive(:print)
19
+ end
20
+
21
+ describe 'draw_left_column' do
22
+ it 'should to_s and right adjust the text' do
23
+ expect(node_as_string).to receive(:rjust)
24
+ .with(cell_width)
25
+ subject.draw_left_column(node)
26
+ end
27
+
28
+ it 'should print the resulting text to the output' do
29
+ expect(output).to receive(:print)
30
+ .with(right_adjusted_text)
31
+ subject.draw_left_column(node)
32
+ end
33
+
34
+ it 'should know that it has drawn a left column' do
35
+ expect do
36
+ subject.draw_left_column(node)
37
+ end.to change { subject.left_column_drawn? }.to(true)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,54 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/centered_canvas'
3
+
4
+ describe Megingiard::CenteredCanvas do
5
+ subject { Megingiard::CenteredCanvas.new(output) }
6
+ let(:node) { double }
7
+ let(:node_as_string) { double }
8
+ let(:centered_text) { double }
9
+ let(:output) { double }
10
+ let(:terminal_width) { double }
11
+ let(:wrapped_node) { double }
12
+ let(:wrapped_node_as_string) { double }
13
+
14
+ before do
15
+ stub_const('Megingiard::TERMINAL_WIDTH', terminal_width)
16
+ allow(node).to receive(:to_s)
17
+ .and_return(node_as_string)
18
+ allow(output).to receive(:puts)
19
+ allow(wrapped_node).to receive(:to_s)
20
+ .and_return(wrapped_node_as_string)
21
+ end
22
+
23
+ describe 'draw_centered_row' do
24
+ context 'left column drawn' do
25
+ before { subject.instance_variable_set('@left_column_drawn', true) }
26
+
27
+ it 'should put the resulting text to the output' do
28
+ expect(output).to receive(:puts)
29
+ .with(node_as_string)
30
+ subject.draw_right_column(node)
31
+ end
32
+
33
+ it 'should know that it has not drawn a left column' do
34
+ expect do
35
+ subject.draw_right_column(node)
36
+ end.to change { subject.left_column_drawn? }.to(false)
37
+ end
38
+ end
39
+
40
+ context 'left column not drawn' do
41
+ before { subject.instance_variable_set('@left_column_drawn', false) }
42
+
43
+ it 'should prepend an empty cell to the output' do
44
+ allow(Megingiard::Node).to receive(:new)
45
+ .with(Megingiard::EMPTY_CELL, node)
46
+ .and_return(wrapped_node)
47
+
48
+ expect(output).to receive(:puts)
49
+ .with(wrapped_node_as_string)
50
+ subject.draw_right_column(node)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,11 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/centered_canvas'
3
+
4
+ describe Megingiard::CenteredCanvas do
5
+ subject { Megingiard::CenteredCanvas.new(output) }
6
+ let(:output) { double }
7
+
8
+ describe 'initialize' do
9
+ its(:left_column_drawn?) { should be false }
10
+ end
11
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/color_node'
3
+
4
+ describe Megingiard::ColorNode do
5
+ subject { Megingiard::ColorNode.new(color_name, child) }
6
+ let(:color_name) { double }
7
+ let(:child) { double(String, to_s: child_string) }
8
+ let(:child_string) { double }
9
+ let(:text_color_sequence) do
10
+ double(Megingiard::TextColorSequence, to_s: text_color_sequence_string)
11
+ end
12
+ let(:reset_sequence_string) { double }
13
+ let(:text_color_sequence_string) { double }
14
+
15
+ before do
16
+ allow(Megingiard::TextColorSequence).to receive(:new)
17
+ .with(color_name)
18
+ .and_return(text_color_sequence)
19
+ allow(Megingiard::RESET_SEQUENCE).to receive(:to_s)
20
+ .and_return(reset_sequence_string)
21
+ end
22
+
23
+ describe 'to_s' do
24
+ it 'should be awesome' do
25
+ sequence = "#{text_color_sequence}#{child}#{Megingiard::RESET_SEQUENCE}"
26
+ expect(subject.to_s).to eq sequence
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,20 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/emoji_node'
3
+
4
+ describe Megingiard::EmojiNode do
5
+ subject { Megingiard::EmojiNode.new(emoji_name) }
6
+ let(:emoji_name) { double }
7
+ let(:emoji) { double }
8
+
9
+ before do
10
+ allow(Megingiard::EMOJIS).to receive(:fetch)
11
+ .with(emoji_name)
12
+ .and_return(emoji)
13
+ end
14
+
15
+ describe 'to_s' do
16
+ it 'should be the Emoji from the according Emoji collection' do
17
+ expect(subject.to_s).to eq(emoji)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,34 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/hash_wrapper'
3
+
4
+ describe Megingiard::HashWrapper do
5
+ let(:hash) { double }
6
+ let(:item) { double }
7
+ let(:item_name) { double }
8
+ let(:error) { ArgumentError }
9
+ let(:other_error) { double }
10
+ subject { Megingiard::HashWrapper.new(hash, error) }
11
+
12
+ describe 'fetch' do
13
+ it 'should get the according item via fetch' do
14
+ expect(hash).to receive(:fetch)
15
+ .with(item)
16
+ subject.fetch(item)
17
+ end
18
+
19
+ it 'should return the fetched item' do
20
+ allow(hash).to receive(:fetch)
21
+ .and_return(item_name)
22
+ expect(subject.fetch(item)).to be item_name
23
+ end
24
+
25
+ it 'should raise an error for an unknown item' do
26
+ allow(hash).to receive(:fetch)
27
+ .and_raise(KeyError)
28
+
29
+ expect do
30
+ subject.fetch(item)
31
+ end.to raise_error(error)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/node'
3
+
4
+ describe Megingiard::Node do
5
+ subject { Megingiard::Node.new(first_child, second_child) }
6
+
7
+ let(:first_child) { double(String, to_s: first_child_string) }
8
+ let(:second_child) { double(String, to_s: second_child_string) }
9
+ let(:first_child_string) { double }
10
+ let(:second_child_string) { double }
11
+ let(:concatenation) { double }
12
+
13
+ describe 'to_s' do
14
+
15
+ it 'should concatenate the children as strings' do
16
+ expect(first_child_string).to receive(:+)
17
+ .with(second_child_string)
18
+
19
+ subject.to_s
20
+ end
21
+
22
+ it 'should return the result' do
23
+ allow(first_child_string).to receive(:+)
24
+ .and_return(concatenation)
25
+
26
+ expect(subject.to_s).to eq concatenation
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/text_color_sequence'
3
+
4
+ describe Megingiard::TextColorSequence do
5
+ subject { Megingiard::TextColorSequence }
6
+ let(:color_code) { double }
7
+ let(:name) { double }
8
+ let(:ansi_code) { double }
9
+
10
+ describe 'initialize' do
11
+ it 'should create the ANSI escape sequence with the according code' do
12
+ expect(Megingiard::ANSI_COLORS).to receive(:fetch)
13
+ .with(name)
14
+ .and_return(color_code)
15
+ expect(color_code).to receive(:+)
16
+ .with(30)
17
+ .and_return(ansi_code)
18
+ expect(Megingiard::AnsiEscapeSequence).to receive(:new)
19
+ .with(ansi_code)
20
+
21
+ subject.new(name)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'megingiard/text_color_sequence'
3
+
4
+ describe Megingiard::TextColorSequence do
5
+ subject { Megingiard::TextColorSequence.new(name) }
6
+ let(:color_code) { double(Integer, :+ => ansi_code) }
7
+ let(:name) { double }
8
+ let(:ansi_code) { double }
9
+ let(:ansi_escape_sequence) { double }
10
+ let(:ansi_escape_sequence_as_string) { double }
11
+
12
+ before do
13
+ allow(Megingiard::ANSI_COLORS).to receive(:fetch)
14
+ .and_return(color_code)
15
+ allow(Megingiard::AnsiEscapeSequence).to receive(:new)
16
+ .and_return(ansi_escape_sequence)
17
+ end
18
+
19
+ describe 'to_s' do
20
+ it 'should return the String representation of the escape sequence' do
21
+ allow(ansi_escape_sequence).to receive(:to_s)
22
+ .and_return(ansi_escape_sequence_as_string)
23
+ expect(subject.to_s).to eq(ansi_escape_sequence_as_string)
24
+ end
25
+ end
26
+ end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: megingiard
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - moonglum
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: utf8_emoji
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 0.1.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 1.6.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 1.6.0
41
+ description: Tools for creating beautiful CLI output
42
+ email:
43
+ - moonglum@moonbeamlabs.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - .gitignore
49
+ - .rspec
50
+ - .ruby-version
51
+ - .travis.yml
52
+ - Gemfile
53
+ - Gemfile.devtools
54
+ - LICENSE.txt
55
+ - README.md
56
+ - Rakefile
57
+ - config/devtools.yml
58
+ - config/flay.yml
59
+ - config/flog.yml
60
+ - config/mutant.yml
61
+ - config/reek.yml
62
+ - config/rubocop.yml
63
+ - config/yardstick.yml
64
+ - lib/megingiard.rb
65
+ - lib/megingiard/ansi_colors.rb
66
+ - lib/megingiard/ansi_escape_code_validator.rb
67
+ - lib/megingiard/ansi_escape_sequence.rb
68
+ - lib/megingiard/bold_node.rb
69
+ - lib/megingiard/bold_sequence.rb
70
+ - lib/megingiard/centered_canvas.rb
71
+ - lib/megingiard/centered_node.rb
72
+ - lib/megingiard/color_node.rb
73
+ - lib/megingiard/emoji_node.rb
74
+ - lib/megingiard/emojis.rb
75
+ - lib/megingiard/hash_wrapper.rb
76
+ - lib/megingiard/node.rb
77
+ - lib/megingiard/reset_sequence.rb
78
+ - lib/megingiard/terminal_width.rb
79
+ - lib/megingiard/text_color_sequence.rb
80
+ - lib/megingiard/version.rb
81
+ - megingiard.gemspec
82
+ - spec/integration/nodes_spec.rb
83
+ - spec/spec_helper.rb
84
+ - spec/unit/ansi_escape_code_validator/validate_spec.rb
85
+ - spec/unit/ansi_escape_sequence/initialize_spec.rb
86
+ - spec/unit/ansi_escape_sequence/to_s_spec.rb
87
+ - spec/unit/bold_sequence/to_s_spec.rb
88
+ - spec/unit/centered_canvas/draw_centered_row_spec.rb
89
+ - spec/unit/centered_canvas/draw_left_column_spec.rb
90
+ - spec/unit/centered_canvas/draw_right_column_spec.rb
91
+ - spec/unit/centered_canvas/initialize_spec.rb
92
+ - spec/unit/color_node/to_s_spec.rb
93
+ - spec/unit/emoji_node/to_s_spec.rb
94
+ - spec/unit/hash_wrapper/fetch_spec.rb
95
+ - spec/unit/node/to_s_spec.rb
96
+ - spec/unit/text_color_sequence/initialize_spec.rb
97
+ - spec/unit/text_color_sequence/to_s_spec.rb
98
+ homepage: ''
99
+ licenses:
100
+ - MIT
101
+ metadata: {}
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubyforge_project:
118
+ rubygems_version: 2.2.2
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: This gem provides beautiful output for your CLI
122
+ test_files:
123
+ - spec/integration/nodes_spec.rb
124
+ - spec/spec_helper.rb
125
+ - spec/unit/ansi_escape_code_validator/validate_spec.rb
126
+ - spec/unit/ansi_escape_sequence/initialize_spec.rb
127
+ - spec/unit/ansi_escape_sequence/to_s_spec.rb
128
+ - spec/unit/bold_sequence/to_s_spec.rb
129
+ - spec/unit/centered_canvas/draw_centered_row_spec.rb
130
+ - spec/unit/centered_canvas/draw_left_column_spec.rb
131
+ - spec/unit/centered_canvas/draw_right_column_spec.rb
132
+ - spec/unit/centered_canvas/initialize_spec.rb
133
+ - spec/unit/color_node/to_s_spec.rb
134
+ - spec/unit/emoji_node/to_s_spec.rb
135
+ - spec/unit/hash_wrapper/fetch_spec.rb
136
+ - spec/unit/node/to_s_spec.rb
137
+ - spec/unit/text_color_sequence/initialize_spec.rb
138
+ - spec/unit/text_color_sequence/to_s_spec.rb
139
+ has_rdoc: