handlebars-helpers 0.0.5 → 0.0.65

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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +3 -3
  3. data/.gitignore +1 -0
  4. data/.handlebars_helpers.json +901 -0
  5. data/.handlebars_string_formatters.json +137 -0
  6. data/.rubocop.yml +4 -2
  7. data/.vscode/settings.json +6 -0
  8. data/Gemfile +3 -0
  9. data/Rakefile +1 -0
  10. data/STORIES.md +44 -6
  11. data/handlebars-helpers.gemspec +2 -0
  12. data/lib/handlebars/helpers.rb +2 -0
  13. data/lib/handlebars/helpers/base_helper.rb +36 -0
  14. data/lib/handlebars/helpers/base_safe_string_helper.rb +17 -0
  15. data/lib/handlebars/helpers/code_ruby/classify.rb +40 -0
  16. data/lib/handlebars/helpers/code_ruby/deconstantize.rb +48 -0
  17. data/lib/handlebars/helpers/code_ruby/demodulize.rb +58 -0
  18. data/lib/handlebars/helpers/code_ruby/foreign_key.rb +49 -0
  19. data/lib/handlebars/helpers/code_ruby/tableize.rb +35 -0
  20. data/lib/handlebars/helpers/comparison/and.rb +44 -0
  21. data/lib/handlebars/helpers/comparison/default.rb +66 -0
  22. data/lib/handlebars/helpers/comparison/eq.rb +35 -0
  23. data/lib/handlebars/helpers/comparison/gt.rb +35 -0
  24. data/lib/handlebars/helpers/comparison/gte.rb +43 -0
  25. data/lib/handlebars/helpers/comparison/lt.rb +35 -0
  26. data/lib/handlebars/helpers/comparison/lte.rb +43 -0
  27. data/lib/handlebars/helpers/comparison/ne.rb +35 -0
  28. data/lib/handlebars/helpers/comparison/or.rb +56 -0
  29. data/lib/handlebars/helpers/configuration.rb +72 -0
  30. data/lib/handlebars/helpers/inflection/ordinal.rb +58 -0
  31. data/lib/handlebars/helpers/inflection/ordinalize.rb +59 -0
  32. data/lib/handlebars/helpers/inflection/pluralize.rb +36 -0
  33. data/lib/handlebars/helpers/inflection/pluralize_by_number.rb +60 -0
  34. data/lib/handlebars/helpers/inflection/singularize.rb +35 -0
  35. data/lib/handlebars/helpers/register_helpers.rb +73 -0
  36. data/lib/handlebars/helpers/string_formatting/append_if.rb +42 -0
  37. data/lib/handlebars/helpers/string_formatting/back_slash.rb +33 -0
  38. data/lib/handlebars/helpers/string_formatting/camel.rb +29 -0
  39. data/lib/handlebars/helpers/string_formatting/constantize.rb +29 -0
  40. data/lib/handlebars/helpers/string_formatting/dasherize.rb +33 -0
  41. data/lib/handlebars/helpers/string_formatting/dotirize.rb +33 -0
  42. data/lib/handlebars/helpers/string_formatting/double_colon.rb +33 -0
  43. data/lib/handlebars/helpers/string_formatting/downcase.rb +31 -0
  44. data/lib/handlebars/helpers/string_formatting/format_as.rb +57 -0
  45. data/lib/handlebars/helpers/string_formatting/humanize.rb +39 -0
  46. data/lib/handlebars/helpers/string_formatting/lamel.rb +29 -0
  47. data/lib/handlebars/helpers/string_formatting/padl.rb +58 -0
  48. data/lib/handlebars/helpers/string_formatting/padr.rb +59 -0
  49. data/lib/handlebars/helpers/string_formatting/pluserize.rb +29 -0
  50. data/lib/handlebars/helpers/string_formatting/prepend_if.rb +42 -0
  51. data/lib/handlebars/helpers/string_formatting/singularize.rb +35 -0
  52. data/lib/handlebars/helpers/string_formatting/slash.rb +33 -0
  53. data/lib/handlebars/helpers/string_formatting/snake.rb +33 -0
  54. data/lib/handlebars/helpers/string_formatting/string.js +511 -0
  55. data/lib/handlebars/helpers/string_formatting/surround.rb +49 -0
  56. data/lib/handlebars/helpers/string_formatting/surround_if.rb +43 -0
  57. data/lib/handlebars/helpers/string_formatting/titleize.rb +39 -0
  58. data/lib/handlebars/helpers/string_formatting/upcase.rb +31 -0
  59. data/lib/handlebars/helpers/string_tokenizer.rb +43 -0
  60. data/lib/handlebars/helpers/template.rb +68 -0
  61. data/lib/handlebars/helpers/version.rb +1 -1
  62. metadata +82 -3
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ # reference: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb
4
+ require 'active_support/core_ext/string'
5
+
6
+ require 'handlebars/helpers/base_safe_string_helper'
7
+ require 'handlebars/helpers/string_formatting/format_as'
8
+
9
+ module Handlebars
10
+ module Helpers
11
+ # String manipulation methods for case formatting
12
+ module StringFormatting
13
+ # Append If will prepend the prefix to value, if value is not empty
14
+ class PrependIf < Handlebars::Helpers::BaseSafeStringHelper
15
+ # Parse will Append If will prepend the prefix to value, if value is not empty
16
+ #
17
+ # @example
18
+ #
19
+ # puts PrependIf.new.parse('turn to symbol', ':', 'snake')
20
+ #
21
+ # :turn_to_symbol
22
+ #
23
+ # @param [String] value - value to add prepend too
24
+ # @param [String] prefix - prefix to insert in front of value
25
+ # @param [String] formats - list of formats to apply to value, defaults to none
26
+ # @return [String] prefix + value when value exists, otherwise ''
27
+ def parse(value, prefix, formats)
28
+ format_as = Handlebars::Helpers::StringFormatting::FormatAs.new
29
+ value.present? ? "#{prefix}#{format_as.parse(value, formats)}" : ''
30
+ end
31
+
32
+ def handlebars_helper
33
+ proc do |_context, value, prefix, formats|
34
+ # Handle optional: formats
35
+ formats = nil if formats.is_a?(V8::Object)
36
+ wrapper(parse(value, prefix, formats))
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # reference: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb
4
+ require 'active_support/core_ext/string'
5
+
6
+ require 'handlebars/helpers/base_helper'
7
+
8
+ module Handlebars
9
+ module Helpers
10
+ # General purpose string manipulation helpers
11
+ module StringFormatting
12
+ # The reverse of #pluralize, returns the singular form of a word in a string
13
+ class Singularize < Handlebars::Helpers::BaseHelper
14
+ # Parse will reverse of #pluralize, returns the singular form of a word in a string
15
+ #
16
+ # @example
17
+ #
18
+ # puts Singularize.new.parse('names')
19
+ #
20
+ # name
21
+ #
22
+ # puts Singularize.new.parse('octopi')
23
+ #
24
+ # octopus
25
+ #
26
+ # @return [String] plural value turned to singular value
27
+ def parse(value)
28
+ return '' if value.nil?
29
+
30
+ value.singularize
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # reference: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb
4
+ require 'active_support/core_ext/string'
5
+
6
+ require 'handlebars/helpers/base_helper'
7
+
8
+ module Handlebars
9
+ module Helpers
10
+ # String manipulation methods for case formatting
11
+ module StringFormatting
12
+ # slash case the characters in the given 'string'.
13
+ class Slash < Handlebars::Helpers::BaseHelper
14
+ # Parse will slash case the characters in the given 'string'.
15
+ #
16
+ # @side effects
17
+ #
18
+ # Text casing is preserved.
19
+ #
20
+ # @example
21
+ #
22
+ # puts Slash.new.parse('the Quick brown Fox 99')
23
+ #
24
+ # the/Quick/brown/Fox99
25
+ #
26
+ # @return [String] value converted to slash notation
27
+ def parse(value)
28
+ tokenizer.parse(value, preserve_case: true, separator: '/')
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # reference: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb
4
+ require 'active_support/core_ext/string'
5
+
6
+ require 'handlebars/helpers/base_helper'
7
+
8
+ module Handlebars
9
+ module Helpers
10
+ # String manipulation methods for case formatting
11
+ module StringFormatting
12
+ # snake case the characters in the given 'string'.
13
+ class Snake < Handlebars::Helpers::BaseHelper
14
+ # Parse will snake case the characters in the given 'string'.
15
+ #
16
+ # @side effects
17
+ #
18
+ # All text is in lower case
19
+ #
20
+ # @example
21
+ #
22
+ # puts Snake.new.parse('the quick brown fox 99')
23
+ #
24
+ # the-quick-brown-fox99
25
+ #
26
+ # @return [String] value converted to snake case
27
+ def parse(value)
28
+ tokenizer.parse(value, separator: '_')
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,511 @@
1
+ /**
2
+ * Center a string using non-breaking spaces
3
+ *
4
+ * @param {String} `str`
5
+ * @param {String} `spaces`
6
+ * @return {String}
7
+ * @api public
8
+ */
9
+
10
+ helpers.center = function(str, spaces) {
11
+ if (!util.isString(str)) return '';
12
+ var space = '';
13
+ var i = 0;
14
+ while (i < spaces) {
15
+ space += '&nbsp;';
16
+ i++;
17
+ }
18
+ return space + str + space;
19
+ };
20
+
21
+ /**
22
+ * Like trim, but removes both extraneous whitespace **and
23
+ * non-word characters** from the beginning and end of a string.
24
+ *
25
+ * ```handlebars
26
+ * {{chop "_ABC_"}}
27
+ * <!-- results in: 'ABC' -->
28
+ *
29
+ * {{chop "-ABC-"}}
30
+ * <!-- results in: 'ABC' -->
31
+ *
32
+ * {{chop " ABC "}}
33
+ * <!-- results in: 'ABC' -->
34
+ * ```
35
+ * @param {String} `string` The string to chop.
36
+ * @return {String}
37
+ * @api public
38
+ */
39
+
40
+ helpers.chop = function(str) {
41
+ if (!util.isString(str)) return '';
42
+ return utils.chop(str);
43
+ };
44
+
45
+
46
+ /**
47
+ * Truncates a string to the specified `length`, and appends
48
+ * it with an elipsis, `…`.
49
+ *
50
+ * ```handlebars
51
+ * {{ellipsis (sanitize "<span>foo bar baz</span>"), 7}}
52
+ * <!-- results in: 'foo bar…' -->
53
+ * {{ellipsis "foo bar baz", 7}}
54
+ * <!-- results in: 'foo bar…' -->
55
+ * ```
56
+ * @param {String} `str`
57
+ * @param {Number} `length` The desired length of the returned string.
58
+ * @return {String} The truncated string.
59
+ * @api public
60
+ */
61
+
62
+ helpers.ellipsis = function(str, limit) {
63
+ if (util.isString(str)) {
64
+ if (str.length <= limit) {
65
+ return str;
66
+ }
67
+ return helpers.truncate(str, limit) + '…';
68
+ }
69
+ };
70
+
71
+ /**
72
+ * Return true if `value` is a string.
73
+ *
74
+ * ```handlebars
75
+ * {{isString "foo"}}
76
+ * <!-- results in: 'true' -->
77
+ * ```
78
+ * @param {String} `value`
79
+ * @return {Boolean}
80
+ * @api public
81
+ */
82
+
83
+ helpers.isString = function(value) {
84
+ return typeof value === 'string';
85
+ };
86
+
87
+ /**
88
+ * Return the number of occurrences of `substring` within the
89
+ * given `string`.
90
+ *
91
+ * ```handlebars
92
+ * {{occurrences "foo bar foo bar baz" "foo"}}
93
+ * <!-- results in: 2 -->
94
+ * ```
95
+ * @param {String} `str`
96
+ * @param {String} `substring`
97
+ * @return {Number} Number of occurrences
98
+ * @api public
99
+ */
100
+
101
+ helpers.occurrences = function(str, substring) {
102
+ if (!util.isString(str)) return '';
103
+ var len = substring.length;
104
+ var pos = 0;
105
+ var n = 0;
106
+
107
+ while ((pos = str.indexOf(substring, pos)) > -1) {
108
+ n++;
109
+ pos += len;
110
+ }
111
+ return n;
112
+ };
113
+
114
+ /**
115
+ * Replace spaces in the given string with pluses.
116
+ *
117
+ * ```handlebars
118
+ * {{plusify "foo bar baz"}}
119
+ * <!-- results in: 'foo+bar+baz' -->
120
+ * ```
121
+ * @param {String} `str` The input string
122
+ * @return {String} Input string with spaces replaced by plus signs
123
+ * @source Stephen Way <https://github.com/stephenway>
124
+ * @api public
125
+ */
126
+
127
+ helpers.plusify = function(str, ch) {
128
+ if (!util.isString(str)) return '';
129
+ if (!util.isString(ch)) ch = ' ';
130
+ return str.split(ch).join('+');
131
+ };
132
+
133
+ /**
134
+ * Prepends the given `string` with the specified `prefix`.
135
+ *
136
+ * ```handlebars
137
+ * <!-- given that "val" is "bar" -->
138
+ * {{prepend val "foo-"}}
139
+ * <!-- results in: 'foo-bar' -->
140
+ * ```
141
+ * @param {String} `str`
142
+ * @param {String} `prefix`
143
+ * @return {String}
144
+ * @api public
145
+ */
146
+
147
+ helpers.prepend = function(str, prefix) {
148
+ return typeof str === 'string' && typeof prefix === 'string'
149
+ ? (prefix + str)
150
+ : str;
151
+ };
152
+
153
+ /**
154
+ * Render a block without processing mustache templates inside the block.
155
+ *
156
+ * ```handlebars
157
+ * {{{{#raw}}}}
158
+ * {{foo}}
159
+ * {{{{/raw}}}}
160
+ * <!-- results in: '{{foo}}' -->
161
+ * ```
162
+ *
163
+ * @param {Object} `options`
164
+ * @return {String}
165
+ * @block
166
+ * @api public
167
+ */
168
+
169
+ helpers.raw = function(options) {
170
+ var str = options.fn();
171
+ var opts = util.options(this, options);
172
+ if (opts.escape !== false) {
173
+ var idx = 0;
174
+ while (((idx = str.indexOf('{{', idx)) !== -1)) {
175
+ if (str[idx - 1] !== '\\') {
176
+ str = str.slice(0, idx) + '\\' + str.slice(idx);
177
+ }
178
+ idx += 3;
179
+ }
180
+ }
181
+ return str;
182
+ };
183
+
184
+ /**
185
+ * Remove all occurrences of `substring` from the given `str`.
186
+ *
187
+ * ```handlebars
188
+ * {{remove "a b a b a b" "a "}}
189
+ * <!-- results in: 'b b b' -->
190
+ * ```
191
+ * @param {String} `str`
192
+ * @param {String} `substring`
193
+ * @return {String}
194
+ * @api public
195
+ */
196
+
197
+ helpers.remove = function(str, ch) {
198
+ if (!util.isString(str)) return '';
199
+ if (!util.isString(ch)) return str;
200
+ return str.split(ch).join('');
201
+ };
202
+
203
+ /**
204
+ * Remove the first occurrence of `substring` from the given `str`.
205
+ *
206
+ * ```handlebars
207
+ * {{remove "a b a b a b" "a"}}
208
+ * <!-- results in: ' b a b a b' -->
209
+ * ```
210
+ * @param {String} `str`
211
+ * @param {String} `substring`
212
+ * @return {String}
213
+ * @api public
214
+ */
215
+
216
+ helpers.removeFirst = function(str, ch) {
217
+ if (!util.isString(str)) return '';
218
+ if (!util.isString(ch)) return str;
219
+ return str.replace(ch, '');
220
+ };
221
+
222
+ /**
223
+ * Replace all occurrences of substring `a` with substring `b`.
224
+ *
225
+ * ```handlebars
226
+ * {{replace "a b a b a b" "a" "z"}}
227
+ * <!-- results in: 'z b z b z b' -->
228
+ * ```
229
+ * @param {String} `str`
230
+ * @param {String} `a`
231
+ * @param {String} `b`
232
+ * @return {String}
233
+ * @api public
234
+ */
235
+
236
+ helpers.replace = function(str, a, b) {
237
+ if (!util.isString(str)) return '';
238
+ if (!util.isString(a)) return str;
239
+ if (!util.isString(b)) b = '';
240
+ return str.split(a).join(b);
241
+ };
242
+
243
+ /**
244
+ * Replace the first occurrence of substring `a` with substring `b`.
245
+ *
246
+ * ```handlebars
247
+ * {{replace "a b a b a b" "a" "z"}}
248
+ * <!-- results in: 'z b a b a b' -->
249
+ * ```
250
+ * @param {String} `str`
251
+ * @param {String} `a`
252
+ * @param {String} `b`
253
+ * @return {String}
254
+ * @api public
255
+ */
256
+
257
+ helpers.replaceFirst = function(str, a, b) {
258
+ if (!util.isString(str)) return '';
259
+ if (!util.isString(a)) return str;
260
+ if (!util.isString(b)) b = '';
261
+ return str.replace(a, b);
262
+ };
263
+
264
+ /**
265
+ * Reverse a string.
266
+ *
267
+ * ```handlebars
268
+ * {{reverse "abcde"}}
269
+ * <!-- results in: 'edcba' -->
270
+ * ```
271
+ * @param {String} `str`
272
+ * @return {String}
273
+ * @api public
274
+ */
275
+
276
+ helpers.reverse = function(str) {
277
+ if (!util.isString(str)) return '';
278
+ return str.split('').reverse().join('');
279
+ };
280
+
281
+ /**
282
+ * Sentence case the given string
283
+ *
284
+ * ```handlebars
285
+ * {{sentence "hello world. goodbye world."}}
286
+ * <!-- results in: 'Hello world. Goodbye world.' -->
287
+ * ```
288
+ * @param {String} `str`
289
+ * @return {String}
290
+ * @api public
291
+ */
292
+
293
+ helpers.sentence = function(str) {
294
+ if (!util.isString(str)) return '';
295
+ return str.replace(/((?:\S[^\.\?\!]*)[\.\?\!]*)/g, function(txt) {
296
+ return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
297
+ });
298
+ };
299
+
300
+ /**
301
+ * snake_case the characters in the given `string`.
302
+ *
303
+ * ```handlebars
304
+ * {{snakecase "a-b-c d_e"}}
305
+ * <!-- results in: 'a_b_c_d_e' -->
306
+ * ```
307
+ * @param {String} `string`
308
+ * @return {String}
309
+ * @api public
310
+ */
311
+
312
+ helpers.snakecase = function(str) {
313
+ if (!util.isString(str)) return '';
314
+ return utils.changecase(str, function(ch) {
315
+ return '_' + ch;
316
+ });
317
+ };
318
+
319
+ /**
320
+ * Split `string` by the given `character`.
321
+ *
322
+ * ```handlebars
323
+ * {{split "a,b,c" ","}}
324
+ * <!-- results in: ['a', 'b', 'c'] -->
325
+ * ```
326
+ * @param {String} `string` The string to split.
327
+ * @return {String} `character` Default is an empty string.
328
+ * @api public
329
+ */
330
+
331
+ helpers.split = function(str, ch) {
332
+ if (!util.isString(str)) return '';
333
+ if (!util.isString(ch)) ch = ',';
334
+ return str.split(ch);
335
+ };
336
+
337
+ /**
338
+ * Tests whether a string begins with the given prefix.
339
+ *
340
+ * ```handlebars
341
+ * {{#startsWith "Goodbye" "Hello, world!"}}
342
+ * Whoops
343
+ * {{else}}
344
+ * Bro, do you even hello world?
345
+ * {{/startsWith}}
346
+ * ```
347
+ * @contributor Dan Fox <http://github.com/iamdanfox>
348
+ * @param {String} `prefix`
349
+ * @param {String} `testString`
350
+ * @param {String} `options`
351
+ * @return {String}
352
+ * @block
353
+ * @api public
354
+ */
355
+
356
+ helpers.startsWith = function(prefix, str, options) {
357
+ var args = [].slice.call(arguments);
358
+ options = args.pop();
359
+ if (util.isString(str) && str.indexOf(prefix) === 0) {
360
+ return options.fn(this);
361
+ }
362
+ if (typeof options.inverse === 'function') {
363
+ return options.inverse(this);
364
+ }
365
+ return '';
366
+ };
367
+
368
+ /**
369
+ * Title case the given string.
370
+ *
371
+ * ```handlebars
372
+ * {{titleize "this is title case"}}
373
+ * <!-- results in: 'This Is Title Case' -->
374
+ * ```
375
+ * @param {String} `str`
376
+ * @return {String}
377
+ * @api public
378
+ */
379
+
380
+ helpers.titleize = function(str) {
381
+ if (!util.isString(str)) return '';
382
+ var title = str.replace(/[- _]+/g, ' ');
383
+ var words = title.split(' ');
384
+ var len = words.length;
385
+ var res = [];
386
+ var i = 0;
387
+ while (len--) {
388
+ var word = words[i++];
389
+ res.push(exports.capitalize(word));
390
+ }
391
+ return res.join(' ');
392
+ };
393
+
394
+ /**
395
+ * Removes extraneous whitespace from the beginning and end
396
+ * of a string.
397
+ *
398
+ * ```handlebars
399
+ * {{trim " ABC "}}
400
+ * <!-- results in: 'ABC' -->
401
+ * ```
402
+ * @param {String} `string` The string to trim.
403
+ * @return {String}
404
+ * @api public
405
+ */
406
+
407
+ helpers.trim = function(str) {
408
+ return typeof str === 'string' ? str.trim() : '';
409
+ };
410
+
411
+ /**
412
+ * Removes extraneous whitespace from the beginning of a string.
413
+ *
414
+ * ```handlebars
415
+ * {{trim " ABC "}}
416
+ * <!-- results in: 'ABC ' -->
417
+ * ```
418
+ * @param {String} `string` The string to trim.
419
+ * @return {String}
420
+ * @api public
421
+ */
422
+
423
+ helpers.trimLeft = function(str) {
424
+ if (util.isString(str)) {
425
+ return str.replace(/^\s+/, '');
426
+ }
427
+ };
428
+
429
+ /**
430
+ * Removes extraneous whitespace from the end of a string.
431
+ *
432
+ * ```handlebars
433
+ * {{trimRight " ABC "}}
434
+ * <!-- results in: ' ABC' -->
435
+ * ```
436
+ * @param {String} `string` The string to trim.
437
+ * @return {String}
438
+ * @api public
439
+ */
440
+
441
+ helpers.trimRight = function(str) {
442
+ if (util.isString(str)) {
443
+ return str.replace(/\s+$/, '');
444
+ }
445
+ };
446
+
447
+ /**
448
+ * Truncate a string to the specified `length`. Also see [ellipsis](#ellipsis).
449
+ *
450
+ * ```handlebars
451
+ * truncate("foo bar baz", 7);
452
+ * <!-- results in: 'foo bar' -->
453
+ * truncate(sanitize("<span>foo bar baz</span>", 7));
454
+ * <!-- results in: 'foo bar' -->
455
+ * ```
456
+ * @param {String} `str`
457
+ * @param {Number} `limit` The desired length of the returned string.
458
+ * @param {String} `suffix` Optionally supply a string to use as a suffix to
459
+ * denote when the string has been truncated. Otherwise an ellipsis (`…`) will be used.
460
+ * @return {String} The truncated string.
461
+ * @api public
462
+ */
463
+
464
+ helpers.truncate = function(str, limit, suffix) {
465
+ if (util.isString(str)) {
466
+ if (typeof suffix !== 'string') {
467
+ suffix = '';
468
+ }
469
+ if (str.length > limit) {
470
+ return str.slice(0, limit - suffix.length) + suffix;
471
+ }
472
+ return str;
473
+ }
474
+ };
475
+
476
+ /**
477
+ * Truncate a string to have the specified number of words.
478
+ * Also see [truncate](#truncate).
479
+ *
480
+ * ```handlebars
481
+ * truncateWords("foo bar baz", 1);
482
+ * <!-- results in: 'foo…' -->
483
+ * truncateWords("foo bar baz", 2);
484
+ * <!-- results in: 'foo bar…' -->
485
+ * truncateWords("foo bar baz", 3);
486
+ * <!-- results in: 'foo bar baz' -->
487
+ * ```
488
+ * @param {String} `str`
489
+ * @param {Number} `limit` The desired length of the returned string.
490
+ * @param {String} `suffix` Optionally supply a string to use as a suffix to
491
+ * denote when the string has been truncated.
492
+ * @return {String} The truncated string.
493
+ * @api public
494
+ */
495
+
496
+ helpers.truncateWords = function(str, count, suffix) {
497
+ if (util.isString(str) && isNumber(count)) {
498
+ if (typeof suffix !== 'string') {
499
+ suffix = '…';
500
+ }
501
+
502
+ var num = Number(count);
503
+ var arr = str.split(/[ \t]/);
504
+ if (num > arr.length) {
505
+ arr = arr.slice(0, num);
506
+ }
507
+
508
+ var val = arr.join(' ').trim();
509
+ return val + suffix;
510
+ }
511
+ };