pax_global_header00006660000000000000000000000064137000746140014514gustar00rootroot0000000000000052 comment=afd82952076e605402a54dd0a142cdcebe34edc7 pastel-0.8.0/000077500000000000000000000000001370007461400130115ustar00rootroot00000000000000pastel-0.8.0/.editorconfig000066400000000000000000000002261370007461400154660ustar00rootroot00000000000000root = true [*.rb] charset = utf-8 end_of_line = lf insert_final_newline = true indent_style = space indent_size = 2 trim_trailing_whitespace = true pastel-0.8.0/.github/000077500000000000000000000000001370007461400143515ustar00rootroot00000000000000pastel-0.8.0/.github/FUNDING.yml000066400000000000000000000000241370007461400161620ustar00rootroot00000000000000github: piotrmurach pastel-0.8.0/.github/ISSUE_TEMPLATE.md000066400000000000000000000010431370007461400170540ustar00rootroot00000000000000### Are you in the right place? * For issues or feature requests file a GitHub issue in this repository * For general questions or discussion post on StackOverflow ### Describe the problem A brief description of the issue/feature. ### Steps to reproduce the problem ``` Your code here to reproduce the issue ``` ### Actual behaviour What happened? This could be a description, log output, error raised etc... ### Expected behaviour What did you expect to happen? ### Describe your environment * OS version: * Ruby version: * Pastel version: pastel-0.8.0/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000006771370007461400201640ustar00rootroot00000000000000### Describe the change What does this Pull Request do? ### Why are we doing this? Any related context as to why is this is a desirable change. ### Benefits How will the library improve? ### Drawbacks Possible drawbacks applying this change. ### Requirements Put an X between brackets on each line if you have done the item: [] Tests written & passing locally? [] Code style checked? [] Rebased with `master` branch? [] Documentation updated? pastel-0.8.0/.gitignore000066400000000000000000000002711370007461400150010ustar00rootroot00000000000000*.gem *.rbc .bundle .config .yardoc Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp *.bundle *.so *.o *.a mkmf.log pastel-0.8.0/.rspec000066400000000000000000000000511370007461400141220ustar00rootroot00000000000000--color --require spec_helper --warnings pastel-0.8.0/.rubocop.yml000066400000000000000000000012471370007461400152670ustar00rootroot00000000000000Lint/AssignmentInCondition: Enabled: false Metrics/AbcSize: Max: 30 Metrics/BlockLength: CountComments: true Max: 25 ExcludedMethods: [] Exclude: - "spec/**/*" Metrics/ClassLength: Max: 1500 Metrics/CyclomaticComplexity: Enabled: false Metrics/LineLength: Max: 80 Metrics/MethodLength: Max: 20 Naming/BinaryOperatorParameterName: Enabled: false Style/AsciiComments: Enabled: false Style/LambdaCall: EnforcedStyle: braces Style/StringLiterals: EnforcedStyle: double_quotes Style/TrivialAccessors: Enabled: false # { ... } for multi-line blocks is okay Style/BlockDelimiters: Enabled: false Style/CommentedKeyword: Enabled: false pastel-0.8.0/.travis.yml000066400000000000000000000006661370007461400151320ustar00rootroot00000000000000--- language: ruby before_install: "gem install bundler -v '< 2.0'" bundler_args: --without yard benchmarks script: "bundle exec rake ci" rvm: - 2.0 - 2.1 - 2.2 - 2.3 - 2.4 - 2.5 - 2.6 - 2.7 - ruby-head - jruby-9.2.11.0 - jruby-head - truffleruby matrix: allow_failures: - rvm: ruby-head - rvm: jruby-head - rvm: truffleruby fast_finish: true branches: only: master notifications: email: false pastel-0.8.0/CHANGELOG.md000066400000000000000000000112231370007461400146210ustar00rootroot00000000000000# Change log ## [v0.8.0] - 2020-07-04 ### Changed * Change gemspec to require Ruby 2.0 or higher * Change Pastel#new to use keyword arguments in place of hash options * Change to freeze all strings * Remove equatable dependency ### Fixed * Fix Pastel#strip to recognise no-number reset by DanielVartanov(@DanielVartanov) * Fix Pastel#undecorate to correctly assign styles for nested colors ## [v0.7.4] - 2020-05-08 ### Fixed * Fix ColorParser#undecorate to require direct dependency by Nick Pezza(@npezza93) ## [v0.7.3] - 2019-06-16 ### Changed * Change gemspec to load required files directly without git * Change to update tty-color & equatable dependency versions ### Fixed * Fix Delegator warning ## [v0.7.2] - 2017-11-09 ### Changed * Change to load relative file paths * Change to allow `#alias_color` to accept multiple colors by Jared Ning (@ordinaryzelig) ## [v0.7.1] - 2017-01-09 ### Changed * Change to load specific files when needed * Change to freeze ANSI attributes * Change to directly assign enabled attribute ## [v0.7.0] - 2016-12-27 ### Changed * Enabled colors on Windows by default * Update tty-color dependency ### Fixed * Fix Color#decorate to prevent redecoration with the same color ## [v0.6.1] - 2016-04-09 ### Fixed * Fix #decorate to apply color to non zero length strings ## [v0.6.0] - 2016-01-15 ### Added * Add helper functions #foreground?, #background?, #style to ANSI module * Add ColorParser for parsing color symbols out of text * Add Pastel#undecorate for parsing color names out of strings ### Changed * Change to use tty-color for color capabilities detection * Change to move enabled option to Pastel#new * Improve performance of Color#lookup * Change Color#decorate performance to be 6x faster! * Change Color DSL styling to be 3x faster! ### Fixed * Fix #strip to only remove color sequences * Fix #decorate to pass through original text when decorating without colors * Fix #decorate to work correctly with nested background colors ## [v0.5.3] - 2015-01-05 ### Fixed * Change gemspec to fix dependencies requirement ## [v0.5.2] - 2015-11-27 (Nov 27, 2015) * Change Color#decorate to accept non-string values and immediately return ## [v0.5.1] - 2015-09-18 ### Added * Add ability to call detached instance with array access ## [v0.5.0] - 2015-09-13 ### Added * Add external dependency to check for color support * Add #colored? to check if string has color escape codes * Add #eachline option to allow coloring of multiline strings ### Changed * Further refine #strip method accuracy ### Fixed * Fix redefining inspect method * Fix string representation for pastel instance ## [v0.4.0] - 2014-11-22 ### Added * Add ability to #detach color combination for later reuse * Add ability to nest styles with blocks ### Fixed * Fix Delegator#respond_to method to correctly report existence of methods ## [v0.3.0] - 2014-11-08 ### Added * Add ability to alias colors through #alias_color method * Add ability to alias colors through the environment variable * Improve performance of Pastel::Color styles and lookup methods ### Fixed * Fix bug concerned with lack of escaping for nested styles ## [v0.2.1] - 2014-10-13 ### Fixed * Fix issue #1 with unitialize dependency ## [v0.2.0] - 2014-10-12 ### Added * Add #supports? to Color to check for terminal color support * Add ability to force color support through :enabled option ### Changed * Change gemspec to include equatable as dependency * Change Delegator to stop creating instances and improve performance [v0.8.0]: https://github.com/piotrmurach/pastel/compare/v0.7.4...v0.8.0 [v0.7.4]: https://github.com/piotrmurach/pastel/compare/v0.7.3...v0.7.4 [v0.7.3]: https://github.com/piotrmurach/pastel/compare/v0.7.2...v0.7.3 [v0.7.2]: https://github.com/piotrmurach/pastel/compare/v0.7.1...v0.7.2 [v0.7.1]: https://github.com/piotrmurach/pastel/compare/v0.7.0...v0.7.1 [v0.7.0]: https://github.com/piotrmurach/pastel/compare/v0.6.1...v0.7.0 [v0.6.1]: https://github.com/piotrmurach/pastel/compare/v0.6.0...v0.6.1 [v0.6.0]: https://github.com/piotrmurach/pastel/compare/v0.5.3...v0.6.0 [v0.5.3]: https://github.com/piotrmurach/pastel/compare/v0.5.2...v0.5.3 [v0.5.2]: https://github.com/piotrmurach/pastel/compare/v0.5.1...v0.5.2 [v0.5.1]: https://github.com/piotrmurach/pastel/compare/v0.5.0...v0.5.1 [v0.5.0]: https://github.com/piotrmurach/pastel/compare/v0.4.0...v0.5.0 [v0.4.0]: https://github.com/piotrmurach/pastel/compare/v0.3.0...v0.4.0 [v0.3.0]: https://github.com/piotrmurach/pastel/compare/v0.2.1...v0.3.0 [v0.2.1]: https://github.com/piotrmurach/pastel/compare/v0.2.0...v0.2.1 [v0.2.0]: https://github.com/piotrmurach/pastel/compare/v0.1.0...v0.2.0 [v0.1.0]: https://github.com/piotrmurach/pastel/compare/v0.1.0 pastel-0.8.0/CODE_OF_CONDUCT.md000066400000000000000000000062351370007461400156160ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at piotr@piotrmurach.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ pastel-0.8.0/Gemfile000066400000000000000000000002711370007461400143040ustar00rootroot00000000000000source "https://rubygems.org" gemspec group :test do gem "coveralls", "~> 0.8.22" gem "simplecov", "~> 0.16.1" gem "yardstick", "~> 0.9.9" gem "benchmark-ips", "~> 2.7.2" end pastel-0.8.0/LICENSE.txt000066400000000000000000000020551370007461400146360ustar00rootroot00000000000000Copyright (c) 2014 Piotr Murach MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pastel-0.8.0/README.md000066400000000000000000000266561370007461400143070ustar00rootroot00000000000000
pastel logo
# Pastel [![Gem Version](https://badge.fury.io/rb/pastel.svg)][gem] [![Build Status](https://secure.travis-ci.org/piotrmurach/pastel.svg?branch=master)][travis] [![Build status](https://ci.appveyor.com/api/projects/status/9blbjfq42o4v1rk4?svg=true)][appveyor] [![Code Climate](https://codeclimate.com/github/piotrmurach/pastel/badges/gpa.svg)][codeclimate] [![Coverage Status](https://coveralls.io/repos/github/piotrmurach/pastel/badge.svg)][coverage] [![Inline docs](http://inch-ci.org/github/piotrmurach/pastel.svg?branch=master)][inchpages] [gem]: http://badge.fury.io/rb/pastel [travis]: http://travis-ci.org/piotrmurach/pastel [appveyor]: https://ci.appveyor.com/project/piotrmurach/pastel [codeclimate]: https://codeclimate.com/github/piotrmurach/pastel [coverage]: https://coveralls.io/github/piotrmurach/pastel [inchpages]: http://inch-ci.org/github/piotrmurach/pastel > Terminal output styling with intuitive and clean API that doesn't monkey patch String class. **Pastel** is minimal and focused to work in all terminal emulators. ![screenshot](https://github.com/piotrmurach/pastel/raw/master/assets/screenshot.png) **Pastel** provides independent coloring component for [TTY](https://github.com/piotrmurach/tty) toolkit. ## Features * Doesn't monkey patch `String` * Intuitive and expressive API * Minimal and focused to work on all terminal emulators * Auto-detection of color support * Allows nested styles * Performant ## Installation Add this line to your application's Gemfile: gem "pastel" And then execute: $ bundle Or install it yourself as: $ gem install pastel ## Contents * [1. Usage](#1-usage) * [2. Interface](#2-interface) * [2.1 Color](#21-color) * [2.2 Decorate](#22-decorate) * [2.3 Undecorate](#23-undecorate) * [2.4 Detach](#24-detach) * [2.5 Strip](#25-strip) * [2.6 Styles](#26-styles) * [2.7 Lookup](#27-lookup) * [2.8 Valid?](#28-valid) * [2.9 Colored?](#29-colored) * [2.10 Enabled?](#210-enabled) * [2.11 Eachline](#211-eachline) * [2.12 Alias Color](#212-alias-color) * [3. Supported Colors](#3-supported-colors) * [4. Environment](#4-environment) * [5. Command line](#5-command-line) ## 1 Usage **Pastel** provides a simple, minimal and intuitive API for styling your strings: ```ruby pastel = Pastel.new puts pastel.red("Unicorns!") ``` **Pastel** doesn't print the colored string out, just returns it, you'll have to print it yourself. You can compose multiple styles through chainable API: ```ruby pastel.red.on_green.bold("Unicorns!") ``` It allows you to combine styled strings with unstyled ones: ```ruby pastel.red("Unicorns") + " will rule " + pastel.green("the World!") ``` It supports variable number of arguments: ```ruby pastel.red("Unicorns", "are", "running", "everywhere!") ``` You can also nest styles as follows: ```ruby pastel.red("Unicorns ", pastel.on_green("everywhere!")) ``` Nesting is smart enough to know where one color ends and another one starts: ```ruby pastel.red("Unicorns " + pastel.green("everywhere") + pastel.on_yellow("!")) ``` You can also nest styles inside blocks: ```ruby pastel.red.on_green("Unicorns") { green.on_red("will ", "dominate") { yellow("the world!") } } ``` When dealing with multiline strings you can set `eachline` option(more info see [eachline](#211-eachline)): ```ruby pastel = Pastel.new(eachline: "\n") ``` You can also predefine needed styles and reuse them: ```ruby error = pastel.red.bold.detach warning = pastel.yellow.detach puts error.("Error!") puts warning.("Warning") ``` If your output is redirected to a file, you probably don't want Pastel to add color to your text. See https://github.com/piotrmurach/pastel#210-enabled for a way to easily accomplish this. **Pastel** has companion library called `pastel-cli` that allows you to style text in terminal via `pastel` executable: ```bash $ pastel green "Unicorns & rainbows!" ``` ## 2 Interface ### 2.1 Color pastel.`[....](string, [string...])` Color styles are invoked as method calls with a string argument. A given color can take any number of strings as arguments. Then it returns a colored string which isn't printed out to terminal. You need to print it yourself if you need to. This is done so that you can save it as a string, pass to something else, send it to a file handle and so on. ```ruby pastel.red("Unicorns ", pastel.bold.underline("everywhere"), "!") ``` Please refer to [3. Supported Colors](#3-supported-colors) section for full list of supported styles. ### 2.2 Decorate This method is a lower level string styling call that takes as the first argument the string to style followed by any number of color attributes, and returns string wrapped in styles. ```ruby pastel.decorate("Unicorn", :green, :on_blue, :bold) ``` This method will be useful in situations where colors are provided as a list of parameters that have been generated dynamically. ### 2.3 Undecorate It performs the opposite to `decorate` method by turning color escape sequences found in the string into a list of hash objects corresponding with the attribute names set by those sequences. Depending on the parsed string, each hash object may contain `:foreground`, `:background`, `:text` and/or `:style` keys. ```ruby pastel.undecorate("\e[32mfoo\e[0m \e[31mbar\e[0m") # => [{foreground: :green, text: "foo"}, {text: " "}, {foreground: :red, text: "bar"}] ``` To translate the color name into sequence use [lookup](#27-lookup) ### 2.4 Detach The `detach` method allows to keep all the associated colors with the detached instance for later reference. This method is useful when detached colors are being reused frequently and thus shorthand version is preferred. The detached object can be invoked using `call` method or it's shorthand `.()`, as well as array like access `[]`. For example, the following are equivalent examples of detaching colors: ```ruby notice = pastel.blue.bold.detach notice.call("Unicorns running") notice.("Unicorns running") notice["Unicorns running"] ``` ### 2.5 Strip Strip only color sequence characters from the provided strings and preserve any movement codes or other escape sequences. The return value will be either array of modified strings or a single string. The arguments are not modified. ```ruby pastel.strip("\e[1A\e[1m\e[34mbold blue text\e[0m") # => "\e[1Abold blue text" ``` ### 2.6 Styles To get a full list of supported styles with the corresponding color codes do: ```ruby pastel.styles ``` ### 2.7 Lookup To perform translation of color name into ansi escape code use `lookup`: ```ruby pastel.lookup(:red) # => "\e[31m" pastel.lookup(:reset) # => "\e[0m" ``` ### 2.8 Valid? Determine whether a color or a list of colors are valid. `valid?` takes one or more attribute strings or symbols and returns true if all attributes are known and false otherwise. ```ruby pastel.valid?(:red, :blue) # => true pastel.valid?(:unicorn) # => false ``` ### 2.9 Colored? In order to determine if string has color escape codes use `colored?` like so ```ruby pastel.colored?("\e[31mcolorful\e[0m") # => true ``` ### 2.10 Enabled? In order to detect if your terminal supports coloring do: ```ruby pastel.enabled? # => false ``` In cases when the color support is not provided no styling will be applied to the colored string. Moreover, you can force **Pastel** to always print out string with coloring switched on: ```ruby pastel = Pastel.new(enabled: true) pastel.enabled? # => true ``` If you are outputting to stdout or stderr, and want to suppress color if output is redirected to a file, you can set the enabled attribute dynamically, as in: ```ruby stdout_pastel = Pastel.new(enabled: $stdout.tty?) stderr_pastel = Pastel.new(enabled: $stderr.tty?) ``` ### 2.11 Eachline Normally **Pastel** colors string by putting color codes at the beginning and end of the string, but if you provide `eachline` option set to some string, that string will be considered the line delimiter. Consequently, each line will be separately colored with escape sequence and reset code at the end. This option is desirable if the output string contains newlines and you're using background colors. Since color code that spans more than one line is often interpreted by terminal as providing background for all the lines that follow. This in turn may cause programs such as pagers to spill the colors throughout the text. In most cases you will want to set `eachline` to `\n` character like so: ```ruby pastel = Pastel.new(eachline: "\n") pastel.red("foo\nbar") # => "\e[31mfoo\e[0m\n\e[31mbar\e[0m" ``` ### 2.12 Alias Color In order to setup an alias for standard colors do: ```ruby pastel.alias_color(:funky, :red, :bold) ``` From that point forward, `:funky` alias can be passed to `decorate`, `valid?` with the same meaning as standard colors: ```ruby pastel.funky.on_green("unicorn") # => will use :red, :bold color ``` This method allows you to give more meaningful names to existing colors. You can also use the `PASTEL_COLORS_ALIASES` environment variable (see [Environment](#4-environment)) to specify aliases. Note: Aliases are global and affect all callers in the same process. ## 3 Supported Colors **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. The variant with `on_` prefix will style the text background color. The foreground colors: * `black` * `red` * `green` * `yellow` * `blue` * `magenta` * `cyan` * `white` * `bright_black` * `bright_red` * `bright_green` * `bright_yellow` * `bright_blue` * `bright_magenta` * `bright_cyan` * `bright_white` The background colors: * `on_black` * `on_red` * `on_green` * `on_yellow` * `on_blue` * `on_magenta` * `on_cyan` * `on_white` * `on_bright_black` * `on_bright_red` * `on_bright_green` * `on_bright_yellow` * `on_bright_blue` * `on_bright_magenta` * `on_bright_cyan` * `on_bright_white` Generic styles: * `clear` * `bold` * `dim` * `italic` * `underline` * `inverse` * `hidden` * `strikethrough` ## 4 Environment ### 4.1 PASTEL_COLORS_ALIASES This environment variable allows you to specify custom color aliases at runtime that will be understood by **Pastel**. The environment variable is read and used when the instance of **Pastel** is created. You can also use `alias_color` to create aliases. Only alphanumeric and `_` and `.` are allowed in the alias names with the following format: ```ruby PASTEL_COLORS_ALIASES="newcolor_1=red,newcolor_2=on_green,funky=red.bold" ``` ## 5. Command line You can also install [pastel-cli](https://github.com/piotrmurach/pastel-cli) to use `pastel` executable in terminal: ```bash $ pastel green 'Unicorns & rainbows!' ``` ## Contributing 1. Fork it ( https://github.com/piotrmurach/pastel/fork ) 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create a new Pull Request ## Code of Conduct Everyone interacting in the Pastel project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/piotrmurach/pastel/blob/master/CODE_OF_CONDUCT.md). ## Copyright Copyright (c) 2014 Piotr Murach. See LICENSE for further details. pastel-0.8.0/Rakefile000066400000000000000000000002151370007461400144540ustar00rootroot00000000000000# encoding: utf-8 require "bundler/gem_tasks" FileList['tasks/**/*.rake'].each(&method(:import)) desc 'Run all specs' task ci: %w[ spec ] pastel-0.8.0/appveyor.yml000066400000000000000000000011461370007461400154030ustar00rootroot00000000000000--- install: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - gem install bundler -v '< 2.0' - bundle install before_test: - ruby -v - gem -v - bundle -v build: off test_script: - bundle exec rake ci environment: matrix: - ruby_version: "200" - ruby_version: "200-x64" - ruby_version: "21" - ruby_version: "21-x64" - ruby_version: "22" - ruby_version: "22-x64" - ruby_version: "23" - ruby_version: "23-x64" - ruby_version: "24" - ruby_version: "24-x64" - ruby_version: "25" - ruby_version: "25-x64" - ruby_version: "26" - ruby_version: "26-x64" pastel-0.8.0/assets/000077500000000000000000000000001370007461400143135ustar00rootroot00000000000000pastel-0.8.0/assets/pastel_logo.png000066400000000000000000000130171370007461400173330ustar00rootroot00000000000000PNG  IHDR|%tEXtSoftwareAdobe ImageReadyqe<IDATx]o[GzE_\V$H묁hEnMu@-=%@+ 軥틤@T.t}AE#7fk:Q_"%Jp=::9gP"e;rf(Ʌ@  @r!H.B Dާ1IK S^}1-͉5ňJ PG,%Z }.D+X\-SR\ <}Ʌ@8x~q]@ #qcp_i)޸qDMIKAr!Tk)Vs>K?d0nf!BX)siYb Bk~=hy N H^0Lˡ\gy(ɅF!kaPɅ_"`Fr!$B H.Ʌ@  KNfKs>CB]XpRϾ2Yr+~$0y>KrnԨ!K#~PrʂgX Y}׮ҟ娵! 6l@!Q2zyOY(r2f]eV}r}nTLrV>?-1)!--dRqfC(hM~$*o]VhE=f}2CC"O?f\bv5&}N˒uI NfrpPdYaJIH#uy"dy0{{5yd3&f$Z&g3ecv/eĚgNy`BA7`zvK`>ߪ=rl*{:`Wju)f\~GI;>-D|)*-@/_ݕ6=Z/e{xt’8%rQ@dE;jZ)t\Ƃ A*ye`͚'FqўKe}X_B*P gϞwk%Ӕ7}mFd=[#SmE.m<쬎憮xZ#] wa{G>L@ƈR h0aj" -vBe"B`R`vaMD>(yJ'0]$p^<Hx\VQ` iZ4ZJ1e$0yX %5Z8X5Fdj?+vo=i-6D163kGd@WulV;{d`ehػ%L˭%F>X<-Sm +ؙ!WFvxo\VKD|k{mA2# yZ;ݮh;l׈b>GOӬ/%1&3C|eI"̓?qr \a sΕ\orQ_jQ 2%֬0=kU[G{לM-*|098%" T`Q^D r\8̜3^>PMIR09;+JĴ{ʾ-o9ƻ.*Qj_glŎͥ1oMܢ*`fF툅ڷjk*^ )OyZ)@w\F@ j(D^x줫Y(Jz=1J=Tc!yeA6 L~Q`*ʜؾsz#*h.E桓_i3)߬ZV 5ryZ&,\6>WPu;/,Hъ%j9L~˓I-5`>1|gMF $y$|I>R$W 4  3#G 9j@IADKK s+PD Lǖ 6 !WYrbP,Rf^Wzۯ4AYXˑ u 4!>BK4jlgRe0(9+tNKaP{MX_ omthTq[o)XfLn/R`ٶDv uwYk%bUWHrfXQ=$l}R=`&?, "nĒ#ܴ20J2BQ1K?mDNiP^OJ?l軜<$ʔjQ#ށ_i~xy@RPxl]Ɣ cؐ(H(c[Db VS!C_"A%:ZQoN0 >JS2ij{%6OŤuwJ/)|'dLIcL'&pnNM!)hK9jo\>U#2o+6*ss̚m~GjRj T$SJ\I. ye`=ѪXx Z6f8U|ohXՖR #]N F{:.kړ\>X*H] 37@Ҷ2q,jYVT]Enc8%ldjFŽŴHg8bGGL7ar}`["˭\o;fy X0B `׈#EK`/cCure沑lnX(`ܸXUhIǹTXr~=2_m$~ sByr|ڇW/E\O\D7v`[]&iLp2h)nt@/kI! | e*eC |M_]L@I}紨Yyfy5|.Өf3  maM j]Д%r|CYl9ԣ@MG(j/bY)V0xhf3ha A z8,qg>񱀎BbY(?+[ ^F[\\o3f扠9;:.q~UV!=6gv"<6, ç]?2/O*.a>&Mp=dB&mE? 0+@9SH'Sm ET"%594f/a_ {7&sYLCY!e#4}EF͇l4T*1͊<3-is]^p3̑;y5:i4 h.#E0 Ŝ ``تe.J2g `e- 0}Cv|4%)aƘF*;<Ĝ&ߩΰ)҆V$vHBOC 4Q ;%JHI*X54LSԗ[AR!蛅Ʌ@ $@r!H.Ʌ@ \ @r!$B Z /CIENDB`pastel-0.8.0/assets/screenshot.png000066400000000000000000000454061370007461400172070ustar00rootroot00000000000000PNG  IHDRZ=QiCCPICC ProfilexygTT˳o=y! 9s9JN0!(QDE$ALDAQ1PD$sZU5z\FGÌDD: {x V Tqц6m?&i_ 8 {kFѱ~"ѤhW;8^~1[ x:*56BHFMR"h0#X?g9"#,_LJ -HdbZ\t85."<y_"v|Æ|&7ي3D tqBh;X&nX/(X>:h# wbl`:^g3T#+ .Apo\)>8-= o: ,Z\,υ¢wl@Հ5 "}$6˂ @E8/@FD!c,?"f@G'  :cc]-?s#5 6 -VBкh=&@,Z6D룵&0ظ?JPbyTk~poiڿ@YXuqTtJ,-8$^Y2r2J *;oΞo"hi#x? _#˔~$OMyD6@ Hr> $Ԁ60 A'Xl 1pTӠ4pt(x ^)0>EC3 C4i@z)d9B/ EB ^hTAY uA}=1zC/,0/,!l ;p0¹p!\p;?SgxP$J%@v}#ȓS3̳k[{wπ/o__@@ `Yu!q!VDa ~E~["D^D5DCDDO J,/#Q#D+!&Y-9*KJHUI=դiҏe022252dd ee/Ⱦcˑ*/")\BBKEbbⲒRerr򒊴J)IUfU[jjjE}O?`Ѱ814ԼuE/mY0&9q@:i]!]Y)==_3zST͆_bڌVӍ{MP&&LFL).ō̂.-Z`,-[ޡᣣ^ǻNN>NMN?:tpIpweprmt]u3q+rrwOwAyz{6}bWׄw=\{a\6nPwQk+~~'?χ臔,ЌiPӡa·mF#|#")aQ|QQbbN,ZAqq,Hr8 ?]^bU$פLɑ)R))fii{f}n~6͜2j&fe?Q)m_w.onV~b>p Hr~E֡C  J 6DH ‘jGO<6q\xCSQjtmq{@ɡ'|N+U)=]F,K(*)8VQRʨ$c N]<{33g϶׈Քbkk?ֹ=q~|džF& 曽G[LZ:/^/o~2002h=8two ݸujڇU>lQisTs1q;O,ߒa-sb,YRvҭrW֕TNS_ӤҖ5ѳ41L43.5i06obrmhc'K^swW_G^jT0s.d5]ؓȪU}$&%g5/,)**E8e3YZރں]O79wnO^񁐃:lzGZjr|D\BbrJjIjSfϔe3<]y>!xSՅsͭ-]o]rEjvɎGgolRu+FYұkŸe&+bOW8Utg;kjI{/hhk|Ѵ,bz1ȥ_^ܩE뮸v޺W iw 6 ݛ@V~9yv\܉g__ʿr~ۻSc?\Y vד-.%~]_7o??HmZCX:MúYCa0w`G'uW01e*T2cigb{þI6+(/DhFȲ"5MJK')#/ Ɏ')X) (*(׫dɨ-9ZRڛ:zIV+F3L\M0f̯YR&YOٴy#;Cc!J0wmK S,ټ?kMeN]O 0 d |TMCFB|%×":#̢ 1cq9 z Iqr)u>i\iO[d=Yټ9Sr/=q5_X`# >T[]1e1*ߓNVlN{I9[]3XkyFҚ_*ĶRSWy7{n232$v/a9#ӣ'Lzě)N}\/Y\r^}?wK;gV sfउBDC{2ΚhyI{~C {` @HfaxP\(CTMB#.,cI@I, |ppoB%&phOl!I1: d9RsXXXXY%sxrœ\q~[A}!k蹰ڨx΄/RRoJI7_!|CMG m;RdR\sP[6YWY{_ujZzFkͶ-ӭɗIWN_Si۹}Ͷ[n}pݑ{YT?Ԏczo޾?A{faԼݧ_\ܿtyy?jZu%;L$x? j&a,Ǒe0D+ K:b+qt#>`NB!VH'*zz 6 o|bKYb`1aYfese'qrqeG|}@,sgZ#2*F[Y5^~ƚ:ܹC?.4ǷLj^:us5v."7n}60pG{V|~|ɳ g+(zޛ·ɾϘޢml-nl'o_\ :#71pEl^ެE ??Wc;w`k8usEЫ pHYs   IDATx}mY{Ҡ#Ab:hF-HN8DIOؘ ݃&yFfdw8ycX}/Wso7ESUyݫ9Q/5T P*@/˽ P*@VAT P*@x9h_%T P*@ Z5T P*@)kv⯿Zo߾-kpd-9&1 &`[i1Xݷ2/>]#_Myc{Iq7 lNqn=FD'jY*@xߊϛI1pɷGsVn.>p{̎uIxi^լϾӫ@(?PLQSd$ T P*@]_9MT P) d]<+/m1xXǝ6Gv-mX}ߑ+OSH2qP16 Ƶ{ [/%[4'NZfپvl\ ;#m68B0<Ş*C--qҨƪ$A!qڣb9WfYfK`nҺ%@Q³Q;X48BD `).Ф-;D8Go Eb[}lKI THA2#סt+?{38r1"9 xYl-F" `[ ؞vbc0-%lin'xd![l-Y#hƩm6U#bvqS e(9DxLo)` TA HmSY8ii{68Hfda- MQr'Ul)Oꦥ!cE 3jPmk(-Yk>%SE5y !شyLKSG@MT {I:tlUl+W؆+*[I_c!8o3AVh<ΤYP eHJ|*I߆,`Ҿ;!K1yc!xT~bEPkW|\n[@khY)[-J]n+0(Pbmu{Ϲ96Sg ՕU;$Vqt*voOP LY?ڝ&_ԭ Dȥۥa3{+оܻ*xxϘy_<h,D{1.02י H;t;x\}>{geb(/\JQ}5c!bd0ŝErfSuŢJvеE0.wlp\+xdYBs~DIc\_b4 ic=ʊc sSYؼs>+oug-A1ddMSiȮr`jW||c\kɝv{mW:`|:-B2o|^{ou.r_\7 %7uh1'mUܼ &N_zA g,L{>X P*@p|]0K*A+r.*>hT1 @Rb]*@WI4T P*@) ȿ{7[7C^bQ*0_4_2R*@W/W%/T P*<߽>ʉT P}QFT P*@T P*@T P*@T P*@T P*@T P*@T P*@T P*@RY?X ?:nYOdkqpEVI *@@]//?)uk"®Uc`/M$x9¸tDp #*@ Jo ޿O(AG05\  }'ީ}uTIp%2c`<^[^\]\~֮m~&OyT6DZ#%֮f0[ƒ?h+zd#8Ej%q[-tujH%iDG ,,\fb@,ZȢkD[/ஊS,#@#ѽՑڛ6rZ1m;9~sՙ!, ;u%&W!ad!m-l.#L#=7xڳ`9rx>/a[$n[J;o!ne;f":H޶ z;l"!.]50XW!{ƑE:R^BW^ݞx,/σ`bu9" \9poG"W#eYStT" m8n姭e@U Ê9)Gm4\fY `OkZLMn] ȅZ֬ʑ7 oTG%vjZ;n8ufe{/«=u:fz]9_|z2޻yWK6$mΩXGj#mɀmHDӵ}{Hq s`,.~NZY+k9Xਾ\)bu"[w`8fivPZeyAM}d=E.!&ɖp[{4} 7W5v0b3^ tfg,r_wbV˸[f)ϚMUHIoj4س_wS7}9 z- &u#_Vt&cdqGzũp'bǔg*JmϞ%*pYv [Sw er۷z+J"1MxudГA={ԼȜx19qPMPwd"c| wd]sn+3\oBy{L`k>^+~Qo9e >t8e=Hna|.{aK78wlUHȉݎ\TO2!}!&ݲhzq]sj"^yTa.R 6lVz5/hŹ/#; FhOsE4dW0RI$-{b{JS|+ivWmd4V۔AXE6 !DbXDsNu$dTA Gb}랎-FI umجguykUcP"1x3Ayj~K;C&6EV#}\bQEO KI&m3y?)0Q,b\xdxQgv8\D1p6dž*{L]oazU$ L:>2hx9?ͲۭaȴsNAylבS2IbMHsnjڞl>Os6=u1OY5'ɶg) =Ywm@;}JOf`2T_0?밹bEq1\wU@3sw}azg sg6s靝IMӠA ߷=htB )Fw~/7*:{Wz/|K RpI!S+@|:^7;ۏa> Hjё9T[Pohy5xa{A_ q^p~~]/A ^=A/xׅ]Q*@T P*@T P*@T P*@T P*@T P*@T P*@T P*@T P*@T P*@TV =KJڿ`]* ?;VnY?ZF\EЧ0T POT7sה:5,®U~c`/M'ux9¸tDp #*@ Jo ۮ?F#i.RxD@u>[s>:ۤzl1TA0-zJ/.Ss.^?Z+Bkq6?< *o"-gk`S-?[=ɢw5ŒD-:~5D_|"#GH|.@31A [hd5ĭpW)He9Sf~[}֐iDƺ~@z0C䶖 G6[sxZdQb89I_0rY?'-_A,pT_HlTrdunv0qopxuB\TIi Y^j2RIp Zyڋ{dK=[|dmui_c:ճYsN9N;|ane-`Cg[u7k5ioٯ۩^r=EݺՑʯx[+YOkLzQt18# 8ѓic`cʳ X@~gOwA^^v-ۻ2~Ja%k:YIՠ螽^yj^dN|uN8u&^]g;v2px1z;b.9\ ߷G^ȼs5 \\]2N:2$0[{ >sథN;D^Po*$unGu_L'wnY4|[95z/<0yfFwj\Iq"+$=d`r%)>H 4+RNzPp2_y+m ,"E"1,u9:D* #ہ>uOGF#ڤuSxl3։:˼۪1(<<5vE{`H?q򍥝!"HRGja`1Rꨢ '~ъ%ȤzBJDudqd1U.]DЁA23H.}n8uls}cCs=7k*W q &fhCЎᜟfY0dڹl<6)]$1RzӦS$c҂tmx'9:㘧mxd볎剞G^6P x'h0pίKvu\D\u8a.ۻ* NΙNr0=3Aud9i3˿NiP 4:GTP#;zvp+@l>]W%x)ϩ@W >lw[RV/G0 oo$5Z@^ *|(|7B>lcƉv* j D;{ ^uaT P*@T P*@T P*>w>[Ds$?}7hU]p]:x鏊 ,JuGo曟,L?*駟 ,ET P*@#_VET PUT P*@#|ʡT P*PUvqT P*@x/Jٻw|Q9]Oy~}C֑`{|?ݻo*UR P*PWO>_|kJ`OƩT |S8ҋ) FS改_vpv$uude$*@wMP*@V`Ux;8KIP*@T ecT P*@@a~Bk4oᾊ|\G+ Mfli =wp<"å5q̽ @![nA[\Gxjs#) ~U&cýg5ؕCXQ( Y[ߕQG[fג嶎umC7p<"kč'AO f \KJ;ީ#\ޖv[cVxD؊HHc[X&39 8le-nmGF#8X386FW]]GFbuU-IzZGcI^2WWZ=o_[ݚ!%qNpx[7H8vZͺR:˶Ma`Sm"mOHo"ESW݂xl Rvr2L$tq'Gkiiw!c#Gכᡃ}6YSAiZ56imf,R֫.FM Y.\G$"q"VGY-J$[k4(?mPI>A&EH%=bZIf"k!k4(?mP>A6AK+fq8u*6< `U$&Y׭U^ ApB{i>AqB\"қlmzjUdu۬ezkkk4(?mP>A*fQTg>ȅ AK젳}nku 3YdsiEʑRq[-XDL%JH,@:R²][%n"brWۣlV5ʑ }qH`12Keu1[81NkGm#'XD܈$)XnO>OIDAT<T"̈ظU6 mc:XD̪H䉑"9I_0rMu9pۘG4 G UJR_>o$3ԭs9n:-{S6Fh%n,)I|X2몎Ii [\eign,UouRŒ$}ZNz޿g>qE?!ebμd<19#/l"=)Gd6GpNT]äI͢~r`;k"2h?McS\f!o~aq<#iSG붚nG4 CGs#Y+!{,\reٯ1;G÷Yq-`J;խ[36no,紱 Mn2F ;n rb=; P@~>{۽l .Okk60oRxcnp#+ׯٻ%//2g))<'^LN:T/3;=/&_vymqbʪ'ҏBy{ M`t88$}VkXITލ5nBW%QV0 >.e1a=fQg͓Fl0d?nHu ws̪ұ#[ՓVȂV DUᙅ^>oq8bbAc}F8tXGJcgGptHƢc : +`פOETۄH1t^;F\RV~'mQn7?u? k+{tMgfJ)\mA+i4yT&V3Y[nz;Tś]t֪,$SJZZM|]^Mkz\Zb#n\Ͽw鿜jj7駟Jw$vNPubUP/xY^1xg'^ ߩ\U.[`/0ziWz|+țkAd]: D 8#@^DjcF /ͯq;M{^ʰ+w{gyu^ULT P*@(+@;X@*@T #W{Ha P*@G_'4Q[+ FS)^vpv$uude$*@wMP*@V`Ux;8KIP*@T ecT P*@@a~Bk4oᾊ|\G+ Mfli {{K  D1CF&l&W mqa)UGĂ+kGRz+ҮV}T "NWHOf#G#bckpJ0^\jMFu]3 8|kZ񠮍z&G6v 6Y>wo(G#5ҺҎwi]~~U8z-A:b%8RX{(`L΢~EA7}El%F@g;[k4׹G+H쳎ʠ%I8]\Xc,[JKys[3$I/j8v6u%kKhG4s$/rqMy2{"84қekԵgr< Bz`)e?9BTmbpY4Ҵ;pxY# gaӠA o6\3)U`Y[q,P.#8c#]Wʬr%-\t6$ z$䒞T]]bz[n`55@6 ͥu{Yy: OTOy]i ǃ*MU`,ЃV~*ZXTYŠ`8!4tE 8Bd!.M6iq=qK*@\mֲY5\56  {x(R*3H^ Bm|v>ɺ,9nanȁq)и,"QG%$ky[?[Yca֮\l`1{D\9«Qr6uHStzDm:-#එ,"fnDM[XGjP' L* fDl\O* k1,"fU$HPbb&̺mL#Gj#˪l%W)/v7ș u9ٜ}kvNŔĽ%}n,ZI{GL亪#mbRjYEMO/j*ia;yN=G9-=8~$bl"=)Gd6GpNT]äI͢~r`;k"2h?McS\f!o~aq<#iSG붚\ff!!bȚ(3zNu4k%sřKl=5f{=82n,VI`u#uƦ?q}Z6Mbs':ލYAN޳=YtR*/vgOw SP߶sWZ^xM[ om^n|5^x={W2E,%ċɉS\juc'ws[+V4-NLY>PWh72co;g֑ݥOvfkU82UȇTϊp+ѻgfLHrmÒ[=:9>)IJ}ލ5tC6E1cU$KƚDWs~̵hdRI }&Dѐ%EQ,qd2#]b_6ŚūeIr?rMItlk#ufWk+*^h+g% U8[#̇]Zs$ @ F߳߈2& }y`4bYY[z5 Ϫ'&y]`\7tVcU7 UG%)cUxfaW[%ξ8XX_#'%֑ҿ8k#:v#c 5)g>'6!G8&uL9׎W=Ԅմ/]b=Yǿxtf$0r Z=M#ͣH7}{dme1E/w֪,$SJZZc¬usr=k$-u>z:_SUUΩ <@LT /4V];5ځʅc ` ={ֻy]/*W{yW>+Py\g#rQh|b(Py`"R\mhwuW5~xi+U_uun2s,y iO}cIM[T Q/#s?:JrNT P*@T P*@T P*@T P*@T P*@T P*@T P*@T P*@T P*@T P*@T P*@T P*@+_NGcnIENDB`pastel-0.8.0/benchmarks/000077500000000000000000000000001370007461400151265ustar00rootroot00000000000000pastel-0.8.0/benchmarks/nesting_speed.rb000066400000000000000000000027401370007461400203050ustar00rootroot00000000000000require 'benchmark/ips' require_relative '../lib/pastel' pastel = Pastel.new Benchmark.ips do |bench| bench.config(time: 5, warmup: 2) bench.report('regular nesting') do pastel.red.on_green('Unicorns' + pastel.green.on_red('will ', 'dominate' + pastel.yellow('the world!'))) end bench.report('block nesting') do pastel.red.on_green('Unicorns') do green.on_red('will ', 'dominate') do yellow('the world!') end end end bench.compare! end # version 0.7.2 # # Warming up -------------------------------------- # regular nesting 1.682k i/100ms # block nesting 1.505k i/100ms # Calculating ------------------------------------- # regular nesting 17.442k (± 2.0%) i/s - 87.464k in 5.016577s # block nesting 15.503k (± 2.3%) i/s - 78.260k in 5.050593s # # Comparison: # regular nesting: 17442.0 i/s # block nesting: 15503.3 i/s - 1.13x slower # # version 0.6.0 # Calculating ------------------------------------- # regular nesting 1282 i/100ms # block nesting 1013 i/100ms # ------------------------------------------------- # regular nesting 13881.5 (±16.3%) i/s - 67946 in 5.043220s # block nesting 11411.6 (±25.4%) i/s - 53689 in 5.088911s # # Comparison: # regular nesting: 13881.5 i/s # block nesting: 11411.6 i/s - 1.22x slower # version 0.5.3 # regular nesting: 2800/s # block nesting: 2600/s pastel-0.8.0/benchmarks/speed.rb000066400000000000000000000033221370007461400165530ustar00rootroot00000000000000require 'benchmark/ips' require_relative '../lib/pastel' pastel = Pastel.new Benchmark.ips do |bench| bench.config(time: 5, warmup: 2) bench.report('color decorate') do pastel.decorate('string', :red, :on_green, :bold) end bench.report('dsl styling') do pastel.red.on_green.bold('string') end bench.compare! end # version 0.7.3 # # Warming up -------------------------------------- # color decorate 8.260k i/100ms # dsl styling 4.211k i/100ms # Calculating ------------------------------------- # color decorate 93.820k (± 2.3%) i/s - 470.820k in 5.021097s # dsl styling 44.655k (± 3.3%) i/s - 227.394k in 5.097981s # # Comparison: # color decorate: 93819.7 i/s # dsl styling: 44654.6 i/s - 2.10x slower # # version 0.6.0 # Calculating ------------------------------------- # color decorate 7346 i/100ms # dsl styling 3436 i/100ms # ------------------------------------------------- # color decorate 96062.1 (±7.9%) i/s - 484836 in 5.081126s # dsl styling 38761.1 (±13.9%) i/s - 192416 in 5.065053s # # Comparison: # color decorate: 96062.1 i/s # dsl styling: 38761.1 i/s - 2.48x slower # version 0.5.3 # Calculating ------------------------------------- # color decorate 1428 i/100ms # dsl styling 1174 i/100ms # ------------------------------------------------- # color decorate 16113.1 (±21.5%) i/s - 77112 in 5.054487s # dsl styling 12622.9 (±20.8%) i/s - 61048 in 5.076738s # # Comparison: # color decorate: 16113.1 i/s # dsl styling: 12622.9 i/s - 1.28x slower # pastel-0.8.0/examples/000077500000000000000000000000001370007461400146275ustar00rootroot00000000000000pastel-0.8.0/examples/palette.rb000066400000000000000000000024401370007461400166120ustar00rootroot00000000000000require_relative '../lib/pastel' pastel = Pastel.new puts pastel.bold('bold ') + ' ' + pastel.dim('dim ') + ' ' + pastel.italic('italic ') + ' ' + pastel.underline('underline') + ' ' + pastel.inverse('inverse ') + ' ' + pastel.strikethrough('strikethrough') puts pastel.red('red ') + ' ' + pastel.green('green ') + ' ' + pastel.yellow('yellow ') + ' ' + pastel.blue('blue ') + ' ' + pastel.magenta('magenta ') + ' ' + pastel.cyan('cyan ') + ' ' + pastel.white('white') puts pastel.bright_red('red ') + ' ' + pastel.bright_green('green ') + ' ' + pastel.bright_yellow('yellow ') + ' ' + pastel.bright_blue('blue ') + ' ' + pastel.bright_magenta('magenta ') + ' ' + pastel.bright_cyan('cyan ') + ' ' + pastel.bright_white('white') puts pastel.on_red('on_red') + ' ' + pastel.on_green('on_green') + ' ' + pastel.on_yellow('on_yellow') + ' ' + pastel.on_blue('on_blue') + ' ' + pastel.on_magenta('on_magenta') + ' ' + pastel.on_cyan('on_cyan') + ' ' + pastel.on_white('on_white') puts pastel.on_bright_red('on_red') + ' ' + pastel.on_bright_green('on_green') + ' ' + pastel.on_bright_yellow('on_yellow') + ' ' + pastel.on_bright_blue('on_blue') + ' ' + pastel.on_bright_magenta('on_magenta') + ' ' + pastel.on_bright_cyan('on_cyan') + ' ' + pastel.on_bright_white('on_white') pastel-0.8.0/lib/000077500000000000000000000000001370007461400135575ustar00rootroot00000000000000pastel-0.8.0/lib/pastel.rb000066400000000000000000000021601370007461400153730ustar00rootroot00000000000000# frozen_string_literal: true require "tty-color" require_relative "pastel/alias_importer" require_relative "pastel/color" require_relative "pastel/color_resolver" require_relative "pastel/delegator" require_relative "pastel/version" module Pastel # Raised when the style attribute is not supported InvalidAttributeNameError = Class.new(::ArgumentError) # Raised when the color alias is not supported InvalidAliasNameError = Class.new(::ArgumentError) # Create Pastel chainable API # # @example # pastel = Pastel.new enabled: true # # @param [Boolean] :enabled # whether or not to disable coloring # @param [Boolean] :eachline # whether or not to wrap eachline with separate coloring # # @return [Delegator] # # @api public def new(enabled: nil, eachline: false) if enabled.nil? enabled = (TTY::Color.windows? || TTY::Color.color?) end color = Color.new(enabled: enabled, eachline: eachline) importer = AliasImporter.new(color, ENV) importer.import resolver = ColorResolver.new(color) Delegator.wrap(resolver) end module_function :new end # Pastel pastel-0.8.0/lib/pastel/000077500000000000000000000000001370007461400150475ustar00rootroot00000000000000pastel-0.8.0/lib/pastel/alias_importer.rb000066400000000000000000000021611370007461400204060ustar00rootroot00000000000000# frozen_string_literal: true module Pastel # A class responsible for importing color aliases class AliasImporter # Create alias importer # # @example # importer = Pastel::AliasImporter.new(Pastel::Color.new, {}) # # @api public def initialize(color, env, output = $stderr) @color = color @env = env @output = output end # Import aliases from the environment # # @example # importer = Pastel::AliasImporter.new(Pastel::Color.new, {}) # importer.import # # @return [nil] # # @api public def import color_aliases = env["PASTEL_COLORS_ALIASES"] return unless color_aliases color_aliases.split(",").each do |color_alias| new_color, old_colors = color_alias.split("=") if !new_color || !old_colors output.puts "Bad color mapping `#{color_alias}`" else color.alias_color(new_color.to_sym, *old_colors.split(".").map(&:to_sym)) end end end protected attr_reader :color, :output, :env end # AliasImporter end # Pastel pastel-0.8.0/lib/pastel/ansi.rb000066400000000000000000000026131370007461400163300ustar00rootroot00000000000000# frozen_string_literal: true module Pastel # Mixin that provides ANSI codes module ANSI ATTRIBUTES = { clear: 0, reset: 0, bold: 1, dark: 2, dim: 2, italic: 3, underline: 4, underscore: 4, inverse: 7, hidden: 8, strikethrough: 9, black: 30, red: 31, green: 32, yellow: 33, blue: 34, magenta: 35, cyan: 36, white: 37, on_black: 40, on_red: 41, on_green: 42, on_yellow: 43, on_blue: 44, on_magenta: 45, on_cyan: 46, on_white: 47, bright_black: 90, bright_red: 91, bright_green: 92, bright_yellow: 93, bright_blue: 94, bright_magenta: 95, bright_cyan: 96, bright_white: 97, on_bright_black: 100, on_bright_red: 101, on_bright_green: 102, on_bright_yellow: 103, on_bright_blue: 104, on_bright_magenta: 105, on_bright_cyan: 106, on_bright_white: 107 }.freeze module_function def foreground?(code) [*(30..37), *(90..97)].include?(code.to_i) end def background?(code) [*(40..47), *(100..107)].include?(code.to_i) end def style?(code) (1..9).include?(code.to_i) end end # ANSI end # Pastel pastel-0.8.0/lib/pastel/color.rb000066400000000000000000000145011370007461400165130ustar00rootroot00000000000000# frozen_string_literal: true require_relative "ansi" module Pastel # A class responsible for coloring strings. class Color include ANSI # All color aliases ALIASES = {} # Match all color escape sequences ANSI_COLOR_REGEXP = /\x1b\[{1,2}[0-9;:?]*m/mo.freeze attr_reader :enabled alias enabled? enabled attr_reader :eachline # Initialize a Terminal Color # # @api public def initialize(enabled: nil, eachline: false) @enabled = enabled @eachline = eachline @cache = {} end # Disable coloring of this terminal session # # @api public def disable! @enabled = false end # Apply ANSI color to the given string. # # Wraps eachline with clear escape. # # @param [String] string # text to add ANSI strings # # @param [Array[Symbol]] colors # the color names # # @example # color.decorate "text", :yellow, :on_green, :underline # # @return [String] # the colored string # # @api public def decorate(string, *colors) return string if blank?(string) || !enabled || colors.empty? ansi_colors = lookup(*colors.dup.uniq) if eachline string.dup.split(eachline).map! do |line| apply_codes(line, ansi_colors) end.join(eachline) else apply_codes(string.dup, ansi_colors) end end # Apply escape codes to the string # # @param [String] string # the string to apply escapes to # @param [Strin] ansi_colors # the ansi colors to apply # # @return [String] # return the string surrounded by escape codes # # @api private def apply_codes(string, ansi_colors) "#{ansi_colors}#{string.gsub(/(\e\[0m)([^\e]+)$/, "\\1#{ansi_colors}\\2")}\e[0m" end # Reset sequence # # @api public def clear lookup(:clear) end # Strip ANSI color codes from a string. # # Only ANSI color codes are removed, not movement codes or # other escapes sequences are stripped. # # @param [Array[String]] strings # a string or array of strings to sanitize # # @example # strip("foo\e[1mbar\e[0m") # => "foobar" # # @return [String] # # @api public def strip(*strings) modified = strings.map { |string| string.dup.gsub(ANSI_COLOR_REGEXP, "") } modified.size == 1 ? modified[0] : modified end # Check if string has color escape codes # # @param [String] string # the string to check for color strings # # @return [Boolean] # true when string contains color codes, false otherwise # # @api public def colored?(string) !ANSI_COLOR_REGEXP.match(string).nil? end # Find the escape code for a given set of color attributes # # @example # color.lookup(:red, :on_green) # => "\e[31;42m" # # @param [Array[Symbol]] colors # the list of color name(s) to lookup # # @return [String] # the ANSI code(s) # # @raise [InvalidAttributeNameError] # exception raised for any invalid color name # # @api private def lookup(*colors) @cache.fetch(colors) do @cache[colors] = "\e[#{code(*colors).join(';')}m" end end # Return raw color code without embeding it into a string. # # @return [Array[String]] # ANSI escape codes # # @api public def code(*colors) attribute = [] colors.each do |color| value = ANSI::ATTRIBUTES[color] || ALIASES[color] if value attribute << value else validate(color) end end attribute end # Expose all ANSI color names and their codes # # @return [Hash[Symbol]] # # @api public def styles ANSI::ATTRIBUTES.merge(ALIASES) end # List all available style names # # @return [Array[Symbol]] # # @api public def style_names styles.keys end # Check if provided colors are known colors # # @param [Array[Symbol,String]] # the list of colors to check # # @example # valid?(:red) # => true # # @return [Boolean] # true if all colors are valid, false otherwise # # @api public def valid?(*colors) colors.all? { |color| style_names.include?(color.to_sym) } end # Define a new colors alias # # @param [String] alias_name # the colors alias to define # @param [Array[Symbol,String]] color # the colors the alias will correspond to # # @return [Array[String]] # the standard color values of the alias # # @api public def alias_color(alias_name, *colors) validate(*colors) if !(alias_name.to_s =~ /^[\w]+$/) raise InvalidAliasNameError, "Invalid alias name `#{alias_name}`" elsif ANSI::ATTRIBUTES[alias_name] raise InvalidAliasNameError, "Cannot alias standard color `#{alias_name}`" end ALIASES[alias_name.to_sym] = colors.map(&ANSI::ATTRIBUTES.method(:[])) colors end # Compare colors for equality of attributes # # @return [Boolean] # # @api public def eql?(other) instance_of?(other.class) && enabled.eql?(other.enabled) && eachline.eql?(other.eachline) end # Compare colors for equivalence of attributes # # @return [Boolean] # # @api public def ==(other) other.is_a?(self.class) && enabled == other.enabled && eachline == other.eachline end # Inspect this instance attributes # # @return [String] # # @api public def inspect "#<#{self.class.name} enabled=#{enabled.inspect} eachline=#{eachline.inspect}>" end # Hash for this instance and its attributes # # @return [Numeric] # # @api public def hash [self.class, enabled, eachline].hash end private # Check if value contains anything to style # # @return [Boolean] # # @api private def blank?(value) value.nil? || !value.respond_to?(:to_str) || value.to_s == "" end # @api private def validate(*colors) return if valid?(*colors) raise InvalidAttributeNameError, "Bad style or unintialized constant, " \ " valid styles are: #{style_names.join(', ')}." end end # Color end # TTY pastel-0.8.0/lib/pastel/color_parser.rb000066400000000000000000000056311370007461400200730ustar00rootroot00000000000000# frozen_string_literal: true require "strscan" require_relative "ansi" module Pastel # Responsible for parsing color symbols out of text with color escapes # # Used internally by {Color}. # # @api private class ColorParser include ANSI ESC = "\x1b" CSI = "\[" # Parse color escape sequences into a list of hashes # corresponding to the color attributes being set by these # sequences # # @example # parse("\e[32mfoo\e[0m") # # => [{foreground: :green, text: "foo"} # # @param [String] text # the text to parse for presence of color ansi codes # # @return [Array[Hash[Symbol,String]]] # # @api public def self.parse(text) scanner = StringScanner.new(text) state = {} result = [] ansi_stack = [] text_chunk = [] until scanner.eos? char = scanner.getch # match control if char == ESC && (delim = scanner.getch) == CSI if scanner.scan(/^0?m/) # reset unpack_ansi(ansi_stack) { |attr, name| state[attr] = name } ansi_stack = [] elsif scanner.scan(/^([1-9;:]+)m/) # ansi codes separated by text if !text_chunk.empty? && !ansi_stack.empty? unpack_ansi(ansi_stack) { |attr, name| state[attr] = name } end scanner[1].split(/:|;/).each do |code| ansi_stack << code end end if !text_chunk.empty? state[:text] = text_chunk.join result.push(state) state = {} text_chunk.clear end elsif char == ESC # broken escape text_chunk << char + delim.to_s else text_chunk << char end end if !text_chunk.empty? state[:text] = text_chunk.join end if !ansi_stack.empty? unpack_ansi(ansi_stack) { |attr, name| state[attr] = name} end if !state[:text].to_s.empty? result.push(state) end result end # Remove from current stack all ansi codes # # @param [Array[Integer]] ansi_stack # the stack with all the ansi codes # # @yield [Symbol, Symbol] attr, name # # @api private def self.unpack_ansi(ansi_stack) ansi_stack.each do |code| yield(attribute_name(code), color_name(code)) end end # Decide attribute name for ansi # # @param [Integer] ansi # the ansi escape code # # @return [Symbol] # # @api private def self.attribute_name(ansi) if ANSI.foreground?(ansi) :foreground elsif ANSI.background?(ansi) :background elsif ANSI.style?(ansi) :style end end # Convert ANSI code to color name # # @return [String] # # @api private def self.color_name(ansi_code) ATTRIBUTES.key(ansi_code.to_i) end end # Parser end # Pastel pastel-0.8.0/lib/pastel/color_resolver.rb000066400000000000000000000013251370007461400204340ustar00rootroot00000000000000# frozen_string_literal: true require_relative "detached" module Pastel # Contains logic for resolving styles applied to component # # Used internally by {Delegator}. # # @api private class ColorResolver # The color instance # @api public attr_reader :color # Initialize ColorResolver # # @param [Color] color # # @api private def initialize(color) @color = color end # Resolve uncolored string # # @api private def resolve(base, unprocessed_string) if base.to_a.last == :detach Detached.new(color, *base.to_a[0...-1]) else color.decorate(unprocessed_string, *base) end end end # ColorResolver end # Pastel pastel-0.8.0/lib/pastel/decorator_chain.rb000066400000000000000000000031661370007461400205260ustar00rootroot00000000000000# frozen_string_literal: true module Pastel # Collects a list of decorators for styling a string # # @api private class DecoratorChain include Enumerable # Create an empty decorator chain # # @return [DecoratorChain] # # @api public def self.empty @empty ||= self.new end # Create a decorator chain # # @api public def initialize(decorators = [].freeze) @decorators = decorators end # Add decorator # # @param [String] decorator # # @api public def add(decorator) if decorators.include?(decorator) self.class.new(decorators) else self.class.new(decorators + [decorator]) end end # Iterate over list of decorators # # @api public def each(&block) decorators.each(&block) end # Compare colors for equality of attributes # # @return [Boolean] # # @api public def eql?(other) instance_of?(other.class) && decorators.eql?(other.decorators) end # Compare colors for equivalence of attributes # # @return [Boolean] # # @api public def ==(other) other.is_a?(self.class) && decorators == other.decorators end # Inspect this instance attributes # # @return [String] # # @api public def inspect "#<#{self.class.name} decorators=#{decorators.inspect}>" end # Hash for this instance and its attributes # # @return [Numeric] # # @api public def hash [self.class, decorators].hash end protected attr_reader :decorators end # DecoratorChain end # Patel pastel-0.8.0/lib/pastel/delegator.rb000066400000000000000000000050041370007461400173410ustar00rootroot00000000000000# frozen_string_literal: true require "forwardable" require_relative "color_parser" require_relative "decorator_chain" module Pastel # Wrapes the {DecoratorChain} to allow for easy resolution # of string coloring. # # @api private class Delegator extend Forwardable def_delegators "@resolver.color", :valid?, :styles, :strip, :decorate, :enabled?, :colored?, :alias_color, :lookup def_delegators ColorParser, :parse alias undecorate parse # Wrap resolver and chain # # @api public def self.wrap(resolver, chain = DecoratorChain.empty) new(resolver, chain) end # Create Delegator # # Used internally by {Pastel} # # @param [ColorResolver] resolver # # @param [DecoratorChain] chain # # @api private def initialize(resolver, chain) @resolver = resolver @chain = chain end # Compare delegated objects for equality of attributes # # @return [Boolean] # # @api public def eql?(other) instance_of?(other.class) && chain.eql?(other.chain) end # Compare delegated objects for equivalence of attributes # # @return [Boolean] # # @api public def ==(other) other.is_a?(self.class) && chain == other.chain end # Object string representation # # @return [String] # # @api def inspect "#" end alias to_s inspect # Hash for this instance and its attributes # # @return [Numeric] # # @api public def hash [self.class, chain].hash end protected attr_reader :chain attr_reader :resolver # Handles color method calls # # @api private def method_missing(method_name, *args, &block) new_chain = chain.add(method_name) delegator = self.class.wrap(resolver, new_chain) if args.empty? && method_name.to_sym != :detach delegator else strings = args.dup strings << evaluate_block(&block) if block_given? resolver.resolve(new_chain, strings.join) end end # Check if color is valid # # @api private def respond_to_missing?(name, include_all = false) resolver.color.respond_to?(name, include_all) || resolver.color.valid?(name) || super end # Evaluate color block # # @api private def evaluate_block(&block) delegator = self.class.wrap(resolver) delegator.instance_eval(&block) end end # Delegator end # Pastel pastel-0.8.0/lib/pastel/detached.rb000066400000000000000000000032101370007461400171310ustar00rootroot00000000000000# frozen_string_literal: true module Pastel # A class representing detached color class Detached # Initialize a detached object # # @param [Pastel::Color] color # the color instance # @param [Array[Symbol]] styles # the styles to be applied # # @api private def initialize(color, *styles) @color = color @styles = styles.dup freeze end # Decorate the values corresponding to styles # # @example # Detached(Color.new, :red, :bold).call("hello") # # => "\e[31mhello\e[0m" # # @param [String] value # the stirng to decorate with styles # # @return [String] # # @api public def call(*args) value = args.join @color.decorate(value, *styles) end alias [] call # @api public def to_proc self end # Compare detached objects for equality of attributes # # @return [Boolean] # # @api public def eql?(other) instance_of?(other.class) && styles.eql?(other.styles) end # Compare detached objects for equivalence of attributes # # @return [Boolean] # # @api public def ==(other) other.is_a?(self.class) && styles == other.styles end # Inspect this instance attributes # # @return [String] # # @api public def inspect "#<#{self.class.name} styles=#{styles.inspect}>" end # Hash for this instance and its attributes # # @return [Numeric] # # @api public def hash [self.class, styles].hash end protected # @api private attr_reader :styles end # Detached end # Pastel pastel-0.8.0/lib/pastel/version.rb000066400000000000000000000001051370007461400170550ustar00rootroot00000000000000# frozen_string_literal: true module Pastel VERSION = "0.8.0" end pastel-0.8.0/pastel.gemspec000066400000000000000000000024551370007461400156540ustar00rootroot00000000000000require_relative "lib/pastel/version" Gem::Specification.new do |spec| spec.name = "pastel" spec.version = Pastel::VERSION spec.authors = ["Piotr Murach"] spec.email = ["piotr@piotrmurach.com"] spec.summary = %q{Terminal strings styling with intuitive and clean API.} spec.description = %q{Terminal strings styling with intuitive and clean API.} spec.homepage = "https://ttytoolkit.org" spec.license = "MIT" if spec.respond_to?(:metadata=) spec.metadata = { "allowed_push_host" => "https://rubygems.org", "bug_tracker_uri" => "https://github.com/piotrmurach/pastel/issues", "changelog_uri" => "https://github.com/piotrmurach/pastel/blob/master/CHANGELOG.md", "documentation_uri" => "https://www.rubydoc.info/gems/pastel", "homepage_uri" => spec.homepage, "source_code_uri" => "https://github.com/piotrmurach/pastel" } end spec.files = Dir["lib/**/*"] spec.extra_rdoc_files = ["README.md", "CHANGELOG.md", "LICENSE.txt"] spec.bindir = "exe" spec.require_paths = ["lib"] spec.required_ruby_version = Gem::Requirement.new(">= 2.0.0") spec.add_dependency "tty-color", "~> 0.5" spec.add_development_dependency "rake" spec.add_development_dependency "rspec", ">= 3.0" end pastel-0.8.0/spec/000077500000000000000000000000001370007461400137435ustar00rootroot00000000000000pastel-0.8.0/spec/spec_helper.rb000066400000000000000000000020021370007461400165530ustar00rootroot00000000000000# frozen_string_literal: true if ENV["COVERAGE"] || ENV["TRAVIS"] require "simplecov" require "coveralls" SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter ] SimpleCov.start do command_name "spec" add_filter "spec" end end require "pastel" RSpec.configure do |config| config.expect_with :rspec do |expectations| expectations.include_chain_clauses_in_custom_matcher_descriptions = true end config.mock_with :rspec do |mocks| mocks.verify_partial_doubles = true end # Limits the available syntax to the non-monkey patched syntax that is recommended. config.disable_monkey_patching! # This setting enables warnings. It's recommended, but in some cases may # be too noisy due to issues in dependencies. config.warnings = true if config.files_to_run.one? config.default_formatter = "doc" end config.profile_examples = 2 config.order = :random Kernel.srand config.seed end pastel-0.8.0/spec/unit/000077500000000000000000000000001370007461400147225ustar00rootroot00000000000000pastel-0.8.0/spec/unit/alias_color_spec.rb000066400000000000000000000013051370007461400205470ustar00rootroot00000000000000# coding: utf-8 RSpec.describe Pastel, '#alias_color' do subject(:pastel) { described_class.new(enabled: true) } it "aliases color" do pastel.alias_color(:funky, :red, :bold) expect(pastel.funky('unicorn')).to eq("\e[31;1municorn\e[0m") end it "aliases color and combines with regular ones" do pastel.alias_color(:funky, :red, :bold) expect(pastel.funky.on_green('unicorn')).to eq("\e[31;1;42municorn\e[0m") end it "reads aliases from the environment" do color_aliases = "funky=red" allow(ENV).to receive(:[]).with('PASTEL_COLORS_ALIASES'). and_return(color_aliases) described_class.new(enabled: true) expect(pastel.valid?(:funky)).to eq(true) end end pastel-0.8.0/spec/unit/alias_importer_spec.rb000066400000000000000000000016521370007461400212770ustar00rootroot00000000000000# coding: utf-8 RSpec.describe Pastel::AliasImporter, '#import' do let(:color) { spy(:color, alias_color: true) } let(:output) { StringIO.new } it "imports aliases from environment" do color_aliases = "funky=red.bold,base=bright_yellow" env = {'PASTEL_COLORS_ALIASES' => color_aliases} importer = described_class.new(color, env) importer.import expect(color).to have_received(:alias_color).with(:funky, :red, :bold) expect(color).to have_received(:alias_color).with(:base, :bright_yellow) end it "fails to import incorrectly formatted colors" do color_aliases = "funky red,base=bright_yellow" env = {'PASTEL_COLORS_ALIASES' => color_aliases} importer = described_class.new(color, env, output) output.rewind importer.import expect(output.string).to eq("Bad color mapping `funky red`\n") expect(color).to have_received(:alias_color).with(:base, :bright_yellow) end end pastel-0.8.0/spec/unit/color/000077500000000000000000000000001370007461400160405ustar00rootroot00000000000000pastel-0.8.0/spec/unit/color/alias_color_spec.rb000066400000000000000000000022351370007461400216700ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pastel::Color, ".alias_color" do subject(:color) { described_class.new(enabled: true) } it "aliases non existent color" do expect { color.alias_color(:funky, :unknown) }.to raise_error(Pastel::InvalidAttributeNameError) end it "aliases color with invalid name" do expect { color.alias_color("some name", :red) }.to raise_error(Pastel::InvalidAliasNameError, /Invalid alias name/) end it "aliases standard color" do expect { color.alias_color(:red, :red) }.to raise_error(Pastel::InvalidAliasNameError, /alias standard color/) end it "aliases color :red to :funky" do color.alias_color(:funky, :red, :bold) expect(color.valid?(:funky)).to eq(true) expect(color.code(:funky)).to eq([[31, 1]]) expect(color.lookup(:funky)).to eq("\e[31;1m") end it "has global aliases" do color_foo = described_class.new(enabled: true) color_bar = described_class.new(enabled: true) color_foo.alias_color(:foo, :red) color_bar.alias_color(:bar, :red) expect(color_foo.valid?(:foo)).to eq(true) expect(color_foo.valid?(:bar)).to eq(true) end end pastel-0.8.0/spec/unit/color/code_spec.rb000066400000000000000000000011611370007461400203100ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pastel::Color, "#code" do let(:string) { "This is a \e[1m\e[34mbold blue text\e[0m" } subject(:color) { described_class.new(enabled: true) } it "finds single code" do expect(color.code(:black)).to eq([30]) end it "finds more than one code" do expect(color.code(:black, :green)).to eq([30, 32]) end it "doesn't find code" do expect { color.code(:unkown) }.to raise_error(ArgumentError) end it "finds alias code" do color.alias_color(:funky, :red, :bold) expect(color.code(:funky)).to eq([color.code(:red) + color.code(:bold)]) end end pastel-0.8.0/spec/unit/color/colored_spec.rb000066400000000000000000000006231370007461400210270ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pastel::Color, "#colored?" do subject(:color) { described_class.new(enabled: true) } it "checks if string has color codes" do string = "foo\e[31mbar\e[0m" expect(color.colored?(string)).to eq(true) end it "checks that string doesn't contain color codes" do string = "foo\nbar" expect(color.colored?(string)).to eq(false) end end pastel-0.8.0/spec/unit/color/decorate_spec.rb000066400000000000000000000043641370007461400211740ustar00rootroot00000000000000# encoding: utf-8 RSpec.describe Pastel::Color, '.decorate' do let(:string) { 'string' } subject(:color) { described_class.new(enabled: true) } it "doesn't output styling when disabled" do color = described_class.new(enabled: false) expect(color.decorate('foo', :red)).to eq('foo') end it "doesn't apply styling to empty string" do expect(color.decorate('')).to eq('') end it "doesn't decorate without color" do expect(color.decorate(string)).to eq(string) end it 'applies green text to string' do expect(color.decorate(string, :green)).to eq("\e[32m#{string}\e[0m") end it 'applies red text background to string' do expect(color.decorate(string, :on_red)).to eq("\e[41m#{string}\e[0m") end it 'applies style and color to string' do expect(color.decorate(string, :bold, :green)).to eq("\e[1;32m#{string}\e[0m") end it 'applies style, color and background to string' do text = color.decorate(string, :bold, :green, :on_blue) expect(text).to eq("\e[1;32;44m#{string}\e[0m") end it "applies styles to nested text" do decorated = color.decorate(string + color.decorate(string, :red) + string, :green) expect(decorated).to eq("\e[32m#{string}\e[31m#{string}\e[0m\e[32m#{string}\e[0m") end it "decorates multiline string as regular by default" do string = "foo\nbar\nbaz" expect(color.decorate(string, :red)).to eq("\e[31mfoo\nbar\nbaz\e[0m") end it "allows to decorate each line separately" do string = "foo\nbar\nbaz" color = described_class.new(enabled: true, eachline: "\n") expect(color.decorate(string, :red)).to eq([ "\e[31mfoo\e[0m", "\e[31mbar\e[0m", "\e[31mbaz\e[0m" ].join("\n")) end it 'errors for unknown color' do expect { color.decorate(string, :crimson) }.to raise_error(Pastel::InvalidAttributeNameError) end it "doesn't decorate non-string instance" do expect(color.decorate({}, :red)).to eq({}) end it "doesn't decorate nil" do expect(color.decorate(nil, :red)).to eq(nil) end it "doesn't decorate zero length string" do expect(color.decorate('', :red)).to eq('') end it "doesn't decorate non-zero length string" do expect(color.decorate(' ', :red)).to eq("\e[31m \e[0m") end end pastel-0.8.0/spec/unit/color/equal_spec.rb000066400000000000000000000016151370007461400205110ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pastel::Color, "#==" do it "is true with equal attributes" do expect(Pastel::Color.new(enabled: false, eachline: "\n")). to eq(Pastel::Color.new(enabled: false, eachline: "\n")) end it "is true with equivalent attributes" do expect(Pastel::Color.new(enabled: false, eachline: "\n")). to eql(Pastel::Color.new(enabled: false, eachline: "\n")) end it "is false with different enabled attribute" do expect(Pastel::Color.new(enabled: true, eachline: "\n")). not_to eq(Pastel::Color.new(enabled: false, eachline: "\n")) end it "is false with different eachline attribute" do expect(Pastel::Color.new(enabled: false, eachline: "\n")). not_to eq(Pastel::Color.new(enabled: false, eachline: "\r\n")) end it "is false with non-color" do expect(Pastel::Color.new(enabled: true)).not_to eq(:other) end end pastel-0.8.0/spec/unit/color/inspect_spec.rb000066400000000000000000000004121370007461400210410ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pastel::Color, "#inspect" do it "inspects color instance attributes" do expect(described_class.new(enabled: false, eachline: "\n").inspect). to eq("#") end end pastel-0.8.0/spec/unit/color/lookup_spec.rb000066400000000000000000000007641370007461400207170ustar00rootroot00000000000000# encoding: utf-8 RSpec.describe Pastel::Color, '#lookup' do it "looksup colors" do color = described_class.new(enabled: true) expect(color.lookup(:red, :on_green, :bold)).to eq("\e[31;42;1m") end it "caches color lookups" do color = described_class.new(enabled: true) allow(color).to receive(:code).and_return([31]) color.lookup(:red, :on_green) color.lookup(:red, :on_green) color.lookup(:red, :on_green) expect(color).to have_received(:code).once end end pastel-0.8.0/spec/unit/color/new_spec.rb000066400000000000000000000006151370007461400201720ustar00rootroot00000000000000# encoding: utf-8 RSpec.describe Pastel::Color, '::new' do it "allows to disable coloring" do color = described_class.new(enabled: false) expect(color.enabled?).to eq(false) expect(color.decorate("Unicorn", :red)).to eq("Unicorn") end it "allows wrapping eachline in colors" do color = described_class.new(eachline: "\n") expect(color.eachline).to eq("\n") end end pastel-0.8.0/spec/unit/color/strip_spec.rb000066400000000000000000000035771370007461400205540ustar00rootroot00000000000000# encoding: utf-8 RSpec.describe Pastel::Color, '.strip' do subject(:color) { described_class.new(enabled: true) } it 'strips ansi color from string' do string = "This is a \e[1m\e[34mbold blue text\e[0m" expect(color.strip(string)).to eq('This is a bold blue text') end it "strips partial ansi color" do string = "foo\e[1mbar" expect(color.strip(string)).to eq('foobar') end it 'preserves movement characters' do # [176A - move cursor up n lines expect(color.strip("foo\e[176Abar")).to eq("foo\e[176Abar") end it 'strips reset/setfg/setbg/italics/strike/underline sequence' do string = "\x1b[0;33;49;3;9;4mfoo\x1b[0m" expect(color.strip(string)).to eq("foo") end it 'strips octal in encapsulating brackets' do string = "\[\033[01;32m\]u@h \[\033[01;34m\]W $ \[\033[00m\]" expect(color.strip(string)).to eq('[]u@h []W $ []') end it 'strips octal codes without brackets' do string = "\033[01;32mu@h \033[01;34mW $ \033[00m" expect(color.strip(string)).to eq('u@h W $ ') end it 'strips octal with multiple colors' do string = "\e[3;0;0;mfoo\e[8;50;0m" expect(color.strip(string)).to eq('foo') end it "strips multiple colors delimited by :" do string = "\e[31:44:4mfoo\e[0m" expect(color.strip(string)).to eq('foo') end it 'strips control codes' do string = "WARN. \x1b[1m&\x1b[0m ERR. \x1b[7m&\x1b[0m" expect(color.strip(string)).to eq('WARN. & ERR. &') end it 'strips escape bytes' do string = "This is a \e[1m\e[34mbold blue text\e[0m" expect(color.strip(string)).to eq("This is a bold blue text") end it 'strips "\e[m" no-color code produced by some standard programs ' do string = "\e[?1h\e=\r\e[33m438b059\e[m Change to bump version up\e[m\r\n\r\e[K\e[?1l\e>" expect(color.strip(string)).to eq("\e[?1h\e=\r438b059 Change to bump version up\r\n\r\e[K\e[?1l\e>") end end pastel-0.8.0/spec/unit/color/styles_spec.rb000066400000000000000000000003331370007461400207210ustar00rootroot00000000000000# coding: utf-8 RSpec.describe Pastel::Color, '#styles' do subject(:color) { described_class.new(enabled: true) } it "exposes all available style ANSI codes" do expect(color.styles[:red]).to eq(31) end end pastel-0.8.0/spec/unit/color/valid_spec.rb000066400000000000000000000007371370007461400205050ustar00rootroot00000000000000# encoding: utf-8 RSpec.describe Pastel::Color, '.valid?' do it "detects valid colors" do color = described_class.new expect(color.valid?(:red, :on_green, :bold)).to eq(true) end it "detects valid color aliases" do color = described_class.new color.alias_color(:funky, :red) expect(color.valid?(:funky)).to eq(true) end it "detects invalid color" do color = described_class.new expect(color.valid?(:red, :unknown)).to eq(false) end end pastel-0.8.0/spec/unit/color_parser_spec.rb000066400000000000000000000060641370007461400207610ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pastel::ColorParser, "::parse" do subject(:parser) { described_class } it "parses string with no color" do expect(parser.parse("foo")).to eq([{text: "foo"}]) end it "parses simple color" do expect(parser.parse("\e[32mfoo\e[0m")).to eq([ {foreground: :green, text: "foo"} ]) end it "parses simple color and style" do expect(parser.parse("\e[32;1mfoo\e[0m")).to eq([ {foreground: :green, style: :bold, text: "foo"} ]) end it "parses chained colors in shorthand syntax" do expect(parser.parse("\e[32;44mfoo\e[0m")).to eq([ {foreground: :green, background: :on_blue, text: "foo"} ]) end it "parses chained colors in regular syntax" do expect(parser.parse("\e[32m\e[44mfoo\e[0m")).to eq([ {foreground: :green, background: :on_blue, text: "foo"} ]) end it "parses many colors" do expect(parser.parse("\e[32mfoo\e[0m \e[31mbar\e[0m")).to eq([ {foreground: :green, text: "foo"}, {text: " "}, {foreground: :red, text: "bar"} ]) end it "parses nested colors with one reset" do expect(parser.parse("\e[32mfoo\e[31mbar\e[0m")).to eq([ {foreground: :green, text: "foo"}, {foreground: :red, text: "bar"} ]) end it "parses nested colors with two resets" do expect(parser.parse("\e[32mfoo\e[31mbar\e[0m\e[0m")).to eq([ {foreground: :green, text: "foo"}, {foreground: :red, text: "bar"} ]) end it "parses unrest color" do expect(parser.parse("\e[32mfoo")).to eq([ {foreground: :green, text: "foo"} ]) end it "parses malformed control sequence" do expect(parser.parse("\eA foo bar ESC\e")).to eq([ {text: "\eA foo bar ESC\e"} ]) end it "parses styles from parent text in nested text" do pastel = Pastel.new colored = pastel.bold.on_yellow("foo" + pastel.blue("bar") + "baz") parsed = parser.parse(colored) expect(parsed).to eq([ {text: "foo", style: :bold, background: :on_yellow}, {text: "bar", foreground: :blue, style: :bold, background: :on_yellow}, {text: "baz", style: :bold, background: :on_yellow} ]) end it "parses color when overriden in nested text" do pastel = Pastel.new colored = pastel.yellow.on_red("foo" + pastel.green.bold.on_cyan("bar") + "baz") parsed = parser.parse(colored) expect(parsed).to eq([ {text: "foo", foreground: :yellow, background: :on_red}, {text: "bar", foreground: :green, style: :bold, background: :on_cyan}, {text: "baz", foreground: :yellow, background: :on_red} ]) end it "parses colors nested with blocks" do pastel = Pastel.new colored = pastel.red.on_green("foo") do green.on_red("bar ", "baz") do yellow("qux") end end parsed = parser.parse(colored) expect(parsed).to eq([ {text: "foo", foreground: :red, background: :on_green}, {text: "bar baz", foreground: :green, background: :on_red}, {text: "qux", foreground: :yellow, background: :on_red} ]) end end pastel-0.8.0/spec/unit/decorate_dsl_spec.rb000066400000000000000000000056621370007461400207220ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pastel, "coloring dsl" do subject(:pastel) { described_class.new(enabled: true) } it "colors string" do expect(pastel.red("unicorn")).to eq("\e[31municorn\e[0m") end it "allows to specify variable number of arguments" do expect(pastel.red("unicorn", "running")).to eq("\e[31municornrunning\e[0m") end it "combines colored strings with regular ones" do expect(pastel.red("Unicorns") + " will rule " + pastel.green("the World!")). to eq("\e[31mUnicorns\e[0m will rule \e[32mthe World!\e[0m") end it "composes two color strings " do expect(pastel.red.on_green("unicorn")).to eq("\e[31;42municorn\e[0m") end it "composes three color strings" do expect(pastel.red.on_green.underline("unicorn")). to eq("\e[31;42;4municorn\e[0m") end it "combines colored composed strings with regular ones" do expect(pastel.red.on_green("Unicorns") + " will rule " + pastel.green.on_red("the World!")). to eq("\e[31;42mUnicorns\e[0m will rule \e[32;41mthe World!\e[0m") end it "allows one level nesting" do expect(pastel.red("Unicorn" + pastel.blue("rule!"))). to eq("\e[31mUnicorn\e[34mrule!\e[0m\e[0m") end it "allows to nest mixed styles" do expect(pastel.red("Unicorn" + pastel.green.on_yellow.underline("running") + "!")). to eq("\e[31mUnicorn\e[32;43;4mrunning\e[0m\e[31m!\e[0m") end it "allows for deep nesting" do expect(pastel.red("r" + pastel.green("g" + pastel.yellow("y") + "g") + "r")). to eq("\e[31mr\e[32mg\e[33my\e[0m\e[32mg\e[0m\e[31mr\e[0m") end it "allows for variable nested arguments" do expect(pastel.red("r", pastel.green("g"), "r")). to eq("\e[31mr\e[32mg\e[0m\e[31mr\e[0m") end it "nests color foreground & background" do expect(pastel.on_red("foo", pastel.green("bar"), "foo")). to eq("\e[41mfoo\e[32mbar\e[0m\e[41mfoo\e[0m") end it "allows to nest styles within block" do string = pastel.red.on_green("Unicorns" + pastel.green.on_red("will ", "dominate" + pastel.yellow("the world!"))) expect(pastel.red.on_green("Unicorns") do green.on_red("will ", "dominate") do yellow("the world!") end end).to eq(string) end it "doesn't decorate nil" do expect(pastel.red(nil)).to eq("") end it "doesn't apply styles to empty string" do expect(pastel.red("")).to eq("") end it "applies styles to empty with width more than 1" do expect(pastel.red(" ")).to eq("\e[31m \e[0m") end it "applies color only once" do expect(pastel.red.red.red("unicorn")).to eq(pastel.red("unicorn")) end it "raises error when chained with unrecognized color" do expect { pastel.unknown.on_red("unicorn") }.to raise_error(Pastel::InvalidAttributeNameError) end it "raises error when doesn't recognize color" do expect { pastel.unknown("unicorn") }.to raise_error(Pastel::InvalidAttributeNameError) end end pastel-0.8.0/spec/unit/decorator_chain_spec.rb000066400000000000000000000036711370007461400214140ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pastel::DecoratorChain do it "is enumerable" do expect(described_class.new).to be_a(Enumerable) end describe ".empty" do it "memoizes an empty chain" do expect(described_class.empty.object_id).to eq(described_class.empty.object_id) end end describe "#each" do it "yields each decorator" do first = double("first") second = double("second") chain = described_class.new.add(first).add(second) yielded = [] expect { chain.each { |decorator| yielded << decorator } }.to change { yielded }.from([]).to([first, second]) end end describe "#==" do it "is equivalent with the same decorator" do expect(described_class.new.add(:foo).add(:bar)). to eq(described_class.new.add(:foo).add(:bar)) end it "is not equivalent with different decorator" do expect(described_class.new.add(:foo).add(:bar)). not_to eq(described_class.new.add(:foo).add(:baz)) end it "is not equivalent to another type" do expect(described_class.new.add(:foo).add(:bar)). not_to eq(:other) end end describe "#eql?" do it "is equal with the same decorator" do expect(described_class.new.add(:foo).add(:bar)). to eql(described_class.new.add(:foo).add(:bar)) end it "is not equal with different decorator" do expect(described_class.new.add(:foo).add(:bar)). not_to eql(described_class.new.add(:foo).add(:baz)) end it "is not equal to another type" do expect(described_class.new.add(:foo).add(:bar)). not_to eql(:other) end end describe "#inspect" do it "displays object information" do expect(described_class.new.inspect).to eq("#") end end describe "#hash" do it "calculates object hash" do expect(described_class.new.hash).to be_a_kind_of(Numeric) end end end pastel-0.8.0/spec/unit/delegator_spec.rb000066400000000000000000000046011370007461400202300ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pastel::Delegator do it "returns delegator for color without argument" do pastel = Pastel.new(enabled: true) expect(pastel.red).to be_a(Pastel::Delegator) end describe "#==" do let(:resolver) { double(:resolver) } it "is equivalent with the same styles" do expect(described_class.new(resolver, [:red, :bold])). to eq(described_class.new(resolver, [:red, :bold])) end it "is not equivalent with different styles" do expect(described_class.new(resolver, [:red, :bold])). not_to eq(described_class.new(resolver, [:green, :bold])) end it "is not equivalent to another type" do expect(described_class.new(resolver, [:red, :bold])).not_to eq(:other) end end describe "#eql?" do let(:resolver) { double(:resolver) } it "is equal with the same styles" do expect(described_class.new(resolver, [:red, :bold])). to eql(described_class.new(resolver, [:red, :bold])) end it "is not equal with different styles" do expect(described_class.new(resolver, [:red, :bold])). not_to eql(described_class.new(resolver, [:green, :bold])) end it "is not equal to another type" do expect(described_class.new(resolver, [:red, :bold])).not_to eql(:other) end end describe "#inspect" do it "inspects delegator styles chain" do delegator = described_class.new(:resolver, [:red, :on_green]) allow(delegator).to receive(:styles).and_return(red: 31, on_green: 42) expect(delegator.inspect).to eq("#") end end describe "#hash" do it "calculates object hash" do expect(described_class.new(:resolver, [:red, :on_green]).hash). to be_a_kind_of(Numeric) end end describe "#respond_to_missing?" do context "for a method defined on" do it "returns true" do resolver = double(:resolver) chain = double(:chain) decorator = described_class.new(resolver, chain) expect(decorator.method(:styles)).not_to be_nil end end context "for an undefined method " do it "returns false" do resolver = double(:resolver, color: true) chain = double(:chain) decorator = described_class.new(resolver, chain) expect { decorator.method(:unknown) }.to raise_error(NameError) end end end end pastel-0.8.0/spec/unit/detach_spec.rb000066400000000000000000000053541370007461400175200ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pastel::Detached do let(:color) { spy(:color) } subject(:pastel) { Pastel.new(enabled: true) } describe "#detach" do it "creates detached instance" do error = pastel.red.bold.detach expect(error).to be_a(Pastel::Detached) end it "ensures instance is immutable" do error = pastel.red.detach expect(error.frozen?).to be(true) end it "detaches colors combination" do error = pastel.red.bold.detach expect(error.call("unicorn")).to eq("\e[31;1municorn\e[0m") expect(error.call("error")).to eq("\e[31;1merror\e[0m") end it "allows array like access" do error = pastel.red.bold.detach expect(error["unicorn"]).to eq("\e[31;1municorn\e[0m") end it "allows alternative call invocation" do error = pastel.red.bold.detach expect(error.("unicorn")).to eq("\e[31;1municorn\e[0m") end it "calls detached colors with no arguments" do warning = pastel.yellow.detach expect(warning.call("")).to eq("") end it "inspects detached colors" do warning = pastel.yellow.bold.detach expect(warning.inspect).to eq("#") end it "accepts multiple strings" do error = pastel.red.bold.detach expect(error.call("Unicorns", " run ", "wild")) .to eq("\e[31;1mUnicorns run wild\e[0m") end end describe "#==" do it "is equivalent with the same styles" do expect(described_class.new(color, :red, :bold)). to eq(described_class.new(color, :red, :bold)) end it "is not equivalent with different styles" do expect(described_class.new(color, :red, :bold)). not_to eq(described_class.new(color, :green, :bold)) end it "is not equivalent to another type" do expect(described_class.new(color, :red, :bold)).not_to eq(:other) end end describe "#eql?" do it "is equivalent with the same styles" do expect(described_class.new(color, :red, :bold)). to eql(described_class.new(color, :red, :bold)) end it "is not equivalent with different styles" do expect(described_class.new(color, :red, :bold)). not_to eql(described_class.new(color, :green, :bold)) end it "is not equivalent to another type" do expect(described_class.new(color, :red, :bold)).not_to eql(:other) end end describe "#inspect" do it "displays object information" do expect(described_class.new(color, :red, :bold).inspect). to eq("#") end end describe "#hash" do it "calculates object hash" do expect(described_class.new(color, :red, :bold).hash).to be_a_kind_of(Numeric) end end end pastel-0.8.0/spec/unit/new_spec.rb000066400000000000000000000033011370007461400170470ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pastel, "#new" do subject(:pastel) { described_class.new(enabled: true) } it { is_expected.to respond_to(:lookup) } it { is_expected.to respond_to(:decorate) } it { is_expected.to respond_to(:undecorate) } it { is_expected.to respond_to(:strip) } describe "#valid?" do it "when valid returns true" do expect(pastel.valid?(:red)).to eq(true) end it "returns false when invalid" do expect(pastel.valid?(:unknown)).to eq(false) end end describe "#colored?" do it "checks if string is colored" do expect(pastel.colored?("\e[31mfoo\e[0m")).to eq(true) end end describe "options passed in" do it "defaults enabled to color detection" do allow(TTY::Color).to receive(:color?).and_return(true) allow(TTY::Color).to receive(:windows?).and_return(false) pastel = described_class.new expect(pastel.enabled?).to eq(true) expect(TTY::Color).to have_received(:color?) end it "defaults to enabled on Windows" do allow(TTY::Color).to receive(:color?).and_return(false) allow(TTY::Color).to receive(:windows?).and_return(true) pastel = described_class.new expect(pastel.enabled?).to eq(true) expect(TTY::Color).to_not have_received(:color?) end it "sets enabled option" do pastel = described_class.new(enabled: false) expect(pastel.enabled?).to eq(false) expect(pastel.red("Unicorn", pastel.green("!"))).to eq("Unicorn!") end it "sets eachline option" do pastel = described_class.new(enabled: true, eachline: "\n") expect(pastel.red("foo\nbar")).to eq("\e[31mfoo\e[0m\n\e[31mbar\e[0m") end end end pastel-0.8.0/spec/unit/respond_to_spec.rb000066400000000000000000000007021370007461400204340ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pastel, ".respond_to?" do subject(:pastel) { described_class.new(enabled: true) } it "responds correctly to color method" do expect(pastel.respond_to?(:decorate)).to eq(true) end it "responds correctly to color property" do expect(pastel.respond_to?(:red)).to eq(true) end it "responds correctly to unkown method" do expect(pastel.respond_to?(:unknown)).to eq(false) end end pastel-0.8.0/spec/unit/undecorate_spec.rb000066400000000000000000000005401370007461400204110ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pastel, "#undecorate" do subject(:pastel) { described_class.new(enabled: true) } it "undecorates string detecting color escape codes" do string = pastel.red.on_green("foo") expect(pastel.undecorate(string)).to eq([ {foreground: :red, background: :on_green, text: "foo"} ]) end end pastel-0.8.0/tasks/000077500000000000000000000000001370007461400141365ustar00rootroot00000000000000pastel-0.8.0/tasks/console.rake000066400000000000000000000003221370007461400164410ustar00rootroot00000000000000# encoding: utf-8 desc 'Load gem inside irb console' task :console do require 'irb' require 'irb/completion' require File.join(__FILE__, '../../lib/pastel') ARGV.clear IRB.start end task c: :console pastel-0.8.0/tasks/coverage.rake000066400000000000000000000003221370007461400165720ustar00rootroot00000000000000# encoding: utf-8 desc 'Measure code coverage' task :coverage do begin original, ENV['COVERAGE'] = ENV['COVERAGE'], 'true' Rake::Task['spec'].invoke ensure ENV['COVERAGE'] = original end end pastel-0.8.0/tasks/spec.rake000066400000000000000000000012551370007461400157370ustar00rootroot00000000000000# encoding: utf-8 begin require 'rspec/core/rake_task' desc 'Run all specs' RSpec::Core::RakeTask.new(:spec) do |task| task.pattern = 'spec/{unit,integration}{,/*/**}/*_spec.rb' end namespace :spec do desc 'Run unit specs' RSpec::Core::RakeTask.new(:unit) do |task| task.pattern = 'spec/unit{,/*/**}/*_spec.rb' end desc 'Run integration specs' RSpec::Core::RakeTask.new(:integration) do |task| task.pattern = 'spec/integration{,/*/**}/*_spec.rb' end end rescue LoadError %w[spec spec:unit spec:integration].each do |name| task name do $stderr.puts "In order to run #{name}, do `gem install rspec`" end end end