verse 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c1d878680201432d1d1d982be8af68dd04f63a8e
4
+ data.tar.gz: 4b2a2ff0056cf352051636ab8d974afafcf7a097
5
+ SHA512:
6
+ metadata.gz: 75e7dafdb2d0264ffa9457d1fae6a42159cb8a5650fa5ba87b75371fb1686ebae80c006541df13d83f7f1a753503c9749c2d64ab9f6df1760128e3ece77a8523
7
+ data.tar.gz: 86da8ab2fc5690ffb84795b8bf892e51b9d36289cfd054546885926946b8c819c739813dac99e9d86559e2eafbb640c22440b7ab31fa0742728a1dae47bd623f
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --require spec_helper
3
+ --warning
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0.0
data/.travis.yml ADDED
@@ -0,0 +1,22 @@
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
+ - 2.2.0
9
+ - ruby-head
10
+ matrix:
11
+ include:
12
+ - rvm: jruby-19mode
13
+ - rvm: jruby-20mode
14
+ - rvm: jruby-21mode
15
+ - rvm: jruby-head
16
+ - rvm: rbx-2
17
+ allow_failures:
18
+ - rvm: ruby-head
19
+ - rvm: jruby-head
20
+ fast_finish: true
21
+ branches:
22
+ 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) 2015 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,106 @@
1
+ # Verse
2
+ [![Gem Version](https://badge.fury.io/rb/verse.png)][gem]
3
+ [![Build Status](https://secure.travis-ci.org/peter-murach/verse.png?branch=master)][travis]
4
+ [![Code Climate](https://codeclimate.com/github/peter-murach/verse.png)][codeclimate]
5
+ [![Coverage Status](https://coveralls.io/repos/peter-murach/verse/badge.png)][coverage]
6
+ [![Inline docs](http://inch-ci.org/github/peter-murach/verse.png)][inchpages]
7
+
8
+ [gem]: http://badge.fury.io/rb/verse
9
+ [travis]: http://travis-ci.org/peter-murach/verse
10
+ [codeclimate]: https://codeclimate.com/github/peter-murach/verse
11
+ [coverage]: https://coveralls.io/r/peter-murach/verse
12
+ [inchpages]: http://inch-ci.org/github/peter-murach/verse
13
+
14
+ > Text transformations such as truncation, wrapping, aligning, indentation and grouping of words.
15
+
16
+ ## Features
17
+
18
+ * No monkey-patching String class
19
+
20
+ ## Installation
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ ```ruby
25
+ gem 'verse'
26
+ ```
27
+
28
+ And then execute:
29
+
30
+ ```bash
31
+ $ bundle
32
+ ```
33
+
34
+ Or install it yourself as:
35
+
36
+ ```bash
37
+ $ gem install verse
38
+ ```
39
+
40
+ ## 1 Usage
41
+
42
+ ### 1.1 align
43
+
44
+ **Verse** allows you to align text:
45
+
46
+ ```ruby
47
+ alignment = Verse::Alignment.new "for there is no folly of the beast\n" +
48
+ " of the earth which\n" +
49
+ " is not infinitely\n" +
50
+ " outdone by the madness of men"
51
+ ```
52
+
53
+ Then using `right`, `left` or `center` methods and passing width you can align the text:
54
+
55
+ ```ruby
56
+ alignemnt.right(40) # =>
57
+ " for there is no folly of the beast\n" +
58
+ " of the earth which\n" +
59
+ " is not infinitely\n" +
60
+ " outdone by the madness of men"
61
+ ```
62
+
63
+ ### 1.2 truncate
64
+
65
+ Using **Verse** you can truncate a given text:
66
+
67
+ ```ruby
68
+ truncation = Verse::Truncation.new "for there is no folly of the beast of the earth " +
69
+ "which is not infinitely outdone by the madness of men"
70
+
71
+ ```
72
+
73
+ Then to shorten the text to given length call `truncate`:
74
+
75
+ ```ruby
76
+ truncation.truncate(20) # => "for there is no fol…"
77
+ ```
78
+
79
+ ### 1.3 wrap
80
+
81
+ ```ruby
82
+ wrapping = Verse::Wrapping.new "Think not, is my eleventh commandment; " +
83
+ "and sleep when you can, is my twelfth."
84
+
85
+ ```
86
+
87
+ Then to wrap the text to given length call `wrap`:
88
+
89
+ ```ruby
90
+ wrapping.wrap(30)
91
+ # => "Think not, is my eleventh"
92
+ "commandment; and sleep when"
93
+ "you can, is my twelfth."
94
+ ```
95
+
96
+ ## Contributing
97
+
98
+ 1. Fork it ( https://github.com/peter-murach/verse/fork )
99
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
100
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
101
+ 4. Push to the branch (`git push origin my-new-feature`)
102
+ 5. Create a new Pull Request
103
+
104
+ ## Copyright
105
+
106
+ Copyright (c) 2015 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/verse.rb ADDED
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+
3
+ require 'verse/alignment'
4
+ require 'verse/sanitizer'
5
+ require 'verse/truncation'
6
+ require 'verse/wrapping'
7
+ require 'verse/version'
8
+
9
+ module Verse
10
+ SPACE = ' '.freeze
11
+ NEWLINE = "\n".freeze
12
+ TAB = "\n".freeze
13
+
14
+ # Truncate a text at a given length
15
+ #
16
+ # @see Verse::Truncation#truncate
17
+ #
18
+ # @api public
19
+ def self.truncate(text, truncate_at, options = {})
20
+ Truncation.truncate(text, truncate_at, options)
21
+ end
22
+
23
+ # Wrap a text into lines at wrap length
24
+ #
25
+ # @see Verse::Wrapping#wrap
26
+ #
27
+ # @api public
28
+ def self.wrap(text, wrap_at, options = {})
29
+ Wrapping.wrap(text, wrap_at, options = {})
30
+ end
31
+ end # Verse
@@ -0,0 +1,94 @@
1
+ # coding: utf-8
2
+
3
+ module Verse
4
+ # A class responsible for text alignment
5
+ class Alignment
6
+
7
+ attr_reader :fill
8
+
9
+ attr_reader :direction
10
+
11
+ # Initialize an Alignment
12
+ #
13
+ # @api public
14
+ def initialize(text, options = {})
15
+ @text = text
16
+ @fill = options.fetch(:fill) { SPACE }
17
+ @direction = options.fetch(:direction) { :left }
18
+ end
19
+
20
+ # Aligns text to the left
21
+ #
22
+ # @return [String]
23
+ #
24
+ # @api public
25
+ def left(width, options = {})
26
+ align(width, :left, options)
27
+ end
28
+
29
+ # Centers text within the width
30
+ #
31
+ # @return [String]
32
+ #
33
+ # @api public
34
+ def center(width, options = {})
35
+ align(width, :center, options)
36
+ end
37
+
38
+ # Aligns text to the right
39
+ #
40
+ # @return [String]
41
+ #
42
+ # @api public
43
+ def right(width, options = {})
44
+ align(width, :right, options)
45
+ end
46
+
47
+ # Aligns text within the width.
48
+ #
49
+ # If the text is greater than the width then unmodified
50
+ # string is returned.
51
+ #
52
+ # @example
53
+ # alignment = Verse::Alignment.new "the madness of men"
54
+ #
55
+ # alignment.align(22, :left)
56
+ # # => "the madness of men "
57
+ #
58
+ # alignment.align(22, :center)
59
+ # # => " the madness of men "
60
+ #
61
+ # alignment.align(22, :right)
62
+ # # => " the madness of men"
63
+ #
64
+ # @api public
65
+ def align(width, direction = :left, options = {})
66
+ filler = options.fetch(:fill) { fill }
67
+ method = convert_to_method(direction)
68
+ process_lines { |line| line.send(method, width, filler) }
69
+ end
70
+
71
+ private
72
+
73
+ # @api private
74
+ def convert_to_method(direction)
75
+ case direction
76
+ when :left then :ljust
77
+ when :right then :rjust
78
+ when :center then :center
79
+ else
80
+ fail ArgumentError, "Unknown alignment `#{direction}`."
81
+ end
82
+ end
83
+
84
+ # @api private
85
+ def process_lines
86
+ lines = text.split(NEWLINE)
87
+ lines.reduce([]) do |aligned, line|
88
+ aligned << yield(line.strip)
89
+ end.join("\n")
90
+ end
91
+
92
+ attr_reader :text
93
+ end # Alignment
94
+ end # Verse
@@ -0,0 +1,18 @@
1
+ # coding: utf-8
2
+
3
+ module Verse
4
+ class Sanitizer
5
+ ANSI_MATCHER = /(\[)?\033(\[)?[;?\d]*[\dA-Za-z](\])?/.freeze
6
+
7
+ # Strip ANSI characters from the text
8
+ #
9
+ # @param [String] text
10
+ #
11
+ # @return [String]
12
+ #
13
+ # @api private
14
+ def sanitize(text)
15
+ text.gsub(ANSI_MATCHER, '')
16
+ end
17
+ end # Sanitizer
18
+ end # Verse
@@ -0,0 +1,78 @@
1
+ # coding: utf-8
2
+
3
+ module Verse
4
+ # A class responsible for text truncation operations
5
+ class Truncation
6
+ DEFAULT_TRAILING = '…'.freeze
7
+
8
+ DEFAULT_LENGTH = 30.freeze
9
+
10
+ attr_reader :separator
11
+
12
+ attr_reader :trailing
13
+
14
+ # Initialize a Truncation
15
+ #
16
+ # @param [String] text
17
+ # the text to be truncated
18
+ #
19
+ # @param [Hash] options
20
+ # @option options [Symbol] :separator the character for splitting words
21
+ # @option options [Symbol] :trailing the character for ending sentence
22
+ #
23
+ # @api public
24
+ def initialize(text, options = {})
25
+ @text = text
26
+ @sanitizer = Sanitizer.new
27
+ @separator = options.fetch(:separator) { nil }
28
+ @trailing = options.fetch(:trailing) { DEFAULT_TRAILING }
29
+ end
30
+
31
+ # Truncate a text at a given length
32
+ #
33
+ # @see Verse::Truncation#truncate
34
+ #
35
+ # @api public
36
+ def self.truncate(text, truncate_at, options = {})
37
+ new(text).truncate(truncate_at, options)
38
+ end
39
+
40
+ # Truncate a text at a given length (defualts to 30)
41
+ #
42
+ # @example
43
+ # truncation = Verse::Truncation.new
44
+ # "The sovereignest thing on earth is parmacetti for an inward bruise."
45
+ #
46
+ # truncation.truncate
47
+ # # => "The sovereignest thing on ear…"
48
+ #
49
+ # truncate(20)
50
+ # # => "The sovereignest th…"
51
+ #
52
+ # truncate(20, separator: ' ' )
53
+ # # => "The sovereignest…"
54
+ #
55
+ # truncate(40, trailing: '... (see more)' )
56
+ # # => "The sovereignest thing on... (see more)"
57
+ #
58
+ # @api public
59
+ def truncate(truncate_at = DEFAULT_LENGTH, options = {})
60
+ if text.length <= truncate_at.to_i || truncate_at.to_i.zero?
61
+ return text.dup
62
+ end
63
+ trail = options.fetch(:trailing) { trailing }
64
+ separation = options.fetch(:separator) { separator }
65
+
66
+ chars = @sanitizer.sanitize(text).chars.to_a
67
+ return chars.join if chars.length <= truncate_at
68
+ length_without_trailing = truncate_at - trail.chars.to_a.size
69
+ stop = chars[0, length_without_trailing].rindex(separation)
70
+
71
+ chars[0, stop || length_without_trailing].join + trail
72
+ end
73
+
74
+ private
75
+
76
+ attr_reader :text
77
+ end # Truncation
78
+ end # Verse
@@ -0,0 +1,5 @@
1
+ # coding: utf-8
2
+
3
+ module Verse
4
+ VERSION = '0.1.0'
5
+ end # Verse
@@ -0,0 +1,123 @@
1
+ # coding: utf-8
2
+
3
+ module Verse
4
+ # A class responsible for text wrapping
5
+ class Wrapping
6
+ DEFAULT_WIDTH = 80.freeze
7
+
8
+ attr_reader :indent
9
+
10
+ attr_reader :padding
11
+
12
+ # Initialize a Wrapping
13
+ #
14
+ # @param [String] text
15
+ # the text to be wrapped
16
+ #
17
+ # @param [Hash] options
18
+ # @option options [Symbol] :indent the indentation
19
+ # @option options [Symbol] :padding the desired spacing
20
+ #
21
+ # @api public
22
+ def initialize(text, options = {})
23
+ @text = text
24
+ @indent = options.fetch(:indent) { 0 }
25
+ @padding = options.fetch(:padding) { [] }
26
+ @line_width = options.fetch(:line_width) { DEFAULT_WIDTH }
27
+ @sanitizer = Sanitizer.new
28
+ end
29
+
30
+ # Wrap a text into lines no longer than wrap_at
31
+ #
32
+ # @api public
33
+ def self.wrap(text, wrap_at, options = {})
34
+ new(text).wrap(wrap_at, options)
35
+ end
36
+
37
+ # Wrap a text into lines no longer than wrap_at length.
38
+ # Preserves existing lines and existing word boundaries.
39
+ #
40
+ # @example
41
+ # wrapping = Verse::Wrapping.new "Some longish text"
42
+ #
43
+ # wrapping.wrap(8)
44
+ # # => "Some\nlongish\ntext"
45
+ #
46
+ # wrapping.wrap(8, indent: 4)
47
+ # # => > Some
48
+ # > longish
49
+ # > text
50
+ #
51
+ # @api public
52
+ def wrap(wrap_at = DEFAULT_WIDTH, options = {})
53
+ if text.length < wrap_at.to_i || wrap_at.to_i.zero?
54
+ return text.dup
55
+ end
56
+
57
+ indentation = options.fetch(:indent) { indent }
58
+ spacing = options.fetch(:padding) { padding }
59
+
60
+ text.split(NEWLINE, -1).map do |line|
61
+ pad_line(indent_line(wrap_line(line, wrap_at), indentation), spacing)
62
+ end * NEWLINE
63
+ end
64
+
65
+ private
66
+
67
+ # Calculate string length without color escapes
68
+ #
69
+ # @param [String] string
70
+ #
71
+ # @api private
72
+ def actual_length(string, at)
73
+ at + (string.length - @sanitizer.sanitize(string).length)
74
+ end
75
+
76
+ # Wrap line at given length
77
+ #
78
+ # @param [String] line
79
+ #
80
+ # @return [String]
81
+ #
82
+ # @api private
83
+ def wrap_line(line, at)
84
+ wrap_at = actual_length(line, at)
85
+ line.strip.gsub(/\n/, ' ').squeeze(' ')
86
+ .gsub(/(.{1,#{wrap_at}})(?:\s+|$\n?)|(.{1,#{wrap_at}})/, "\\1\\2\n")
87
+ .strip
88
+ end
89
+
90
+ # Indent string by given value
91
+ #
92
+ # @param [String] text
93
+ #
94
+ # @return [String]
95
+ #
96
+ # @api private
97
+ def indent_line(text, indent)
98
+ text.split(NEWLINE).each do |line|
99
+ line.insert(0, SPACE * indent)
100
+ end
101
+ end
102
+
103
+ # Add padding to each line in wrapped text
104
+ #
105
+ # @param [String] text
106
+ # the wrapped text
107
+ #
108
+ # @return [String]
109
+ #
110
+ # @api private
111
+ def pad_line(text, padding)
112
+ return text if text.empty? || padding.empty?
113
+
114
+ padding_left = SPACE * padding[3].to_i
115
+ padding_right = SPACE * padding[1].to_i
116
+ text.map! do |part|
117
+ part.insert(0, padding_left).insert(-1, padding_right)
118
+ end
119
+ end
120
+
121
+ attr_reader :text
122
+ end # Wrapping
123
+ end # Verse
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ if RUBY_VERSION > '1.9' and (ENV['COVERAGE'] || ENV['TRAVIS'])
4
+ require 'simplecov'
5
+ require 'coveralls'
6
+
7
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
8
+ SimpleCov::Formatter::HTMLFormatter,
9
+ Coveralls::SimpleCov::Formatter
10
+ ]
11
+
12
+ SimpleCov.start do
13
+ command_name 'spec'
14
+ add_filter 'spec'
15
+ end
16
+ end
17
+
18
+ require 'verse'
19
+
20
+ RSpec.configure do |config|
21
+ config.expect_with :rspec do |expectations|
22
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
23
+ end
24
+
25
+ config.mock_with :rspec do |mocks|
26
+ mocks.verify_partial_doubles = true
27
+ end
28
+
29
+ # Limits the available syntax to the non-monkey patched syntax that is recommended.
30
+ config.disable_monkey_patching!
31
+
32
+ # This setting enables warnings. It's recommended, but in some cases may
33
+ # be too noisy due to issues in dependencies.
34
+ config.warnings = true
35
+
36
+ if config.files_to_run.one?
37
+ config.default_formatter = 'doc'
38
+ end
39
+
40
+ config.profile_examples = 2
41
+
42
+ config.order = :random
43
+
44
+ Kernel.srand config.seed
45
+ end
46
+
47
+ def unindent(text)
48
+ text.gsub(/^[ \t]*/, '').chomp
49
+ end
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Verse::Alignment, '.align' do
6
+ it "centers line" do
7
+ text = "the madness of men"
8
+ alignment = Verse::Alignment.new(text)
9
+ expect(alignment.center(22)).to eq(" the madness of men ")
10
+ end
11
+
12
+ it "centers multiline text" do
13
+ text = "for there is no folly of the beast\n of the earth which\n is not infinitely\n outdone by the madness of men"
14
+ alignment = Verse::Alignment.new(text)
15
+ expect(alignment.center(40)).to eq([
16
+ " for there is no folly of the beast \n",
17
+ " of the earth which \n",
18
+ " is not infinitely \n",
19
+ " outdone by the madness of men "
20
+ ].join)
21
+ end
22
+
23
+ it "centers multiline text with fill of '*'" do
24
+ text = "for there is no folly of the beast\n of the earth which\n is not infinitely\n outdone by the madness of men"
25
+ alignment = Verse::Alignment.new(text, fill: '*')
26
+ expect(alignment.center(40)).to eq([
27
+ "***for there is no folly of the beast***\n",
28
+ "***********of the earth which***********\n",
29
+ "***********is not infinitely************\n",
30
+ "*****outdone by the madness of men******"
31
+ ].join)
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Verse::Alignment, '.left' do
6
+ it "aligns line to left" do
7
+ text = "the madness of men"
8
+ alignment = Verse::Alignment.new(text)
9
+ expect(alignment.left(22)).to eq("the madness of men ")
10
+ end
11
+
12
+ it "aligns multiline text to left" do
13
+ text = "for there is no folly of the beast\n of the earth which\n is not infinitely\n outdone by the madness of men"
14
+ alignment = Verse::Alignment.new(text)
15
+ expect(alignment.left(40)).to eq([
16
+ "for there is no folly of the beast \n",
17
+ "of the earth which \n",
18
+ "is not infinitely \n",
19
+ "outdone by the madness of men "
20
+ ].join)
21
+ end
22
+
23
+ it "centers multiline text with fill of '*'" do
24
+ text = "for there is no folly of the beast\n of the earth which\n is not infinitely\n outdone by the madness of men"
25
+ alignment = Verse::Alignment.new(text, fill: '*')
26
+ expect(alignment.left(40)).to eq([
27
+ "for there is no folly of the beast******\n",
28
+ "of the earth which**********************\n",
29
+ "is not infinitely***********************\n",
30
+ "outdone by the madness of men***********"
31
+ ].join)
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Verse::Alignment, '.right' do
6
+ it "aligns line to left" do
7
+ text = "the madness of men"
8
+ alignment = Verse::Alignment.new(text)
9
+ expect(alignment.right(22)).to eq(" the madness of men")
10
+ end
11
+
12
+ it "aligns multiline text to left" do
13
+ text = "for there is no folly of the beast\n of the earth which\n is not infinitely\n outdone by the madness of men"
14
+ alignment = Verse::Alignment.new(text)
15
+ expect(alignment.right(40)).to eq([
16
+ " for there is no folly of the beast\n",
17
+ " of the earth which\n",
18
+ " is not infinitely\n",
19
+ " outdone by the madness of men"
20
+ ].join)
21
+ end
22
+
23
+ it "centers multiline text with fill of '*'" do
24
+ text = "for there is no folly of the beast\n of the earth which\n is not infinitely\n outdone by the madness of men"
25
+ alignment = Verse::Alignment.new(text, fill: '*')
26
+ expect(alignment.right(40)).to eq([
27
+ "******for there is no folly of the beast\n",
28
+ "**********************of the earth which\n",
29
+ "***********************is not infinitely\n",
30
+ "***********outdone by the madness of men"
31
+ ].join)
32
+ end
33
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Verse::Sanitizer, '.sanitize' do
6
+ subject(:sanitizer) { described_class.new }
7
+
8
+ {
9
+ "\e[20h" => '',
10
+ "\e[?1h" => '',
11
+ "\e[20l" => '',
12
+ "\e[?9l" => '',
13
+ "\eO" => '',
14
+ "\e[m" => '',
15
+ "\e[0m" => '',
16
+ "\eA" => '',
17
+ "\e[0;33;49;3;9;4m\e[0m" => ''
18
+ }.each do |code, expected|
19
+ it "strips #{code} from string" do
20
+ expect(sanitizer.sanitize(code)).to eq(expected)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,10 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Verse, '#truncate' do
6
+ it "truncates text" do
7
+ text = 'ラドクリフ、マラソン五輪代表に1万m出場にも含み'
8
+ expect(Verse.truncate(text, 10)).to eq('ラドクリフ、マラソ…')
9
+ end
10
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Verse::Truncation, '#new' do
6
+ let(:text) { 'There go the ships; there is that Leviathan whom thou hast made to play therein.'}
7
+
8
+ it "defaults to no separator and unicode trailing" do
9
+ truncation = Verse::Truncation.new text
10
+ expect(truncation.separator).to eq(nil)
11
+ end
12
+
13
+ it "defaults to unicode trailing" do
14
+ truncation = Verse::Truncation.new text
15
+ expect(truncation.trailing).to eq('…')
16
+ end
17
+
18
+
19
+ it "allows to setup global separator value" do
20
+ truncation = Verse::Truncation.new text, separator: ' '
21
+ expect(truncation.separator).to eq(' ')
22
+ end
23
+
24
+ it "allows to setup global trailing value" do
25
+ truncation = Verse::Truncation.new text, trailing: '...'
26
+ expect(truncation.trailing).to eq('...')
27
+ end
28
+ end
@@ -0,0 +1,63 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Verse::Truncation, '.truncate' do
6
+ let(:text) { 'ラドクリフ、マラソン五輪代表に1万m出場にも含み' }
7
+
8
+ it "doesn't change text for 0 length" do
9
+ truncation = Verse::Truncation.new(text)
10
+ expect(truncation.truncate(0)).to eq(text)
11
+ end
12
+
13
+ it "doensn't change text for nil length" do
14
+ truncation = Verse::Truncation.new(text)
15
+ expect(truncation.truncate(nil)).to eq(text)
16
+ end
17
+
18
+ it "doesn't change text for equal length" do
19
+ truncation = Verse::Truncation.new(text)
20
+ expect(truncation.truncate(text.length)).to eq(text)
21
+ end
22
+
23
+ it 'truncates text' do
24
+ truncation = Verse::Truncation.new(text)
25
+ trailing = '…'
26
+ expect(truncation.truncate(12)).to eq("ラドクリフ、マラソン五#{trailing}")
27
+ end
28
+
29
+ it "doesn't truncate text when length exceeds content" do
30
+ truncation = Verse::Truncation.new(text)
31
+ expect(truncation.truncate(100)).to eq(text)
32
+ end
33
+
34
+ it 'truncates text with string separator' do
35
+ truncation = Verse::Truncation.new(text)
36
+ trailing = '…'
37
+ expect(truncation.truncate(12, separator: ' ')).to eq("ラドクリフ、マラソン五#{trailing}")
38
+ end
39
+
40
+ it 'truncates text with regex separator' do
41
+ truncation = Verse::Truncation.new(text)
42
+ trailing = '…'
43
+ expect(truncation.truncate(12, separator: /\s/)).to eq("ラドクリフ、マラソン五#{trailing}")
44
+ end
45
+
46
+ it 'truncates text with custom trailing' do
47
+ truncation = Verse::Truncation.new(text)
48
+ trailing = '... (see more)'
49
+ expect(truncation.truncate(20, trailing: trailing)).to eq("ラドクリフ、#{trailing}")
50
+ end
51
+
52
+ it 'correctly truncates with ANSI characters' do
53
+ text = "This is a \e[1m\e[34mbold blue text\e[0m"
54
+ truncation = Verse::Truncation.new(text)
55
+ expect(truncation.truncate).to eq 'This is a bold blue text'
56
+ end
57
+
58
+ it "finishes on word boundary" do
59
+ text = "for there is no folly of the beast of the earth"
60
+ truncation = Verse::Truncation.new(text)
61
+ expect(truncation.truncate(20, separator: ' ')).to eq('for there is no…')
62
+ end
63
+ end
@@ -0,0 +1,10 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Verse, '#wrap' do
6
+ it "wraps text" do
7
+ text = 'ラドクリフ、マラソン五輪代表に1万m出場にも含み'
8
+ expect(Verse.wrap(text, 8)).to eql("ラドクリフ、マラ\nソン五輪代表に1\n万m出場にも含み")
9
+ end
10
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Verse::Wrapping, '#new' do
6
+ let(:text) { "There go the ships; there is that Leviathan whom thou hast made to play therein."}
7
+
8
+ it "defaults indentation to 0" do
9
+ wrapping = Verse::Wrapping.new(text)
10
+ expect(wrapping.indent).to eq(0)
11
+ end
12
+
13
+ it "defaults paddnig to empty array" do
14
+ wrapping = Verse::Wrapping.new(text)
15
+ expect(wrapping.padding).to eq([])
16
+ end
17
+
18
+ it "allows to set global indenation" do
19
+ wrapping = Verse::Wrapping.new(text, indent: 5)
20
+ expect(wrapping.indent).to eq(5)
21
+ end
22
+
23
+ it "allows to set global padding" do
24
+ wrapping = Verse::Wrapping.new(text, padding: [1,2,3,4])
25
+ expect(wrapping.padding).to eq([1,2,3,4])
26
+ end
27
+ end
@@ -0,0 +1,88 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Verse::Wrapping, '.wrap' do
6
+ context 'when unicode' do
7
+ let(:text) { 'ラドクリフ、マラソン五輪代表に1万m出場にも含み' }
8
+
9
+ it "doesn't wrap at zero length" do
10
+ wrapping = Verse::Wrapping.new(text)
11
+ expect(wrapping.wrap(0)).to eq(text)
12
+ end
13
+
14
+ it "doesn't wrap at nil length" do
15
+ wrapping = Verse::Wrapping.new(text)
16
+ expect(wrapping.wrap(nil)).to eq(text)
17
+ end
18
+
19
+ it 'wraps at 8 characters' do
20
+ wrapping = Verse::Wrapping.new(text)
21
+ expect(wrapping.wrap(8)).to eq("ラドクリフ、マラ\nソン五輪代表に1\n万m出場にも含み")
22
+ end
23
+
24
+ it "doesn't wrap at length exceeding content length" do
25
+ wrapping = Verse::Wrapping.new(text)
26
+ expect(wrapping.wrap(100)).to eq(text)
27
+ end
28
+ end
29
+
30
+ context 'when long text' do
31
+ it "wraps long text at 45 characters" do
32
+ text =
33
+ "What of it, if some old hunks of a sea-captain orders me to get a broom
34
+ and sweep down the decks? What does that indignity amount to, weighed,
35
+ I mean, in the scales of the New Testament? Do you think the archangel
36
+ Gabriel thinks anything the less of me, because I promptly and
37
+ respectfully obey that old hunks in that particular instance? Who ain't
38
+ a slave? Tell me that. Well, then, however the old sea-captains may
39
+ order me about--however they may thump and punch me about, I have the
40
+ satisfaction of knowing that it is all right;
41
+ "
42
+ wrapping = Verse::Wrapping.new(text)
43
+ expect(wrapping.wrap(45)).to eq unindent <<-EOS
44
+ What of it, if some old hunks of a\n sea-captain orders me to get a broom
45
+ and sweep down the decks? What does that\n indignity amount to, weighed,
46
+ I mean, in the scales of the New Testament?\n Do you think the archangel
47
+ Gabriel thinks anything the less of me,\n because I promptly and
48
+ respectfully obey that old hunks in that\nparticular instance? Who ain't
49
+ a slave? Tell me that. Well, then, however\n the old sea-captains may
50
+ order me about--however they may thump and\n punch me about, I have the
51
+ satisfaction of knowing that it is all right;\n
52
+ EOS
53
+ end
54
+ end
55
+
56
+ context 'when indented' do
57
+ let(:length) { 8 }
58
+ let(:indent) { 4 }
59
+
60
+ it "wraps with indentation" do
61
+ text = 'ラドクリフ、マラソン五輪代表に1万m出場にも含み'
62
+ wrapping = Verse::Wrapping.new(text)
63
+ expect(wrapping.wrap(8, indent: 4)).to eq(" ラドクリフ、マラ\n ソン五輪代表に1\n 万m出場にも含み")
64
+ end
65
+ end
66
+
67
+ context 'with ansi colors' do
68
+ it "wraps text with ANSII codes" do
69
+ text = "\[\033[01;32m\]Hey have\[\033[01;34m\]some cake\[\033[00m\]"
70
+ wrapping = Verse::Wrapping.new(text)
71
+ expect(wrapping.wrap(8)).to eq("\[\033[01;32m\]Hey have\[\033[01;34m\]some\ncake\[\033[00m\]")
72
+ end
73
+ end
74
+
75
+ context 'with newlines' do
76
+ it "preserves newlines for both prefix and postfix" do
77
+ text = "\n\nラドクリフ、マラソン五輪代表に1万m出場にも含み\n\n\n"
78
+ wrapping = Verse::Wrapping.new(text)
79
+ expect(wrapping.wrap(10)).to eq("\n\nラドクリフ、マラソン\n五輪代表に1万m出場\nにも含み\n\n\n")
80
+ end
81
+
82
+ it "preserves newlines with padding" do
83
+ text = "\n\nラドクリフ、マラソン五輪代表に1万m出場にも含み\n\n"
84
+ wrapping = Verse::Wrapping.new(text)
85
+ expect(wrapping.wrap(10, padding: [1,2,3,4])).to eq("\n\n ラドクリフ、マラソン \n 五輪代表に1万m出場 \n にも含み \n\n")
86
+ end
87
+ end
88
+ 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/verse')
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
data/verse.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'verse/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "verse"
8
+ spec.version = Verse::VERSION
9
+ spec.authors = ["Piotr Murach"]
10
+ spec.email = [""]
11
+ spec.summary = %q{}
12
+ spec.description = %q{}
13
+ spec.homepage = ""
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{^(spec)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.5'
22
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: verse
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: 2015-02-07 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.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ description: ''
28
+ email:
29
+ - ''
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - .gitignore
35
+ - .rspec
36
+ - .ruby-version
37
+ - .travis.yml
38
+ - Gemfile
39
+ - LICENSE.txt
40
+ - README.md
41
+ - Rakefile
42
+ - lib/verse.rb
43
+ - lib/verse/alignment.rb
44
+ - lib/verse/sanitizer.rb
45
+ - lib/verse/truncation.rb
46
+ - lib/verse/version.rb
47
+ - lib/verse/wrapping.rb
48
+ - spec/spec_helper.rb
49
+ - spec/unit/alignment/align_spec.rb
50
+ - spec/unit/alignment/left_spec.rb
51
+ - spec/unit/alignment/right_spec.rb
52
+ - spec/unit/sanitizer/sanitize_spec.rb
53
+ - spec/unit/truncate_spec.rb
54
+ - spec/unit/truncation/new_spec.rb
55
+ - spec/unit/truncation/truncate_spec.rb
56
+ - spec/unit/wrap_spec.rb
57
+ - spec/unit/wrapping/new_spec.rb
58
+ - spec/unit/wrapping/wrap_spec.rb
59
+ - tasks/console.rake
60
+ - tasks/coverage.rake
61
+ - tasks/spec.rake
62
+ - verse.gemspec
63
+ homepage: ''
64
+ licenses:
65
+ - MIT
66
+ metadata: {}
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubyforge_project:
83
+ rubygems_version: 2.0.3
84
+ signing_key:
85
+ specification_version: 4
86
+ summary: ''
87
+ test_files:
88
+ - spec/spec_helper.rb
89
+ - spec/unit/alignment/align_spec.rb
90
+ - spec/unit/alignment/left_spec.rb
91
+ - spec/unit/alignment/right_spec.rb
92
+ - spec/unit/sanitizer/sanitize_spec.rb
93
+ - spec/unit/truncate_spec.rb
94
+ - spec/unit/truncation/new_spec.rb
95
+ - spec/unit/truncation/truncate_spec.rb
96
+ - spec/unit/wrap_spec.rb
97
+ - spec/unit/wrapping/new_spec.rb
98
+ - spec/unit/wrapping/wrap_spec.rb
99
+ has_rdoc: