verse 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 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: