bblib 1.0.2 → 2.0.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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +502 -8
  3. data/lib/bblib.rb +1 -28
  4. data/lib/bblib/all.rb +5 -0
  5. data/lib/{time → bblib/classes}/cron.rb +0 -0
  6. data/lib/{string → bblib/classes}/fuzzy_matcher.rb +0 -0
  7. data/lib/bblib/cli.rb +3 -0
  8. data/lib/bblib/cli/color.rb +51 -0
  9. data/lib/bblib/cli/exceptions/invalid_argument.rb +5 -0
  10. data/lib/bblib/cli/exceptions/missing_argument.rb +5 -0
  11. data/lib/bblib/cli/exceptions/missing_required_argument.rb +5 -0
  12. data/lib/bblib/cli/exceptions/opts_parser.rb +9 -0
  13. data/lib/bblib/cli/option.rb +116 -0
  14. data/lib/bblib/cli/options/basic_option.rb +19 -0
  15. data/lib/bblib/cli/options/bool.rb +19 -0
  16. data/lib/bblib/cli/options/command.rb +11 -0
  17. data/lib/bblib/cli/options/date.rb +14 -0
  18. data/lib/bblib/cli/options/float.rb +14 -0
  19. data/lib/bblib/cli/options/integer.rb +14 -0
  20. data/lib/bblib/cli/options/json.rb +16 -0
  21. data/lib/bblib/cli/options/regexp.rb +17 -0
  22. data/lib/bblib/cli/options/string.rb +13 -0
  23. data/lib/bblib/cli/options/symbol.rb +13 -0
  24. data/lib/bblib/cli/options/time.rb +14 -0
  25. data/lib/bblib/cli/options/toggle.rb +27 -0
  26. data/lib/bblib/cli/options/untoggle.rb +18 -0
  27. data/lib/bblib/cli/opts_parser.rb +65 -0
  28. data/lib/bblib/core.rb +37 -0
  29. data/lib/{hash → bblib/core/classes}/hash_struct.rb +9 -2
  30. data/lib/bblib/core/classes/splitter.rb +101 -0
  31. data/lib/{time → bblib/core/classes}/task_timer.rb +0 -0
  32. data/lib/{hash → bblib/core/classes}/tree_hash.rb +22 -22
  33. data/lib/bblib/core/exceptions/abstract.rb +3 -0
  34. data/lib/bblib/core/exceptions/exception.rb +5 -0
  35. data/lib/{hash_path → bblib/core/hash_path}/hash_path.rb +0 -2
  36. data/lib/{hash_path → bblib/core/hash_path}/part.rb +0 -0
  37. data/lib/{mixins → bblib/core/mixins}/attrs.rb +17 -7
  38. data/lib/{mixins → bblib/core/mixins}/bbmixins.rb +1 -0
  39. data/lib/{mixins → bblib/core/mixins}/bridge.rb +0 -0
  40. data/lib/bblib/core/mixins/delegator.rb +90 -0
  41. data/lib/{class → bblib/core/mixins}/effortless.rb +0 -0
  42. data/lib/{mixins → bblib/core/mixins}/family_tree.rb +1 -1
  43. data/lib/{mixins → bblib/core/mixins}/hooks.rb +0 -0
  44. data/lib/{mixins → bblib/core/mixins}/logger.rb +0 -0
  45. data/lib/{mixins → bblib/core/mixins}/prototype.rb +0 -0
  46. data/lib/{mixins → bblib/core/mixins}/serializer.rb +0 -0
  47. data/lib/{mixins → bblib/core/mixins}/simple_init.rb +41 -3
  48. data/lib/{mixins → bblib/core/mixins}/type_init.rb +0 -0
  49. data/lib/{array/bbarray.rb → bblib/core/util/array.rb} +0 -0
  50. data/lib/{string → bblib/core/util}/cases.rb +0 -0
  51. data/lib/{file/bbfile.rb → bblib/core/util/file.rb} +31 -7
  52. data/lib/{hash/bbhash.rb → bblib/core/util/hash.rb} +0 -3
  53. data/lib/{logging/bblogging.rb → bblib/core/util/logging.rb} +11 -2
  54. data/lib/{string → bblib/core/util}/matching.rb +0 -0
  55. data/lib/{number/bbnumber.rb → bblib/core/util/number.rb} +0 -0
  56. data/lib/{object/bbobject.rb → bblib/core/util/object.rb} +2 -0
  57. data/lib/{opal/bbopal.rb → bblib/core/util/opal.rb} +0 -0
  58. data/lib/{os/bbos.rb → bblib/core/util/os.rb} +0 -0
  59. data/lib/{string → bblib/core/util}/pluralization.rb +0 -0
  60. data/lib/{string → bblib/core/util}/regexp.rb +0 -0
  61. data/lib/{string → bblib/core/util}/roman.rb +0 -0
  62. data/lib/{string/bbstring.rb → bblib/core/util/string.rb} +18 -24
  63. data/lib/{time/bbtime.rb → bblib/core/util/time.rb} +1 -5
  64. data/lib/bblib/cron.rb +2 -0
  65. data/lib/bblib/fuzzy_matcher.rb +2 -0
  66. data/lib/bblib/html.rb +4 -0
  67. data/lib/{html → bblib/html}/builder.rb +0 -0
  68. data/lib/{html → bblib/html}/tag.rb +0 -0
  69. data/lib/{html → bblib/html}/tag_set.rb +0 -0
  70. data/lib/bblib/system.rb +3 -0
  71. data/lib/bblib/system/command.rb +7 -0
  72. data/lib/bblib/system/system.rb +39 -0
  73. data/lib/bblib/version.rb +1 -1
  74. metadata +71 -44
  75. data/lib/error/abstract.rb +0 -3
  76. data/lib/hash_path/path_hash.rb +0 -84
  77. data/lib/hash_path/proc.rb +0 -93
  78. data/lib/hash_path/processors.rb +0 -239
  79. data/lib/html/bbhtml.rb +0 -3
  80. data/lib/system/bbsystem.rb +0 -42
@@ -0,0 +1,90 @@
1
+
2
+ module BBLib
3
+ module Delegator
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ base.send(:attr_ary, :instance_delegates)
7
+ end
8
+
9
+ def delegates
10
+ (instance_delegates + self.class.delegates).uniq
11
+ end
12
+
13
+ protected
14
+
15
+ def method_missing(method, *args, &block)
16
+ delegates.each do |delegate|
17
+ case delegate
18
+ when Symbol
19
+ next unless respond_to?(delegate) && method(delegate).arity == 0
20
+ object = send(delegate)
21
+ next unless object.respond_to?(method)
22
+ return object.send(method, *args, &block)
23
+ else
24
+ next unless delegate.respond_to?(method)
25
+ return delegate.send(method, *args, &block)
26
+ end
27
+ end
28
+ super
29
+ end
30
+
31
+ def respond_to_missing?(method, include_private = false)
32
+ return super if self.class.delegate_fast
33
+ super || delegates.any? do |delegate|
34
+ next if delegate == self # Protection from recursion
35
+ case delegate
36
+ when Symbol
37
+ self.method(delegate)&.arity == 0 &&
38
+ send(delegate).respond_to?(method)
39
+ else
40
+ delegate.respond_to?(method)
41
+ end
42
+ end
43
+ end
44
+
45
+ def delegate_to(*mthds)
46
+ mthds.flatten.each do |method|
47
+ next if instance_delegates.include?(method)
48
+ instance_delegates << method
49
+ end
50
+ true
51
+ end
52
+
53
+ module ClassMethods
54
+ # When turned on the respond_to_missing method is left unchanged.
55
+ # This GREATLY speeds up the instantiation of classes with lots of
56
+ # calls to respond_to?
57
+ def delegate_fast(*args)
58
+ return @delegate_fast ||= _ancestor_delegate_fast if args.empty?
59
+ @delegate_fast = args.first ? true : false
60
+ end
61
+
62
+ def delegate_to(*mthds)
63
+ mthds.flatten.each do |method|
64
+ next if delegates.include?(method)
65
+ delegates << method
66
+ end
67
+ true
68
+ end
69
+
70
+ def _ancestor_delegate_fast
71
+ ancestors.reverse.find do |anc|
72
+ next unless anc.respond_to?(:delegate_fast)
73
+ return anc.delegate_fast
74
+ end
75
+ true
76
+ end
77
+
78
+ def delegates
79
+ @delegates ||= ancestor_delegates
80
+ end
81
+
82
+ def ancestor_delegates
83
+ ancestors.reverse.flat_map do |anc|
84
+ next if anc == self || !anc.respond_to?(:delegates)
85
+ anc.delegates
86
+ end.compact.uniq
87
+ end
88
+ end
89
+ end
90
+ end
@@ -18,7 +18,7 @@ module BBLib
18
18
  def direct_descendants(include_singletons = false)
19
19
  return _inherited_by if BBLib.in_opal?
20
20
  ObjectSpace.each_object(Class).select do |c|
21
- (include_singletons || !c.singleton_class?) && c.ancestors[1] == self
21
+ (include_singletons || !c.singleton_class?) && c.ancestors[1..-1].find { |k| k.is_a?(Class) } == self
22
22
  end
23
23
  end
24
24
 
@@ -6,7 +6,11 @@ module BBLib
6
6
  module SimpleInit
7
7
  attr_reader :_init_type
8
8
 
9
- INIT_TYPES = [:strict, :loose].freeze
9
+ # strict - Raise exceptions for unknown named attributes
10
+ # loose - Ignore unknown named attributes
11
+ # collect - Put unknown named attributes into the attribute
12
+ # configured by collect_attribute
13
+ INIT_TYPES = [:strict, :loose, :collect].freeze
10
14
 
11
15
  def self.included(base)
12
16
  base.extend ClassMethods
@@ -20,6 +24,8 @@ module BBLib
20
24
  result = instance_eval(&block)
21
25
  simple_init_block_result(result) if respond_to?(:simple_init_block_result, true)
22
26
  end
27
+ send(:simple_postinit, *args, &block) if respond_to?(:simple_postinit, true)
28
+ self
23
29
  end
24
30
  end
25
31
 
@@ -39,6 +45,8 @@ module BBLib
39
45
  end
40
46
  raise ArgumentError, "Unknown #{init_foundation_method} \"#{named[init_foundation_method]}\" for #{self}" unless klass
41
47
  klass == self ? __new(*args, &block) : klass.new(*args, &block)
48
+ elsif named[init_foundation_method].nil? && init_foundation_default_class != self
49
+ init_foundation_default_class.new(*args, &block)
42
50
  else
43
51
  __new(*args, &block)
44
52
  end
@@ -64,6 +72,8 @@ module BBLib
64
72
  end
65
73
  raise ArgumentError, "Unknown #{init_foundation_method} \"#{named[init_foundation_method]}\"" unless klass
66
74
  klass == self ? super : klass.new(*args, &block)
75
+ elsif named[init_foundation_method].nil? && init_foundation_default_class != self && init_foundation_default_class < self
76
+ init_foundation_default_class.new(*args, &block)
67
77
  else
68
78
  super
69
79
  end
@@ -99,6 +109,23 @@ module BBLib
99
109
  self.init_foundation_compare(&block) if block
100
110
  end
101
111
 
112
+ def init_foundation_default_class
113
+ self
114
+ end
115
+
116
+ def collect_method(name = nil)
117
+ @collect_method = name if name
118
+ @collect_method ||= _super_collect_method
119
+ end
120
+
121
+ def _super_collect_method
122
+ ancestors.each do |ancestor|
123
+ next if ancestor == self
124
+ return ancestor.collect_method if ancestor.respond_to?(:collect_method)
125
+ end
126
+ :attributes
127
+ end
128
+
102
129
  def ancestor_init_foundation_method
103
130
  anc = ancestors.find do |a|
104
131
  next if a == self
@@ -175,16 +202,27 @@ module BBLib
175
202
  next unless !set_v_arg.include?(method) && details[:options][:required] && !named.include?(method) && !send(method)
176
203
  method
177
204
  end.compact
178
- raise ArgumentError, "You are missing the following required #{BBLib.pluralize('argument', missing.size)}: #{missing.join_terms}" unless missing.empty?
205
+ raise ArgumentError, "You are missing the following required #{BBLib.pluralize('argument', missing.size)} for #{self.class}: #{missing.join_terms}" unless missing.empty?
179
206
  end
180
207
  named.each do |method, value|
181
208
  next if method == self.class.init_foundation_method
182
209
  setter = "#{method}="
183
210
  exists = respond_to?(setter)
184
- raise ArgumentError, "Undefined attribute #{setter} for class #{self.class}." if !exists && self.class.init_type == :strict
211
+ if !exists && self.class.init_type == :strict
212
+ raise ArgumentError, "Undefined attribute #{setter} for class #{self.class}."
213
+ elsif !exists && self.class.init_type == :collect
214
+ _collect_attribute(method, value)
215
+ end
185
216
  next unless exists
186
217
  send(setter, value)
187
218
  end
188
219
  end
220
+
221
+ def _collect_attribute(method, value)
222
+ inst_name = "@#{self.class.collect_method}"
223
+ hash = instance_variable_get(inst_name)
224
+ hash = instance_variable_set(inst_name, {}) unless hash.is_a?(Hash)
225
+ hash[method] = value
226
+ end
189
227
  end
190
228
  end
File without changes
@@ -14,7 +14,7 @@ module BBLib
14
14
  # @param [Boolean] files If true, paths to files matching the filter will be returned.
15
15
  # @param [Boolean] dirs If true, paths to dirs matching the filter will be returned.
16
16
  # @param [Array] exclude Can be an array of regular expressions or strings that should be ignored when scanning. * in a string is expanded into .*, but all other characters are literal.
17
- def self.scan_dir(path, *filters, recursive: false, files: true, dirs: true, exclude: [], &block)
17
+ def self.scan_dir(path, *filters, recursive: false, files: true, dirs: true, exclude: [], filter_base: true, &block)
18
18
  return [] unless Dir.exist?(path)
19
19
  filters = filters.map { |filter| filter.is_a?(Regexp) ? filter : /^#{Regexp.quote(filter).gsub('\\*', '.*')}$/ }
20
20
  exclude = exclude ? [exclude].flatten.map { |exp| exp.is_a?(Regexp) ? exp : /^#{Regexp.quote(exp).gsub('\\*', '.*')}$/ } : []
@@ -22,12 +22,12 @@ module BBLib
22
22
  next if item =~ /^\.{1,2}$/ || (!exclude.empty? && exclude.any? { |exp| item =~ exp })
23
23
  item = "#{path}/#{item}".gsub('\\', '/')
24
24
  if File.file?(item)
25
- if files && (filters.empty? || filters.any? { |filter| item =~ filter })
25
+ if files && (filters.empty? || filters.any? { |filter| item =~ filter || filter_base && item.file_name =~ filter })
26
26
  block_given? ? yield(item) : item
27
27
  end
28
28
  elsif File.directory?(item)
29
29
  recur = recursive ? scan_dir(item, *filters, recursive: recursive, exclude: exclude, files: files, dirs: dirs, &block) : []
30
- if dirs && (filters.empty? || filters.any? { |filter| item =~ filter })
30
+ if dirs && (filters.empty? || filters.any? { |filter| item =~ filter || filter_base && item.file_name =~ filter })
31
31
  (block_given? ? yield(item) : [item] + recur)
32
32
  elsif recursive
33
33
  recur
@@ -37,13 +37,13 @@ module BBLib
37
37
  end
38
38
 
39
39
  # Uses BBLib.scan_dir but returns only files
40
- def self.scan_files(path, *filters, recursive: false, exclude: [], &block)
41
- scan_dir(path, *filters, recursive: recursive, dirs: false, exclude: exclude, &block)
40
+ def self.scan_files(path, *filters, recursive: false, exclude: [], filter_base: true, &block)
41
+ scan_dir(path, *filters, recursive: recursive, dirs: false, exclude: exclude, filter_base: filter_base, &block)
42
42
  end
43
43
 
44
44
  # Uses BBLib.scan_dir but returns only directories.
45
- def self.scan_dirs(path, *filters, recursive: false, exclude: [], &block)
46
- scan_dir(path, *filters, recursive: recursive, files: false, exclude: exclude, &block)
45
+ def self.scan_dirs(path, *filters, recursive: false, exclude: [], filter_base: true, &block)
46
+ scan_dir(path, *filters, recursive: recursive, files: false, exclude: exclude, filter_base: filter_base, &block)
47
47
  end
48
48
 
49
49
  # Shorthand method to write a string to disk. By default the
@@ -104,6 +104,30 @@ module BBLib
104
104
  zettabyte: { mult: 1024**7, exp: %w(zb zetta z zbyte zettabyte), styles: { short: 'ZB', long: ' zettabyte' } },
105
105
  yottabyte: { mult: 1024**8, exp: %w(yb yotta y ybyte yottabyte), styles: { short: 'YB', long: ' yottabyte' } }
106
106
  }.freeze
107
+
108
+ # Basic detection for whether or not a file is binary or not
109
+ def self.binary?(file, bytes: 1024, ctrl_threshold: 0.5, binary_threshold: 0.05)
110
+ ascii = 0
111
+ ctrl = 0
112
+ binary = 0
113
+
114
+ read_bytes = File.open(file, 'rb') { |io| io.read(bytes) }
115
+
116
+ return false if read_bytes.nil? || read_bytes.empty?
117
+
118
+ read_bytes.each_byte do |byte|
119
+ case byte
120
+ when 0..31
121
+ ctrl += 1
122
+ when 32..127
123
+ ascii += 1
124
+ else
125
+ binary += 1
126
+ end
127
+ end
128
+
129
+ ctrl.to_f / ascii > ctrl_threshold || binary.to_f / ascii > binary_threshold
130
+ end
107
131
  end
108
132
 
109
133
  # Monkey patches for the Numeric class
@@ -1,7 +1,4 @@
1
1
 
2
- require_relative 'tree_hash'
3
- require_relative 'hash_struct'
4
-
5
2
  class Hash
6
3
  # Merges with another hash but also merges all nested hashes and arrays/values.
7
4
  def deep_merge(with, merge_arrays: true, overwrite: true, uniq: false)
@@ -10,10 +10,11 @@ module BBLib
10
10
  log = ::Logger.new(STDOUT)
11
11
  log.level = ::Logger::INFO
12
12
  log.formatter = proc do |severity, datetime, progname, msg|
13
+ severity = severity.to_s.to_color(severity) if BBLib.color_logs
13
14
  if msg.is_a?(Exception)
14
15
  msg = msg.inspect + "\n\t" + msg.backtrace.join("\n\t")
15
16
  end
16
- "[#{datetime}] #{severity} - #{msg.to_s.chomp}\n"
17
+ "#{datetime} [#{severity}] #{msg.to_s.chomp}\n"
17
18
  end
18
19
  log.datetime_format = '%Y-%m-%d %H:%M:%S'
19
20
  log
@@ -32,10 +33,18 @@ module BBLib
32
33
  @logger_on
33
34
  end
34
35
 
36
+ def self.color_logs
37
+ @color_logs
38
+ end
39
+
40
+ def self.color_logs=(toggle)
41
+ @color_logs = (toggle ? true : false)
42
+ end
43
+
35
44
  class << self
36
45
  [:fatal, :error, :warn, :info, :debug].each do |sev|
37
46
  define_method(sev) do |*args|
38
- logger.send(sev, *args)
47
+ logger.send(sev, *args) if logger
39
48
  end
40
49
  end
41
50
  end
@@ -7,6 +7,8 @@ module BBLib
7
7
  klasses.any? { |klass| obj.is_a?(klass) }
8
8
  end
9
9
 
10
+ # Takes any type of object and converts it into a hash based on its instance
11
+ # variables.
10
12
  def self.to_hash(obj)
11
13
  return { obj => nil } if obj.instance_variables.empty?
12
14
  hash = {}
File without changes
@@ -1,8 +1,5 @@
1
-
2
- # frozen_string_literal: true
3
1
  require_relative 'matching'
4
2
  require_relative 'roman'
5
- require_relative 'fuzzy_matcher'
6
3
  require_relative 'cases'
7
4
  require_relative 'regexp'
8
5
  require_relative 'pluralization'
@@ -25,9 +22,12 @@ module BBLib
25
22
  .map { |m| convert ? m.to_f : m }
26
23
  end
27
24
 
25
+ EXTRACT_NUMBER_REGEXP = /(?<=[^\.]|^)\d+\.\d+(?=[^\.]|$)|(?<=[^\.\d]|^)\d+(?=[^\.\d]|$)/
26
+ EXTRACT_NUMBER_REGEXP_NO_INNER = /(?<=[^\.]|^)\d+\.\d+(?=[^\.]|$)|(?<=[^\.\d\w]|^)\d+(?=[^\.\d\w]|$)/
27
+
28
28
  # Extracts any correctly formed integers or floats from a string
29
- def self.extract_numbers(str, convert: true)
30
- str.scan(/\d+\.\d+(?<=[^\.])|\d+(?<=[^\.])|\d+\.\d+$|\d+$/)
29
+ def self.extract_numbers(str, convert: true, include_inner: true)
30
+ str.scan(include_inner ? EXTRACT_NUMBER_REGEXP : EXTRACT_NUMBER_REGEXP_NO_INNER)
31
31
  .map { |f| convert ? (f.include?('.') ? f.to_f : f.to_i) : f }
32
32
  end
33
33
 
@@ -99,15 +99,18 @@ module BBLib
99
99
  # a context (either a Hash or Object) to then interpolate in placeholders.
100
100
  # The default pattern looks for {{method_name}} within the string but can be
101
101
  # customized to a different pattern by setting the pattern named argument.
102
- def self.pattern_render(text, context = {}, pattern: /\{{2}.*?\}{2}/, field_pattern: /(?<=^\{{2}).*(?=\}{2})/)
102
+ def self.pattern_render(text, context = {})
103
103
  raise ArgumentError, "Expected text argument to be a String, got a #{text.class}" unless text.is_a?(String)
104
- txt = text.dup
104
+ # TODO Make patterns customizable
105
+ pattern = /\{{2}.*?\}{2}/
106
+ field_pattern = /(?<=^\{{2}).*(?=\}{2})/
107
+ txt = text.dup
105
108
  txt.scan(pattern).each do |match|
106
109
  field = match.scan(field_pattern).first
107
110
  next unless field
108
111
  value = case context
109
112
  when Hash
110
- context[field.to_sym] || context[field]
113
+ context.hpath(field).first
111
114
  else
112
115
  context.send(field) if context.respond_to?(field)
113
116
  end.to_s
@@ -130,7 +133,7 @@ class String
130
133
 
131
134
  # Split on delimiters
132
135
  def quote_split(*delimiters)
133
- encap_split('"\'', *delimiters)
136
+ encap_split(%w{" '}, *delimiters)
134
137
  end
135
138
 
136
139
  alias qsplit quote_split
@@ -138,21 +141,12 @@ class String
138
141
  # Split on only delimiters not between specific encapsulators
139
142
  # Various characters are special and automatically recognized such as parens
140
143
  # which automatically match anything between a begin and end character.
141
- def encap_split(encapsulator, *delimiters)
142
- pattern = case encapsulator
143
- when '('
144
- '\\(\\)'
145
- when '['
146
- '\\[\\]'
147
- when '{'
148
- '\\{\\}'
149
- when '<'
150
- '\\<\\>'
151
- else
152
- encapsulator
153
- end
154
- patterns = delimiters.map { |d| /#{Regexp.escape(d)}(?=(?:[^#{pattern}]|[#{pattern}][^#{pattern}]*[#{pattern}])*$)/}
155
- msplit(*patterns)
144
+ #
145
+ # Regex below is no longer used because of how inefficient it is.
146
+ # Comment is left in case it is ever useful again
147
+ # /(?<group>\((?:[^\(\)]*|\g<group>)*\)[^\(\)]*?),|,(?<=[^\(\)|$])/
148
+ def encap_split(expressions, *delimiters, **opts)
149
+ BBLib::Splitter.split(self, *delimiters, **opts.merge(expressions: expressions))
156
150
  end
157
151
 
158
152
  alias esplit encap_split
@@ -1,7 +1,3 @@
1
- # frozen_string_literal: true
2
- require_relative 'task_timer'
3
- require_relative 'cron'
4
-
5
1
  module BBLib
6
2
  # Parses known time based patterns out of a string to construct a numeric duration.
7
3
  def self.parse_duration(str, output: :sec, min_interval: :sec)
@@ -173,7 +169,7 @@ end
173
169
 
174
170
  class Numeric
175
171
  include BBLib::Durations
176
-
172
+
177
173
  def to_duration(input: :sec, stop: :milli, style: :medium)
178
174
  BBLib.to_duration self, input: input, stop: stop, style: style
179
175
  end