pastel 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: df0d822f2378510e2c8f751d179c208585fb2e38
4
+ data.tar.gz: bf0a8a93193af9c35ff862e0e69373d97bf29d51
5
+ SHA512:
6
+ metadata.gz: 7dcf28176858eeaccc03eb33d702214274e72d32082887d66938afa1f7d6f38b24fa7523437ed4249d51e6c7425db729e42ad50cd83daf0747ea953abb96157c
7
+ data.tar.gz: c6a17854cb0c7328474da74d4c8ffee8f0af6c757f729bbef91ec02da7202e91d4da108ef318be8dcf4ab51e1fa20a9985cd8e3ed35c4f5f50db3de7e888488d
data/.gitignore ADDED
@@ -0,0 +1,22 @@
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
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ pastel
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0.0
data/.travis.yml ADDED
@@ -0,0 +1,24 @@
1
+ language: ruby
2
+ bundler_args: --without yard benchmarks
3
+ script: "bundle exec rake ci"
4
+ rvm:
5
+ - 1.9.3
6
+ - 2.0.0
7
+ - 2.1.0
8
+ - ruby-head
9
+ matrix:
10
+ include:
11
+ - rvm: jruby-19mode
12
+ - rvm: jruby-20mode
13
+ - rvm: jruby-21mode
14
+ - rvm: jruby-head
15
+ - rvm: rbx-2
16
+ allow_failures:
17
+ - rvm: ruby-head
18
+ - rvm: jruby-head
19
+ - rvm: jruby-20mode
20
+ - rvm: jruby-21mode
21
+ - rvm: rbx-2
22
+ fast_finish: true
23
+ branches:
24
+ only: master
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'rake', '~> 10.3.2'
7
+ gem 'rspec', '~> 3.1.0'
8
+ gem 'yard', '~> 0.8.7'
9
+ end
10
+
11
+ group :metrics do
12
+ gem 'coveralls', '~> 0.7.0'
13
+ gem 'simplecov', '~> 0.8.2'
14
+ gem 'yardstick', '~> 0.9.9'
15
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Piotr Murach
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,191 @@
1
+ # Pastel
2
+ [![Gem Version](https://badge.fury.io/rb/pastel.png)][gem]
3
+ [![Build Status](https://secure.travis-ci.org/peter-murach/pastel.png?branch=master)][travis]
4
+ [![Code Climate](https://codeclimate.com/github/peter-murach/pastel.png)][codeclimate]
5
+
6
+ [gem]: http://badge.fury.io/rb/pastel
7
+ [travis]: http://travis-ci.org/peter-murach/pastel
8
+ [codeclimate]: https://codeclimate.com/github/peter-murach/pastel
9
+
10
+ Terminal output styling with intuitive and clean API that doesn't monkey patch String class.
11
+
12
+ **Pastel** is minimal and focused to work in all terminal emulators.
13
+
14
+ ## Features
15
+
16
+ * Doesn't monkey patch `String`
17
+ * Intuitive and expressive API
18
+ * Minimal and focused to work on all terminal emulators
19
+ * Auto-detection of color support
20
+ * Performant
21
+
22
+ ## Installation
23
+
24
+ Add this line to your application's Gemfile:
25
+
26
+ gem 'pastel'
27
+
28
+ And then execute:
29
+
30
+ $ bundle
31
+
32
+ Or install it yourself as:
33
+
34
+ $ gem install pastel
35
+
36
+ ## Contents
37
+
38
+ * [1. Usage](#1-usage)
39
+ * [2. Interface](#2-interface)
40
+ * [2.1 Color](#21-color)
41
+ * [2.2 Decorate](#22-decorate)
42
+ * [2.3 Strip](#23-strip)
43
+ * [2.4 Styles](#24-styles)
44
+ * [2.5 Valid?](#25-valid)
45
+ * [3. The available styles](#3-the-available-styles)
46
+
47
+ ## 1 Usage
48
+
49
+ **Pastel** provides a simple, minimal and intuitive API for styling your strings:
50
+
51
+ ```ruby
52
+ pastel = Pastel.new
53
+
54
+ pastel.red('Unicorns!')
55
+ ```
56
+
57
+ It allows you to combine styled strings with regular ones:
58
+
59
+ ```ruby
60
+ pastel.red('Unicorns') + ' will rule ' + pastel.green('the World!')
61
+ ```
62
+
63
+ You can compose multiple styles through chainable API:
64
+
65
+ ```ruby
66
+ pastel.red.on_green.bold('Unicorns!')
67
+ ```
68
+
69
+ It supports variable number of arguments with individual styling:
70
+
71
+ ```ruby
72
+ pastel.red('Unicorns', 'are', 'running', 'everywhere!')
73
+ ```
74
+
75
+ You can also nest styles as follows:
76
+
77
+ ```ruby
78
+ pastel.red('Unicorns ', pastel.on_green('everywhere!'))
79
+ ```
80
+
81
+ ## 2 Interface
82
+
83
+ ### 2.1 Color
84
+
85
+ You can pass variable number of styled strings like so:
86
+
87
+ ```ruby
88
+ pastel.red('Unicorns', pastel.on_yellow('are running', pastel.bold.underline('everywhere')), '!')
89
+ ```
90
+
91
+ Please refer to [3. The available styles](#3-the-available-styles) section for full list of supported styles.
92
+
93
+ ### 2.2 Decorate
94
+
95
+ This method is a lower level string styling call that takes as the first argument the string to style and any number of attributes, and returns string wrapped in styles.
96
+
97
+ ```ruby
98
+ pastel.decorate('Unicorn', :green, :on_blue, :bold)
99
+ ```
100
+
101
+ ### 2.3 Strip
102
+
103
+ Strip all color sequence characters:
104
+
105
+ ```ruby
106
+ pastel.strip("\e[1m\e[34mbold blue text\e[0m"") # => "bold blue text"
107
+ ```
108
+
109
+ ### 2.4 Styles
110
+
111
+ To get a full list of supported styles with the corresponding color codes do:
112
+
113
+ ```ruby
114
+ pastel.styles
115
+ ```
116
+
117
+ ### 2.5 Valid?
118
+
119
+ Determine whether a color is valid:
120
+
121
+ ```ruby
122
+ pastel.valid?(:red) # => true
123
+ pastel.valid?(:unicorn) # => false
124
+ ```
125
+
126
+ ## 3 The available styles
127
+
128
+ **Pastel** works with terminal emulators that support minimum sixteen colors. It provides `16` basic colors and `8` styles with further `16` bright color pairs. The corresponding bright color is obtained by prepending the `bright` to the normal color name. For example, color `red` will have `bright_red` as its pair.
129
+
130
+ The variant with `on_` prefix will style the text background color.
131
+
132
+ The foreground colors:
133
+
134
+ * `black`
135
+ * `red`
136
+ * `green`
137
+ * `yellow`
138
+ * `blue`
139
+ * `magenta`
140
+ * `cyan`
141
+ * `white`
142
+ * `bright_black`
143
+ * `bright_red`
144
+ * `bright_green`
145
+ * `bright_yellow`
146
+ * `bright_blue`
147
+ * `bright_magenta`
148
+ * `bright_cyan`
149
+ * `bright_white`
150
+
151
+ The background colors:
152
+
153
+ * `on_black`
154
+ * `on_red`
155
+ * `on_green`
156
+ * `on_yellow`
157
+ * `on_blue`
158
+ * `on_magenta`
159
+ * `on_cyan`
160
+ * `on_white`
161
+ * `on_bright_black`
162
+ * `on_bright_red`
163
+ * `on_bright_green`
164
+ * `on_bright_yellow`
165
+ * `on_bright_blue`
166
+ * `on_bright_magenta`
167
+ * `on_bright_cyan`
168
+ * `on_bright_white`
169
+
170
+ Generic styles:
171
+
172
+ * `clear`
173
+ * `bold`
174
+ * `dim`
175
+ * `italic`
176
+ * `underline`
177
+ * `inverse`
178
+ * `hidden`
179
+ * `strikethrough`
180
+
181
+ ## Contributing
182
+
183
+ 1. Fork it ( https://github.com/[my-github-username]/pastel/fork )
184
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
185
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
186
+ 4. Push to the branch (`git push origin my-new-feature`)
187
+ 5. Create a new Pull Request
188
+
189
+ ## Copyright
190
+
191
+ Copyright (c) 2014 Piotr Murach. See LICENSE for further details.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+
3
+ require "bundler/gem_tasks"
4
+
5
+ FileList['tasks/**/*.rake'].each(&method(:import))
6
+
7
+ desc 'Run all specs'
8
+ task ci: %w[ spec ]
data/lib/pastel.rb ADDED
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+
3
+ require 'equatable'
4
+ require 'pastel/ansi'
5
+ require 'pastel/color'
6
+ require 'pastel/color_resolver'
7
+ require 'pastel/delegator'
8
+ require 'pastel/decorator_chain'
9
+ require 'pastel/version'
10
+
11
+ module Pastel
12
+ # Raised when the style attribute is not supported
13
+ InvalidAttributeNameError = Class.new(::ArgumentError)
14
+
15
+ def new
16
+ Delegator.for(DecoratorChain.empty)
17
+ end
18
+
19
+ module_function :new
20
+ end # Pastel
@@ -0,0 +1,54 @@
1
+ # encoding: utf-8
2
+
3
+ module Pastel
4
+ # Mixin that provides ANSI codes
5
+ module ANSI
6
+ CLEAR = "\e[0m"
7
+
8
+ BOLD = "\e[1m"
9
+ DIM = "\e[2m"
10
+ ITALIC = "\e[3m"
11
+ UNDERLINE = "\e[4m"
12
+ INVERSE = "\e[7m"
13
+ HIDDEN = "\e[8m"
14
+ STRIKETHROUGH = "\e[9m"
15
+
16
+ # Escape codes for text color.
17
+ BLACK = "\e[30m"
18
+ RED = "\e[31m"
19
+ GREEN = "\e[32m"
20
+ YELLOW = "\e[33m"
21
+ BLUE = "\e[34m"
22
+ MAGENTA = "\e[35m"
23
+ CYAN = "\e[36m"
24
+ WHITE = "\e[37m"
25
+
26
+ BRIGHT_BLACK = "\e[90m"
27
+ BRIGHT_RED = "\e[91m"
28
+ BRIGHT_GREEN = "\e[92m"
29
+ BRIGHT_YELLOW = "\e[93m"
30
+ BRIGHT_BLUE = "\e[94m"
31
+ BRIGHT_MAGENTA = "\e[95m"
32
+ BRIGHT_CYAN = "\e[96m"
33
+
34
+ # Escape codes for background color.
35
+ ON_BLACK = "\e[40m"
36
+ ON_RED = "\e[41m"
37
+ ON_GREEN = "\e[42m"
38
+ ON_YELLOW = "\e[43m"
39
+ ON_BLUE = "\e[44m"
40
+ ON_MAGENTA = "\e[45m"
41
+ ON_CYAN = "\e[46m"
42
+ ON_WHITE = "\e[47m"
43
+
44
+ ON_BRIGHT_BLACK = "\e[100m"
45
+ ON_BRIGHT_RED = "\e[101m"
46
+ ON_BRIGHT_GREEN = "\e[102m"
47
+ ON_BRIGHT_YELLOW = "\e[103m"
48
+ ON_BRIGHT_BLUE = "\e[104m"
49
+ ON_BRIGHT_MAGENTA = "\e[105m"
50
+ ON_BRIGHT_CYAN = "\e[106m"
51
+
52
+ BACKGROUND_COLORS = constants.grep(/^ON_*/).freeze
53
+ end # ANSI
54
+ end # Pastel
@@ -0,0 +1,139 @@
1
+ # encoding: utf-8
2
+
3
+ module Pastel
4
+ # A class responsible for coloring strings.
5
+ class Color
6
+ include Equatable
7
+ include ANSI
8
+
9
+ attr_reader :enabled
10
+
11
+ # Initialize a Terminal Color
12
+ #
13
+ # @api public
14
+ def initialize(enabled = false)
15
+ @enabled = enabled
16
+ end
17
+
18
+ # Disable coloring of this terminal session
19
+ #
20
+ # @api public
21
+ def disable!
22
+ @enabled = false
23
+ end
24
+
25
+ # Apply ANSI color to the given string.
26
+ #
27
+ # @param [String] string
28
+ # text to add ANSI strings
29
+ #
30
+ # @param [Array[Symbol]] colors
31
+ #
32
+ # @example
33
+ # color.decorate "text", :yellow, :on_green, :underline
34
+ #
35
+ # @return [String]
36
+ #
37
+ # @api public
38
+ def decorate(string, *colors)
39
+ return string if string.empty?
40
+ validate(*colors)
41
+ ansi_colors = colors.map { |color| lookup(color) }
42
+ ansi_string = "#{ansi_colors.join}#{string}#{ANSI::CLEAR}"
43
+ if ansi_string =~ /(#{Regexp.quote(ANSI::CLEAR)}){2,}/
44
+ ansi_string.gsub!(/(#{Regexp.quote(ANSI::CLEAR)}){2,}/, '')
45
+ end
46
+ matches = ansi_string.scan(/#{Regexp.quote(ANSI::CLEAR)}/)
47
+ if matches.length >= 2
48
+ ansi_string.sub!(/#{Regexp.quote(ANSI::CLEAR)}/, ansi_colors.join)
49
+ end
50
+ ansi_string
51
+ end
52
+
53
+ # Same as instance method.
54
+ #
55
+ # @return [String]
56
+ #
57
+ # @api public
58
+ def self.decorate(string, *colors)
59
+ new.decorate(string, *colors)
60
+ end
61
+
62
+ # Strip ANSI color codes from a string.
63
+ #
64
+ # @param [String] string
65
+ #
66
+ # @return [String]
67
+ #
68
+ # @api public
69
+ def strip(string)
70
+ string.to_s.gsub(/(\[)?\033(\[)?[;?\d]*[\dA-Za-z](\])?/, '')
71
+ end
72
+
73
+ # Return raw color code without embeding it into a string.
74
+ #
75
+ # @return [Array[String]]
76
+ # ANSI escape codes
77
+ #
78
+ # @api public
79
+ def code(*colors)
80
+ validate(*colors)
81
+ colors.map { |color| lookup(color) }
82
+ end
83
+
84
+ # Find color representation.
85
+ #
86
+ # @param [Symbol,String] color
87
+ # the color name to lookup by
88
+ #
89
+ # @return [String]
90
+ # the ANSI code
91
+ #
92
+ # @api private
93
+ def lookup(color)
94
+ self.class.const_get(color.to_s.upcase)
95
+ end
96
+
97
+ # Expose all ANSI color names and their codes
98
+ #
99
+ # @return [Hash[Symbol]]
100
+ #
101
+ # @api public
102
+ def styles
103
+ ANSI.constants(false).each_with_object({}) do |col, acc|
104
+ acc[col.to_sym.downcase] = lookup(col)
105
+ acc
106
+ end
107
+ end
108
+
109
+ # List all available style names
110
+ #
111
+ # @return [Array[Symbol]]
112
+ #
113
+ # @api public
114
+ def style_names
115
+ styles.keys
116
+ end
117
+
118
+ # Check if provided colors are known colors
119
+ #
120
+ # @param [Array[Symbol,String]]
121
+ # the colors to check
122
+ #
123
+ # @reutrn [Boolean]
124
+ #
125
+ # @api public
126
+ def valid?(*colors)
127
+ colors.all? { |color| style_names.include?(color.to_sym) }
128
+ end
129
+
130
+ protected
131
+
132
+ # @api private
133
+ def validate(*colors)
134
+ return if valid?(*colors)
135
+ fail InvalidAttributeNameError, 'Bad style or unintialized constant, ' \
136
+ " valid styles are: #{style_names.join(', ')}."
137
+ end
138
+ end # Color
139
+ end # TTY
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+
3
+ module Pastel
4
+ # Contains logic for resolving styles applied to component
5
+ #
6
+ # Used internally by {Delegator}.
7
+ #
8
+ # @api private
9
+ class ColorResolver
10
+ attr_reader :color
11
+
12
+ def initialize(color = Color.new)
13
+ @color = color
14
+ end
15
+
16
+ def resolve(base, *args)
17
+ unprocessed_string = args.join
18
+ base.reduce(unprocessed_string) do |component, decorator|
19
+ color.decorate(component, decorator)
20
+ end
21
+ end
22
+ end # ColorResolver
23
+ end # Pastel
@@ -0,0 +1,40 @@
1
+ # coding: utf-8
2
+
3
+ module Pastel
4
+ # Collects a list of decorators for styling a string
5
+ #
6
+ # @api private
7
+ class DecoratorChain
8
+ include Enumerable
9
+ include Equatable
10
+
11
+ attr_reader :decorators
12
+
13
+ def initialize(decorators = [])
14
+ @decorators = decorators
15
+ end
16
+
17
+ # Add decorator
18
+ #
19
+ # @api public
20
+ def add(decorator)
21
+ self.class.new(decorators + [decorator])
22
+ end
23
+
24
+ # Iterate over list of decorators
25
+ #
26
+ # @api public
27
+ def each(&block)
28
+ decorators.each(&block)
29
+ end
30
+
31
+ # Create an empty decorator chain
32
+ #
33
+ # @return [DecoratorChain]
34
+ #
35
+ # @api public
36
+ def self.empty
37
+ new([])
38
+ end
39
+ end # DecoratorChain
40
+ end # Patel
@@ -0,0 +1,44 @@
1
+ # coding: utf-8
2
+
3
+ module Pastel
4
+ # Wrapes the {DecoratorChain} to allow for easy resolution
5
+ # of string coloring.
6
+ #
7
+ # @api private
8
+ class Delegator
9
+ extend Forwardable
10
+
11
+ attr_reader :base
12
+
13
+ attr_reader :resolver
14
+
15
+ def_delegators :@color, :valid?, :styles, :strip, :decorate
16
+
17
+ def initialize(base)
18
+ @base = base
19
+ @color = Color.new
20
+ @resolver = ColorResolver.new(@color)
21
+ end
22
+
23
+ # @api public
24
+ def self.for(value)
25
+ new(value)
26
+ end
27
+
28
+ protected
29
+
30
+ def method_missing(method_name, *args, &block)
31
+ new_base = base.add(method_name)
32
+ delegator = self.class.new(new_base)
33
+ if args.empty?
34
+ delegator
35
+ else
36
+ resolver.resolve(new_base, *args)
37
+ end
38
+ end
39
+
40
+ def respond_to_missing?(name, include_all = false)
41
+ super || @color.respond_to?(name, include_all)
42
+ end
43
+ end # Delegator
44
+ end # Pastel
@@ -0,0 +1,5 @@
1
+ # coding: utf-8
2
+
3
+ module Pastel
4
+ VERSION = "0.1.0"
5
+ end
data/pastel.gemspec ADDED
@@ -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 'pastel/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "pastel"
8
+ spec.version = Pastel::VERSION
9
+ spec.authors = ["Piotr Murach"]
10
+ spec.email = [""]
11
+ spec.summary = %q{Terminal strings styling with intuitive and clean API.}
12
+ spec.description = %q{Terminal strings styling with intuitive and clean API.}
13
+ spec.homepage = "https://github.com/peter-murach/pastel"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
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_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency "equatable", "~> 0.5"
23
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+
3
+ require 'pastel'
4
+
5
+ RSpec.configure do |config|
6
+ config.expect_with :rspec do |expectations|
7
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
8
+ end
9
+
10
+ config.mock_with :rspec do |mocks|
11
+ mocks.verify_partial_doubles = true
12
+ end
13
+
14
+ # Limits the available syntax to the non-monkey patched syntax that is recommended.
15
+ config.disable_monkey_patching!
16
+
17
+ # This setting enables warnings. It's recommended, but in some cases may
18
+ # be too noisy due to issues in dependencies.
19
+ config.warnings = true
20
+
21
+ if config.files_to_run.one?
22
+ config.default_formatter = 'doc'
23
+ end
24
+
25
+ config.profile_examples = 2
26
+
27
+ config.order = :random
28
+
29
+ Kernel.srand config.seed
30
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Pastel::Color, '.code' do
6
+ let(:string) { "This is a \e[1m\e[34mbold blue text\e[0m" }
7
+
8
+ subject(:color) { described_class.new }
9
+
10
+ it 'finds single code' do
11
+ expect(color.code(:black)).to eq(["\e[30m"])
12
+ end
13
+
14
+ it 'finds more than one code' do
15
+ expect(color.code(:black, :green)).to eq(["\e[30m", "\e[32m"])
16
+ end
17
+
18
+ it "doesn't find code" do
19
+ expect { color.code(:unkown) }.to raise_error(ArgumentError)
20
+ end
21
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Pastel::Color, '.decorate' do
6
+ let(:string) { 'string' }
7
+
8
+ subject(:color) { described_class.new }
9
+
10
+ it "doesn't apply styling to empty string" do
11
+ expect(color.decorate('')).to eq('')
12
+ end
13
+
14
+ it 'applies green text to string' do
15
+ expect(color.decorate(string, :green)).to eq("\e[32m#{string}\e[0m")
16
+ end
17
+
18
+ it 'applies red text background to string' do
19
+ expect(color.decorate(string, :on_red)).to eq("\e[41m#{string}\e[0m")
20
+ end
21
+
22
+ it 'applies style and color to string' do
23
+ expect(color.decorate(string, :bold, :green)).to eq("\e[1m\e[32m#{string}\e[0m")
24
+ end
25
+
26
+ it 'applies style, color and background to string' do
27
+ text = color.decorate(string, :bold, :green, :on_blue)
28
+ expect(text).to eq("\e[1m\e[32m\e[44m#{string}\e[0m")
29
+ end
30
+
31
+ it "applies styles to nested text" do
32
+ decorated = color.decorate(string + color.decorate(string, :red) + string, :green)
33
+ expect(decorated).to eq("\e[32m#{string}\e[31m#{string}\e[32m#{string}\e[0m")
34
+ end
35
+
36
+ it 'errors for unknown color' do
37
+ expect {
38
+ color.decorate(string, :crimson)
39
+ }.to raise_error(Pastel::InvalidAttributeNameError)
40
+ end
41
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Pastel::Color, '.strip' do
6
+ let(:instance) { described_class.new }
7
+
8
+ subject(:color) { instance.strip(string) }
9
+
10
+ context 'with ansi colors' do
11
+ let(:string) { "This is a \e[1m\e[34mbold blue text\e[0m" }
12
+
13
+ it 'removes color from string' do
14
+ is_expected.to eq('This is a bold blue text')
15
+ end
16
+ end
17
+
18
+ context 'with octal in encapsulating brackets' do
19
+ let(:string) { "\[\033[01;32m\]u@h \[\033[01;34m\]W $ \[\033[00m\]" }
20
+
21
+ it { is_expected.to eq('u@h W $ ') }
22
+ end
23
+
24
+ context 'with octal without brackets' do
25
+ let(:string) { "\033[01;32mu@h \033[01;34mW $ \033[00m" }
26
+
27
+ it { is_expected.to eq('u@h W $ ') }
28
+ end
29
+
30
+ context 'with octal with multiple colors' do
31
+ let(:string) { "\e[3;0;0;t\e[8;50;0t" }
32
+
33
+ it { is_expected.to eq('') }
34
+ end
35
+
36
+ context 'with control codes' do
37
+ let(:string ) { "WARN. \x1b[1m&\x1b[0m ERR. \x1b[7m&\x1b[0m" }
38
+
39
+ it { is_expected.to eq('WARN. & ERR. &') }
40
+ end
41
+
42
+ context 'with escape byte' do
43
+ let(:string) { "This is a \e[1m\e[34mbold blue text\e[0m" }
44
+
45
+ it { is_expected.to eq("This is a bold blue text") }
46
+ end
47
+ end
@@ -0,0 +1,12 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Pastel::Color do
6
+
7
+ subject(:color) { described_class.new }
8
+
9
+ it "exposes all available style ANSI codes" do
10
+ expect(color.styles[:red]).to eq("\e[31m")
11
+ end
12
+ end
@@ -0,0 +1,49 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Pastel::DecoratorChain do
6
+ it "is enumerable" do
7
+ expect(described_class.new).to be_a(Enumerable)
8
+ end
9
+
10
+ it "is equatable" do
11
+ expect(described_class.new).to be_a(Equatable)
12
+ end
13
+
14
+ describe ".each" do
15
+ it "yields each decorator" do
16
+ first = double('first')
17
+ second = double('second')
18
+ chain = described_class.new.add(first).add(second)
19
+ yielded = []
20
+
21
+ expect {
22
+ chain.each { |decorator| yielded << decorator }
23
+ }.to change { yielded }.from([]).to([first, second])
24
+ end
25
+ end
26
+
27
+ describe ".==" do
28
+ it "is equivalent with the same decorator" do
29
+ expect(described_class.new.add(:foo).add(:bar)).
30
+ to eq(described_class.new.add(:foo).add(:bar))
31
+ end
32
+
33
+ it "is not equivalent with different decorator" do
34
+ expect(described_class.new.add(:foo).add(:bar)).
35
+ not_to eq(described_class.new.add(:foo).add(:baz))
36
+ end
37
+
38
+ it "is not equivalent to another type" do
39
+ expect(described_class.new.add(:foo).add(:bar)).
40
+ not_to eq(:other)
41
+ end
42
+ end
43
+
44
+ describe ".inspect" do
45
+ it "displays object information" do
46
+ expect(described_class.new.inspect).to match(/decorators=\[\]/)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Pastel::Delegator do
6
+ describe ".respond_to_missing?" do
7
+ context 'for a method defined on' do
8
+ it "returns true" do
9
+ chain = double(:chain)
10
+ decorator = described_class.new(chain)
11
+ expect(decorator.method(:styles)).not_to be_nil
12
+ end
13
+ end
14
+
15
+ context "for an undefined method " do
16
+ it "returns false" do
17
+ chain = double(:chain)
18
+ decorator = described_class.new(chain)
19
+ expect { decorator.method(:unknown) }.to raise_error(NameError)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,61 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Pastel do
6
+
7
+ subject(:pastel) { described_class.new }
8
+
9
+ describe 'coloring string' do
10
+ it "doesn't apply styles to empty string" do
11
+ expect(pastel.red('')).to eq('')
12
+ end
13
+
14
+ it "colors string" do
15
+ expect(pastel.red("unicorn")).to eq("\e[31municorn\e[0m")
16
+ end
17
+
18
+ it "allows to specify variable number of arguments" do
19
+ expect(pastel.red("unicorn", "running")).to eq("\e[31municornrunning\e[0m")
20
+ end
21
+
22
+ it "combines colored strings with regular ones" do
23
+ expect(pastel.red("Unicorns") + ' will rule ' + pastel.green('the World!')).
24
+ to eq("\e[31mUnicorns\e[0m will rule \e[32mthe World!\e[0m")
25
+ end
26
+
27
+ it "composes color strings" do
28
+ expect(pastel.red.on_green.underline("unicorn")).
29
+ to eq("\e[4m\e[42m\e[31municorn\e[0m")
30
+ end
31
+
32
+ it "allows to nest mixed styles" do
33
+ expect(pastel.red("Unicorn" + pastel.green.on_yellow.underline('running') + '!')).
34
+ to eq("\e[31mUnicorn\e[4m\e[43m\e[32mrunning\e[31m!\e[0m")
35
+ end
36
+
37
+ it "allows for deep nesting" do
38
+ expect(pastel.red('r' + pastel.green('g' + pastel.yellow('y') + 'g') + 'r')).
39
+ to eq("\e[31mr\e[32mg\e[33my\e[32mg\e[31mr\e[0m")
40
+ end
41
+
42
+ it "allows for variable nested arguments" do
43
+ expect(pastel.red('r', pastel.green('g'), 'r')).
44
+ to eq("\e[31mr\e[32mg\e[31mr\e[0m")
45
+ end
46
+ end
47
+
48
+ describe '.valid?' do
49
+ it "when valid returns true" do
50
+ expect(pastel.valid?(:red)).to eq(true)
51
+ end
52
+
53
+ it "returns false when invalid" do
54
+ expect(pastel.valid?(:unknown)).to eq(false)
55
+ end
56
+ end
57
+
58
+ it "respond to styles method" do
59
+ expect(pastel).to respond_to(:styles)
60
+ end
61
+ end
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+
3
+ desc 'Load gem inside irb console'
4
+ task :console do
5
+ require 'irb'
6
+ require 'irb/completion'
7
+ require File.join(__FILE__, '../../lib/pastel')
8
+ ARGV.clear
9
+ IRB.start
10
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+
3
+ desc 'Measure code coverage'
4
+ task :coverage do
5
+ begin
6
+ original, ENV['COVERAGE'] = ENV['COVERAGE'], 'true'
7
+ Rake::Task['spec'].invoke
8
+ ensure
9
+ ENV['COVERAGE'] = original
10
+ end
11
+ end
data/tasks/spec.rake ADDED
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+
3
+ begin
4
+ require 'rspec/core/rake_task'
5
+
6
+ desc 'Run all specs'
7
+ RSpec::Core::RakeTask.new(:spec) do |task|
8
+ task.pattern = 'spec/{unit,integration}{,/*/**}/*_spec.rb'
9
+ end
10
+
11
+ namespace :spec do
12
+ desc 'Run unit specs'
13
+ RSpec::Core::RakeTask.new(:unit) do |task|
14
+ task.pattern = 'spec/unit{,/*/**}/*_spec.rb'
15
+ end
16
+
17
+ desc 'Run integration specs'
18
+ RSpec::Core::RakeTask.new(:integration) do |task|
19
+ task.pattern = 'spec/integration{,/*/**}/*_spec.rb'
20
+ end
21
+ end
22
+
23
+ rescue LoadError
24
+ %w[spec spec:unit spec:integration].each do |name|
25
+ task name do
26
+ $stderr.puts "In order to run #{name}, do `gem install rspec`"
27
+ end
28
+ end
29
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pastel
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Piotr Murach
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: equatable
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '0.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '0.5'
41
+ description: Terminal strings styling with intuitive and clean API.
42
+ email:
43
+ - ''
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - .gitignore
49
+ - .rspec
50
+ - .ruby-gemset
51
+ - .ruby-version
52
+ - .travis.yml
53
+ - Gemfile
54
+ - LICENSE.txt
55
+ - README.md
56
+ - Rakefile
57
+ - lib/pastel.rb
58
+ - lib/pastel/ansi.rb
59
+ - lib/pastel/color.rb
60
+ - lib/pastel/color_resolver.rb
61
+ - lib/pastel/decorator_chain.rb
62
+ - lib/pastel/delegator.rb
63
+ - lib/pastel/version.rb
64
+ - pastel.gemspec
65
+ - spec/spec_helper.rb
66
+ - spec/unit/color/code_spec.rb
67
+ - spec/unit/color/decorate_spec.rb
68
+ - spec/unit/color/strip_spec.rb
69
+ - spec/unit/color/styles_spec.rb
70
+ - spec/unit/decorator_chain_spec.rb
71
+ - spec/unit/delegator_spec.rb
72
+ - spec/unit/new_spec.rb
73
+ - tasks/console.rake
74
+ - tasks/coverage.rake
75
+ - tasks/spec.rake
76
+ homepage: https://github.com/peter-murach/pastel
77
+ licenses:
78
+ - MIT
79
+ metadata: {}
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubyforge_project:
96
+ rubygems_version: 2.0.3
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: Terminal strings styling with intuitive and clean API.
100
+ test_files:
101
+ - spec/spec_helper.rb
102
+ - spec/unit/color/code_spec.rb
103
+ - spec/unit/color/decorate_spec.rb
104
+ - spec/unit/color/strip_spec.rb
105
+ - spec/unit/color/styles_spec.rb
106
+ - spec/unit/decorator_chain_spec.rb
107
+ - spec/unit/delegator_spec.rb
108
+ - spec/unit/new_spec.rb
109
+ has_rdoc: