strings-truncation 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
+ SHA256:
3
+ metadata.gz: 3837ca8b06eb6173b46ec33382369c9cb6f1f1a78515cf318658dfdf2f3dbce1
4
+ data.tar.gz: 49be33a7cc3f22afbf77c2f168704f46234b5312ada53a4aefcdb4d315143e14
5
+ SHA512:
6
+ metadata.gz: 6308a3c890c3fd4f603399eb27cbe7faba13dbbc318497a3974d2db31ea3ed2827056162fb96eec522fc4dcbf2cd7b3e1eb84293500a041a8bcdbf349a554a9e
7
+ data.tar.gz: abf85e81c7fff5e9d0036c9e039a5880de95fcf9fdceef2d24abbeb908be8d6e07fdd09010d2e1a37e95e2b28395ccea7791d6c456c1772302e0fd62d5ebd9e0
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # Change log
2
+
3
+ ## [v0.1.0] - 2020-02-23
4
+
5
+ * Initial implementation and release
6
+
7
+ [v0.1.0]: https://github.com/piotrmurach/strings-truncation/compare/efb1d50...v0.1.0
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Piotr Murach
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,247 @@
1
+ <div align="center">
2
+ <img width="225" src="https://github.com/piotrmurach/strings/blob/master/assets/strings_logo.png" alt="strings logo" />
3
+ </div>
4
+
5
+ # Strings::Truncation
6
+
7
+ [![Gem Version](https://badge.fury.io/rb/strings-truncation.svg)][gem]
8
+ [![Actions CI](https://github.com/piotrmurach/strings-truncation/workflows/CI/badge.svg?branch=master)][gh_actions_ci]
9
+ [![Build status](https://ci.appveyor.com/api/projects/status/s8y94c4tvi8mgrh2?svg=true)][appveyor]
10
+ [![Maintainability](https://api.codeclimate.com/v1/badges/f7ecb5bf87696e522ccb/maintainability)][codeclimate]
11
+ [![Coverage Status](https://coveralls.io/repos/github/piotrmurach/strings-truncation/badge.svg?branch=master)][coverage]
12
+ [![Inline docs](http://inch-ci.org/github/piotrmurach/strings-truncation.svg?branch=master)][inchpages]
13
+
14
+ [gem]: http://badge.fury.io/rb/strings-truncation
15
+ [gh_actions_ci]: https://github.com/piotrmurach/strings-truncation/actions?query=workflow%3ACI
16
+ [appveyor]: https://ci.appveyor.com/project/piotrmurach/strings-truncation
17
+ [codeclimate]: https://codeclimate.com/github/piotrmurach/strings-truncation/maintainability
18
+ [coverage]: https://coveralls.io/github/piotrmurach/strings-truncation?branch=master
19
+ [inchpages]: http://inch-ci.org/github/piotrmurach/strings-truncation
20
+
21
+ > Truncate strings with fullwidth characters and ANSI codes.
22
+
23
+ ## Features
24
+
25
+ * No monkey-patching String class
26
+ * Omit text from the start, middle, end or both ends
27
+ * Account for fullwidth characters in encodings such as UTF-8, EUC-JP
28
+ * Shorten text without whitespaces between words (Chinese, Japanese, Korean etc)
29
+ * Preserve ANSI escape codes
30
+
31
+ ## Contents
32
+
33
+ * [1. Usage](#1-usage)
34
+ * [2. API](#2-api)
35
+ * [2.1 configure](#21-configure)
36
+ * [2.2 truncate](#22-truncate)
37
+ * [3. Extending String class](#3-extending-string-class)
38
+
39
+ ## Installation
40
+
41
+ Add this line to your application's Gemfile:
42
+
43
+ ```ruby
44
+ gem "strings-truncation"
45
+ ```
46
+
47
+ And then execute:
48
+
49
+ $ bundle
50
+
51
+ Or install it yourself as:
52
+
53
+ $ gem install strings-truncation
54
+
55
+ ## 1. Usage
56
+
57
+ Use `truncate` to shorten string to 30 characters by default:
58
+
59
+ ```ruby
60
+ strings = Strings::Truncation.new
61
+ strings.truncate("I try all things, I achieve what I can.")
62
+ # => "I try all things, I achieve w…"
63
+ ```
64
+
65
+ As a convenience, you can call `truncate` method directly on a class:
66
+
67
+ ```ruby
68
+ Strings::Truncation.truncate("I try all things, I achieve what I can.")
69
+ # => "I try all things, I achieve w…"
70
+ ```
71
+
72
+ To change the default truncation length, pass an integer as a second argument:
73
+
74
+ ```ruby
75
+ strings.truncate("I try all things, I achieve what I can.", 15)
76
+ # => "I try all thin…"
77
+ ```
78
+
79
+ Or if you want to be more explicit and flexible use `:length` keyword:
80
+
81
+ ```ruby
82
+ strings.truncate("I try all things, I achieve what I can.", length: 15)
83
+ # => "I try all thin…"
84
+ ```
85
+
86
+ You can specify custom omission string in place of default `…`:
87
+
88
+ ```ruby
89
+ strings.truncate("I try all things, I achieve what I can.", omission: "...")
90
+ # => "I try all things, I achieve..."
91
+ ```
92
+
93
+ If you wish to truncate preserving words use a string or regexp as a separator:
94
+
95
+ ```ruby
96
+ strings.truncate("I try all things, I achieve what I can.", separator: /\s/)
97
+ # => "I try all things, I achieve…"
98
+ ```
99
+
100
+ You can omit text from the `start`, `middle`, `end` or both `ends`:
101
+
102
+ ```ruby
103
+ strings.truncate("I try all things, I achieve what I can", position: :middle)
104
+ # => "I try all thing…ve what I can."
105
+ ```
106
+
107
+ You can truncate text with fullwidth characters (Chinese, Japanese, Korean etc):
108
+
109
+ ```ruby
110
+ strings.truncate("おはようございます", 8)
111
+ # => "おはよ…"
112
+ ```
113
+
114
+ As well as truncate text that contains ANSI escape codes:
115
+
116
+ ```ruby
117
+ strings.truncate("\e[34mI try all things, I achieve what I can\e[0m", 18)
118
+ # => "\e[34mI try all things,\e[0m…"
119
+ ```
120
+
121
+ ## 2. API
122
+
123
+ ### 2.1 configure
124
+
125
+ To change default configuration settings at initialization use keyword arguments.
126
+
127
+ For example, to omit text from the start and separate on a whitespace character do:
128
+
129
+ ```ruby
130
+ strings = Strings::Truncation.new(position: :start, separator: /\s/)
131
+ ```
132
+
133
+ After initialization, you can use `configure` to change settings inside a block:
134
+
135
+ ```ruby
136
+ strings.configure do |config|
137
+ config.length 25
138
+ config.omission "[...]"
139
+ config.position :start
140
+ config.separator /\s/
141
+ end
142
+ ```
143
+
144
+ Alternatively, you can also use `configure` with keyword arguments:
145
+
146
+ ```ruby
147
+ strings.configure(position: :start, separator: /\s/)
148
+ ```
149
+
150
+ ### 2.2 truncate
151
+
152
+ By default a string is truncated from the end to maximum length of `30` display columns.
153
+
154
+ ```ruby
155
+ strings.truncate("I try all things, I achieve what I can.")
156
+ # => "I try all things, I achieve w…"
157
+ ```
158
+
159
+ To change the default truncation length, pass an integer as a second argument:
160
+
161
+ ```ruby
162
+ strings.truncate("I try all things, I achieve what I can.", 15)
163
+ # => "I try all thin…"
164
+ ```
165
+
166
+ Or use `:length` keyword to be more explicit:
167
+
168
+ ```ruby
169
+ strings.truncate("I try all things, I achieve what I can.", length: 15)
170
+ # => "I try all thin…"
171
+ ```
172
+
173
+ The default `…` omission character can be replaced using `:omission`:
174
+
175
+ ```ruby
176
+ strings.truncate("I try all things, I achieve what I can.", omission: "...")
177
+ # => "I try all things, I achieve..."
178
+ ```
179
+
180
+ You can omit text from the `start`, `middle`, `end` or both `ends` by specifying `:position`:
181
+
182
+ ```ruby
183
+ strings.truncate("I try all things, I achieve what I can", position: :start)
184
+ # => "…things, I achieve what I can."
185
+
186
+ strings.truncate("I try all things, I achieve what I can", position: :middle)
187
+ # => "I try all thing…ve what I can."
188
+
189
+ strings.truncate("I try all things, I achieve what I can", position: :ends)
190
+ # => "… all things, I achieve what …"
191
+ ```
192
+
193
+ To truncate based on custom character(s) use `:separator` that accepts a string or regular expression:
194
+
195
+ ```ruby
196
+ strings.truncate("I try all things, I achieve what I can.", separator: /\s/)
197
+ => "I try all things, I achieve…"
198
+ ```
199
+
200
+ You can combine all settings to achieve desired result:
201
+
202
+ ```ruby
203
+ strings.truncate("I try all things, I achieve what I can.", length: 20,
204
+ omission: "...", position: :ends, separator: /\s/)
205
+ # => "...I achieve what..."
206
+ ```
207
+
208
+ ## 3. Extending String class
209
+
210
+ Though it is highly discouraged to pollute core Ruby classes, you can add the required methods to String class by using refinements.
211
+
212
+ To include all the **Strings::Truncation** methods, you can load extensions like so:
213
+
214
+ ```ruby
215
+ require "strings/truncation/extensions"
216
+
217
+ using Strings::Truncation::Extensions
218
+ ```
219
+
220
+ And then call `truncate` directly on any string:
221
+
222
+ ```ruby
223
+ "I try all things, I achieve what I can.".truncate(20, separator: " ")
224
+ # => "I try all things, I…"
225
+ ```
226
+
227
+ ## Development
228
+
229
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
230
+
231
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
232
+
233
+ ## Contributing
234
+
235
+ Bug reports and pull requests are welcome on GitHub at https://github.com/piotrmurach/strings-truncation. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
236
+
237
+ ## License
238
+
239
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
240
+
241
+ ## Code of Conduct
242
+
243
+ Everyone interacting in the Strings::Truncation project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/piotrmurach/strings-truncation/blob/master/CODE_OF_CONDUCT.md).
244
+
245
+ ## Copyright
246
+
247
+ Copyright (c) 2019 Piotr Murach. See LICENSE for further details.
@@ -0,0 +1 @@
1
+ require "strings/truncation"
@@ -0,0 +1,368 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+ require "strscan"
5
+ require "strings-ansi"
6
+ require "unicode/display_width"
7
+
8
+ require_relative "truncation/configuration"
9
+ require_relative "truncation/version"
10
+
11
+ module Strings
12
+ class Truncation
13
+ class Error < StandardError; end
14
+
15
+ ANSI_REGEXP = /#{Strings::ANSI::ANSI_MATCHER}/.freeze
16
+ RESET_REGEXP = /#{Regexp.escape(Strings::ANSI::RESET)}/.freeze
17
+ END_REGEXP = /\A(#{Strings::ANSI::ANSI_MATCHER})*\z/.freeze
18
+
19
+ # Global instance
20
+ #
21
+ # @api private
22
+ def self.__instance__
23
+ @__instance__ ||= Truncation.new
24
+ end
25
+
26
+ class << self
27
+ extend Forwardable
28
+
29
+ delegate %i[truncate] => :__instance__
30
+ end
31
+
32
+ # Create a Strings::Truncation instance
33
+ #
34
+ # @example
35
+ # strings = Strings::Truncation.new(separator: /[,- ]/)
36
+ #
37
+ # @param [Integer] length
38
+ # the maximum length to truncate to
39
+ # @param [String] omission
40
+ # the string to denote omitted content
41
+ # @param [String|Integer] position
42
+ # the position of the omission within the string
43
+ # @param [String|Regexp] separator
44
+ # the separator to break words on
45
+ #
46
+ # @api public
47
+ def initialize(**options)
48
+ configuration.update(**options)
49
+ end
50
+
51
+ # Access configuration
52
+ #
53
+ # @api public
54
+ def configuration
55
+ @configuration ||= Configuration.new
56
+ end
57
+
58
+ # Configure truncation
59
+ #
60
+ # @example
61
+ # strings = Strings::Truncation.new
62
+ # strings.configure do |config|
63
+ # config.length 20
64
+ # config.omission "[...]"
65
+ # config.position :middle
66
+ # config.separator /[,- ]/
67
+ # end
68
+ #
69
+ # @example
70
+ # strings = Strings::Truncation.new
71
+ # strings.configure length: 20, omission: "[...]"
72
+ #
73
+ # @yield [Configuration]
74
+ #
75
+ # @api public
76
+ def configure(**options)
77
+ if block_given?
78
+ yield configuration
79
+ else
80
+ configuration.update(**options)
81
+ end
82
+ end
83
+
84
+ # Truncate a text at a given length (defualts to 30)
85
+ #
86
+ # @param [String] text
87
+ # the text to be truncated
88
+ #
89
+ # @param [Integer] truncate_at
90
+ # the width at which to truncate the text
91
+ #
92
+ # @param [String|Regexp] separator
93
+ # the character for splitting words
94
+ #
95
+ # @param [String] omission
96
+ # the string to use for displaying omitted content
97
+ #
98
+ # @param [String|Integer] position
99
+ # the position in text from which to omit content
100
+ #
101
+ # @example
102
+ # truncate("It is not down on any map; true places never are.")
103
+ # # => "It is not down on any map; tr…""
104
+ #
105
+ # truncate("It is not down on any map; true places never are.", 15)
106
+ # # => "It is not down…""
107
+ #
108
+ # truncate("It is not down on any map; true places never are.",
109
+ # separator: " ")
110
+ # # => "It is not down on any map;…"
111
+ #
112
+ # truncate("It is not down on any map; true places never are.", 40,
113
+ # omission: "[...]")
114
+ # # => "It is not down on any map; true pla[...]"
115
+ #
116
+ # @api public
117
+ def truncate(text, truncate_at = configuration.length, length: nil,
118
+ position: configuration.position,
119
+ separator: configuration.separator,
120
+ omission: configuration.omission)
121
+ truncate_at = length if length
122
+
123
+ return text if truncate_at.nil? || text.bytesize <= truncate_at.to_i
124
+
125
+ return "" if truncate_at.zero?
126
+
127
+ separator = Regexp.new(separator) if separator
128
+
129
+ case position
130
+ when :start
131
+ truncate_start(text, truncate_at, omission, separator)
132
+ when :middle
133
+ truncate_middle(text, truncate_at, omission, separator)
134
+ when :ends
135
+ truncate_ends(text, truncate_at, omission, separator)
136
+ when :end
137
+ truncate_from(0, text, truncate_at, omission, separator)
138
+ when Numeric
139
+ truncate_from(position, text, truncate_at, omission, separator)
140
+ else
141
+ raise Error, "unsupported position: #{position.inspect}"
142
+ end
143
+ end
144
+
145
+ private
146
+
147
+ # Truncate text at the start
148
+ #
149
+ # @param [String] text
150
+ # the text to truncate
151
+ # @param [Integer] length
152
+ # the maximum length to truncate at
153
+ # @param [String] omission
154
+ # the string to denote omitted content
155
+ # @param [String|Regexp] separator
156
+ # the pattern or string to separate on
157
+ #
158
+ # @return [String]
159
+ # the truncated text
160
+ #
161
+ # @api private
162
+ def truncate_start(text, length, omission, separator)
163
+ text_width = display_width(Strings::ANSI.sanitize(text))
164
+ omission_width = display_width(omission)
165
+ return text if text_width == length
166
+ return omission if omission_width == length
167
+
168
+ from = [text_width - length, 0].max
169
+ from += omission_width if from > 0
170
+ words, = *slice(text, from, length - omission_width,
171
+ omission_width: omission_width,
172
+ separator: separator)
173
+
174
+ "#{omission if from > 0}#{words}"
175
+ end
176
+
177
+ # Truncate text before the from position and after the length
178
+ #
179
+ # @param [Integer] from
180
+ # the position to start extracting from
181
+ # @param [String] text
182
+ # the text to truncate
183
+ # @param [Integer] length
184
+ # the maximum length to truncate at
185
+ # @param [String] omission
186
+ # the string to denote omitted content
187
+ # @param [String|Regexp] separator
188
+ # the pattern or string to separate on
189
+ #
190
+ # @return [String]
191
+ # the truncated text
192
+ #
193
+ # @api private
194
+ def truncate_from(from, text, length, omission, separator)
195
+ omission_width = display_width(omission)
196
+ length_without_omission = length - omission_width
197
+ length_without_omission -= omission_width if from > 0
198
+ words, stop = *slice(text, from, length_without_omission,
199
+ omission_width: omission_width,
200
+ separator: separator)
201
+
202
+ "#{omission if from > 0}#{words}#{omission if stop}"
203
+ end
204
+
205
+ # Truncate text in the middle
206
+ #
207
+ # @param [String] text
208
+ # the text to truncate
209
+ # @param [Integer] length
210
+ # the maximum length to truncate at
211
+ # @param [String] omission
212
+ # the string to denote omitted content
213
+ # @param [String|Regexp] separator
214
+ # the pattern or string to separate on
215
+ #
216
+ # @return [String]
217
+ # the truncated text
218
+ #
219
+ # @api private
220
+ def truncate_middle(text, length, omission, separator)
221
+ text_width = display_width(Strings::ANSI.sanitize(text))
222
+ omission_width = display_width(omission)
223
+ return text if text_width == length
224
+ return omission if omission_width == length
225
+
226
+ half_length = length / 2
227
+ rem_length = half_length + length % 2
228
+ half_omission = omission_width / 2
229
+ rem_omission = half_omission + omission_width % 2
230
+
231
+ before_words, = *slice(text, 0, half_length - half_omission,
232
+ omission_width: half_omission,
233
+ separator: separator)
234
+
235
+ after_words, = *slice(text, text_width - rem_length + rem_omission,
236
+ rem_length - rem_omission,
237
+ omission_width: rem_omission,
238
+ separator: separator)
239
+
240
+ "#{before_words}#{omission}#{after_words}"
241
+ end
242
+
243
+ # Truncate text at both ends
244
+ #
245
+ # @param [String] text
246
+ # the text to truncate
247
+ # @param [Integer] length
248
+ # the maximum length to truncate at
249
+ # @param [String] omission
250
+ # the string to denote omitted content
251
+ # @param [String|Regexp] separator
252
+ # the pattern or string to separate on
253
+ #
254
+ # @return [String]
255
+ # the truncated text
256
+ #
257
+ # @api private
258
+ def truncate_ends(text, length, omission, separator)
259
+ text_width = display_width(Strings::ANSI.sanitize(text))
260
+ omission_width = display_width(omission)
261
+ return text if text_width <= length
262
+ return omission if length <= 2 * omission_width
263
+
264
+ from = (text_width - length) / 2 + omission_width
265
+ words, stop = *slice(text, from, length - 2 * omission_width,
266
+ omission_width: omission_width,
267
+ separator: separator)
268
+ return omission if words.empty?
269
+
270
+ "#{omission if from > 0}#{words}#{omission if stop}"
271
+ end
272
+
273
+ # Extract number of characters from a text starting at the from position
274
+ #
275
+ # @param [Integer] from
276
+ # the position to start from
277
+ # @param [Integer] length
278
+ # the number of characters to extract
279
+ # @param [Integer] omission_width
280
+ # the width of the omission
281
+ # @param [String|Regexp] separator
282
+ # the string or pattern to use for splitting words
283
+ #
284
+ # @return [Array<String, Boolean>]
285
+ # return a substring and a stop flag
286
+ #
287
+ # @api private
288
+ def slice(text, from, length, omission_width: 0, separator: nil)
289
+ scanner = StringScanner.new(text)
290
+ length_with_omission = length + omission_width
291
+ current_length = 0
292
+ start_position = 0
293
+ ansi_reset = false
294
+ visible_char = false
295
+ word_break = false
296
+ stop = false
297
+ words = []
298
+ word = []
299
+ char = nil
300
+
301
+ while !(scanner.eos? || stop)
302
+ if scanner.scan(RESET_REGEXP)
303
+ unless scanner.eos?
304
+ words << scanner.matched
305
+ ansi_reset = false
306
+ end
307
+ elsif scanner.scan(ANSI_REGEXP)
308
+ words << scanner.matched
309
+ ansi_reset = true
310
+ else
311
+ if (char =~ separator && start_position <= from) ||
312
+ separator && start_position.zero?
313
+ word_break = start_position != from
314
+ end
315
+
316
+ char = scanner.getch
317
+ char_width = display_width(char)
318
+ start_position += char_width
319
+ next if (start_position - char_width) < from
320
+
321
+ current_length += char_width
322
+
323
+ if char =~ separator
324
+ if word_break
325
+ word_break = false
326
+ current_length = 0
327
+ next
328
+ end
329
+ visible_char = true
330
+ words << word.join
331
+ word.clear
332
+ end
333
+
334
+ if current_length <= length || scanner.check(END_REGEXP) &&
335
+ current_length <= length_with_omission
336
+ if separator
337
+ word << char unless word_break
338
+ else
339
+ words << char
340
+ visible_char = true
341
+ end
342
+ else
343
+ stop = true
344
+ end
345
+ end
346
+ end
347
+
348
+ visible_char = true if !word.empty? && scanner.eos?
349
+
350
+ return ["", stop] unless visible_char
351
+
352
+ words << word.join if !word.empty? && scanner.eos?
353
+
354
+ words << Strings::ANSI::RESET if ansi_reset
355
+
356
+ [words.join, stop]
357
+ end
358
+
359
+ # Visible width of a string
360
+ #
361
+ # @return [Integer]
362
+ #
363
+ # @api private
364
+ def display_width(string)
365
+ Unicode::DisplayWidth.of(string)
366
+ end
367
+ end # Truncation
368
+ end # Strings
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Strings
4
+ class Truncation
5
+ # A configuration object used by a Strings::Truncation instance
6
+ #
7
+ # @api private
8
+ class Configuration
9
+ DEFAULT_LENGTH = 30
10
+
11
+ DEFAULT_OMISSION = "…"
12
+
13
+ DEFAULT_POSITION = 0
14
+
15
+ # Create a configuration
16
+ #
17
+ # @api public
18
+ def initialize(length: DEFAULT_LENGTH, omission: DEFAULT_OMISSION,
19
+ position: DEFAULT_POSITION, separator: nil)
20
+ @length = length
21
+ @omission = omission
22
+ @position = position
23
+ @separator = separator
24
+ end
25
+
26
+ # Update current configuration
27
+ #
28
+ # @api public
29
+ def update(length: nil, omission: nil, position: nil, separator: nil)
30
+ @length = length if length
31
+ @omission = omission if omission
32
+ @position = position if position
33
+ @separator = separator if separator
34
+ end
35
+
36
+ # The maximum length to truncate to
37
+ #
38
+ # @example
39
+ # strings = Strings::Truncation.new
40
+ #
41
+ # strings.configure do |config|
42
+ # config.length 40
43
+ # end
44
+ #
45
+ # @param [Integer] number
46
+ #
47
+ # @api public
48
+ def length(number = (not_set = true))
49
+ if not_set
50
+ @length
51
+ else
52
+ @length = number
53
+ end
54
+ end
55
+
56
+ # The string to denote omitted content
57
+ #
58
+ # @example
59
+ # strings = Strings::Truncation.new
60
+ #
61
+ # strings.configure do |config|
62
+ # config.omission "..."
63
+ # end
64
+ #
65
+ # @param [String] string
66
+ #
67
+ # @api public
68
+ def omission(string = (not_set = true))
69
+ if not_set
70
+ @omission
71
+ else
72
+ @omission = string
73
+ end
74
+ end
75
+
76
+ # The position of the omission within the string
77
+ #
78
+ # @example
79
+ # strings = Strings::Truncation.new
80
+ #
81
+ # strings.configure do |config|
82
+ # config.position :start
83
+ # end
84
+ #
85
+ #
86
+ # @param [Symbol] position
87
+ # the position out of :start, :middle or :end
88
+ #
89
+ # @api public
90
+ def position(position = (not_set = true))
91
+ if not_set
92
+ @position
93
+ else
94
+ @position = position
95
+ end
96
+ end
97
+
98
+ # The separator to break the characters into words
99
+ #
100
+ # @example
101
+ # strings = Strings::Truncation.new
102
+ #
103
+ # strings.configure do |config|
104
+ # config.separator /[, ]/
105
+ # end
106
+ #
107
+ # @param [String|Regexp] separator
108
+ #
109
+ # @api public
110
+ def separator(separator = (not_set = true))
111
+ if not_set
112
+ @separator
113
+ else
114
+ @separator = separator
115
+ end
116
+ end
117
+ end # Configruation
118
+ end # Truncation
119
+ end # Strings
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../truncation"
4
+
5
+ module Strings
6
+ class Truncation
7
+ module Extensions
8
+ refine String do
9
+ def truncate(*args, **options)
10
+ Strings::Truncation.truncate(self, *args, **options)
11
+ end
12
+ end
13
+ end # Extensions
14
+ end # Truncation
15
+ end # Strings
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Strings
4
+ class Truncation
5
+ VERSION = "0.1.0"
6
+ end # Truncation
7
+ end # Strings
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: strings-truncation
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: 2021-02-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: strings-ansi
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: unicode-display_width
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '3.0'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '1.6'
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '3.0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rspec
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '3.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '3.0'
75
+ description: Truncate strings with fullwidth characters and ANSI codes. Characters
76
+ can be omitted from the start, middle, end or both ends.
77
+ email:
78
+ - piotr@piotrmurach.com
79
+ executables: []
80
+ extensions: []
81
+ extra_rdoc_files:
82
+ - README.md
83
+ - CHANGELOG.md
84
+ - LICENSE.txt
85
+ files:
86
+ - CHANGELOG.md
87
+ - LICENSE.txt
88
+ - README.md
89
+ - lib/strings-truncation.rb
90
+ - lib/strings/truncation.rb
91
+ - lib/strings/truncation/configuration.rb
92
+ - lib/strings/truncation/extensions.rb
93
+ - lib/strings/truncation/version.rb
94
+ homepage: https://github.com/piotrmurach/strings-truncation
95
+ licenses:
96
+ - MIT
97
+ metadata:
98
+ allowed_push_host: https://rubygems.org
99
+ bug_tracker_uri: https://github.com/piotrmurach/strings-truncation/issues
100
+ changelog_uri: https://github.com/piotrmurach/strings-truncation/blob/master/CHANGELOG.md
101
+ documentation_uri: https://www.rubydoc.info/gems/strings-truncation
102
+ homepage_uri: https://github.com/piotrmurach/strings-truncation
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: 2.0.0
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubygems_version: 3.1.2
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: Truncate strings with fullwidth characters and ANSI codes.
122
+ test_files: []