bblib 0.3.0 → 0.4.1

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 +5 -5
  2. data/.gitignore +11 -10
  3. data/.rspec +2 -2
  4. data/.travis.yml +4 -4
  5. data/CODE_OF_CONDUCT.md +13 -13
  6. data/Gemfile +4 -4
  7. data/LICENSE.txt +21 -21
  8. data/README.md +247 -757
  9. data/Rakefile +6 -6
  10. data/bblib.gemspec +34 -34
  11. data/bin/console +14 -14
  12. data/bin/setup +7 -7
  13. data/lib/array/bbarray.rb +71 -29
  14. data/lib/bblib.rb +12 -12
  15. data/lib/bblib/version.rb +3 -3
  16. data/lib/class/effortless.rb +23 -0
  17. data/lib/error/abstract.rb +3 -0
  18. data/lib/file/bbfile.rb +93 -52
  19. data/lib/hash/bbhash.rb +130 -46
  20. data/lib/hash/hash_struct.rb +24 -0
  21. data/lib/hash/tree_hash.rb +364 -0
  22. data/lib/hash_path/hash_path.rb +210 -0
  23. data/lib/hash_path/part.rb +83 -0
  24. data/lib/hash_path/path_hash.rb +84 -0
  25. data/lib/hash_path/proc.rb +93 -0
  26. data/lib/hash_path/processors.rb +239 -0
  27. data/lib/html/bbhtml.rb +2 -0
  28. data/lib/html/builder.rb +34 -0
  29. data/lib/html/tag.rb +49 -0
  30. data/lib/logging/bblogging.rb +42 -0
  31. data/lib/mixins/attrs.rb +422 -0
  32. data/lib/mixins/bbmixins.rb +7 -0
  33. data/lib/mixins/bridge.rb +17 -0
  34. data/lib/mixins/family_tree.rb +41 -0
  35. data/lib/mixins/hooks.rb +139 -0
  36. data/lib/mixins/logger.rb +31 -0
  37. data/lib/mixins/serializer.rb +71 -0
  38. data/lib/mixins/simple_init.rb +160 -0
  39. data/lib/number/bbnumber.rb +15 -7
  40. data/lib/object/bbobject.rb +46 -19
  41. data/lib/opal/bbopal.rb +0 -4
  42. data/lib/os/bbos.rb +24 -16
  43. data/lib/os/bbsys.rb +60 -43
  44. data/lib/string/bbstring.rb +165 -66
  45. data/lib/string/cases.rb +37 -29
  46. data/lib/string/fuzzy_matcher.rb +48 -50
  47. data/lib/string/matching.rb +43 -30
  48. data/lib/string/pluralization.rb +156 -0
  49. data/lib/string/regexp.rb +45 -0
  50. data/lib/string/roman.rb +17 -30
  51. data/lib/system/bbsystem.rb +42 -0
  52. data/lib/time/bbtime.rb +79 -58
  53. data/lib/time/cron.rb +174 -132
  54. data/lib/time/task_timer.rb +86 -70
  55. metadata +27 -10
  56. data/lib/gem/bbgem.rb +0 -28
  57. data/lib/hash/hash_path.rb +0 -344
  58. data/lib/hash/hash_path_proc.rb +0 -256
  59. data/lib/hash/path_hash.rb +0 -81
  60. data/lib/object/attr.rb +0 -182
  61. data/lib/object/hooks.rb +0 -69
  62. data/lib/object/lazy_class.rb +0 -73
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:spec)
5
-
6
- task :default => :spec
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -1,34 +1,34 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'bblib/version'
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = "bblib"
8
- spec.version = BBLib::VERSION
9
- spec.authors = ["Brandon Black"]
10
- spec.email = ["d2sm10@hotmail.com"]
11
-
12
- spec.summary = %q{A library containing many reusable, basic functions.}
13
- spec.description = %q{A library containing many reusable, basic functions.}
14
- spec.homepage = "https://github.com/bblack16/bblib-ruby"
15
- spec.license = "MIT"
16
-
17
- # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
- # delete this section to allow pushing this gem to any host.
19
- # if spec.respond_to?(:metadata)
20
- # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
21
- # else
22
- # raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
- # end
24
-
25
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) || f.end_with?('.gem') }
26
- spec.bindir = "exe"
27
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
- spec.require_paths = ["lib"]
29
-
30
- spec.add_development_dependency "bundler", "~> 1.10"
31
- spec.add_development_dependency "rake", "~> 10.0"
32
- spec.add_development_dependency "rspec"
33
- spec.add_development_dependency "simplecov"
34
- end
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bblib/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "bblib"
8
+ spec.version = BBLib::VERSION
9
+ spec.authors = ["Brandon Black"]
10
+ spec.email = ["d2sm10@hotmail.com"]
11
+
12
+ spec.summary = %q{A library containing many reusable, basic functions.}
13
+ spec.description = %q{A library containing many reusable, basic functions.}
14
+ spec.homepage = "https://github.com/bblack16/bblib-ruby"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
+ # delete this section to allow pushing this gem to any host.
19
+ # if spec.respond_to?(:metadata)
20
+ # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
21
+ # else
22
+ # raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
+ # end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) || f.end_with?('.gem') }
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_development_dependency "bundler", "~> 1.10"
31
+ spec.add_development_dependency "rake", "~> 10.0"
32
+ spec.add_development_dependency "rspec"
33
+ spec.add_development_dependency "simplecov"
34
+ end
@@ -1,14 +1,14 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require "bblib"
5
-
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
8
-
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
12
-
13
- require "irb"
14
- IRB.start
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "bblib"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup CHANGED
@@ -1,7 +1,7 @@
1
- #!/bin/bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
-
5
- bundle install
6
-
7
- # Do any other automated setup that you need to do here
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -1,50 +1,92 @@
1
-
1
+ # frozen_string_literal: true
2
2
  module BBLib
3
-
4
- # Takes two arrays (can be of different length) and interleaves them like [a[0], b[0], a[1], b[1]...]
5
- def self.interleave a, b
6
- ary = Array.new
7
- [a.size, b.size].max.times do |i|
8
- ary.push(a[i]) if i < a.size
9
- ary.push(b[i]) if i < b.size
3
+ # Takes two arrays (can be of different length) and interleaves
4
+ # them like [a[0], b[0], a[1], b[1]...]
5
+ def self.interleave(ary_a, ary_b)
6
+ ary = []
7
+ [ary_a.size, ary_b.size].max.times do |indx|
8
+ ary.push(ary_a[indx]) if indx < ary_a.size
9
+ ary.push(ary_b[indx]) if indx < ary_b.size
10
10
  end
11
11
  ary
12
12
  end
13
13
 
14
+ # Returns the element that occurs the most frequently in an array or
15
+ def self.most_frequent(*args)
16
+ totals = args.each_with_object(Hash.new(0)) { |elem, hash| hash[elem] += 1 }
17
+ max = totals.values.max
18
+ totals.keys.find { |key| totals[key] == max }
19
+ end
20
+
21
+ # Returns the most commonly occurring string in an arrray of params.
22
+ # Elements that are not strings are converted to their string representations.
23
+ #
24
+ # @param [TrueClass, FalseClass] case_insensitive Compare strings case isensitively.
25
+ def self.most_frequent_str(*args, case_insensitive: false)
26
+ most_frequent(*args.map { |arg| case_insensitive ? arg.to_s.downcase : arg.to_s })
27
+ end
28
+
29
+ # Takes an array and averages all of the floats and integers within it.
30
+ # Non numeric values are ignored.
31
+ def self.average(ary)
32
+ numbers = ary.select { |v| BBLib.is_a?(v, Integer, Float) }
33
+ numbers.inject(0) do |sum, x|
34
+ sum += x
35
+ end / numbers.size.to_f
36
+ end
14
37
  end
15
38
 
39
+ # Monkey Patches for the Array class
16
40
  class Array
41
+ # Splits all elements in an array using a list of delimiters.
42
+ def msplit(*delims)
43
+ map { |elem| elem.msplit(*delims) if elem.respond_to?(:msplit) }.flatten
44
+ end
45
+
46
+ alias multi_split msplit
47
+
48
+ # Converts all keys in nested hashes to symbols.
49
+ def keys_to_sym(clean: false)
50
+ map { |elem| elem.respond_to?(:keys_to_sym) ? elem.keys_to_sym(clean: clean) : elem }
51
+ end
17
52
 
18
- def msplit *delims, keep_empty: false
19
- self.map{ |i| i.msplit(delims, keep_empty:keep_empty)}.flatten
53
+ # Converts all keys in nested hashes to strings.
54
+ def keys_to_s
55
+ map { |v| v.respond_to?(:keys_to_s) ? v.keys_to_s : v }
20
56
  end
21
- alias_method :multi_split, :msplit
22
57
 
23
- def keys_to_sym clean: false
24
- self.map{ |v| v.is_a?(Hash) || v.is_a?(Array) ? v.keys_to_sym(clean:clean) : v }
58
+ # Takes two arrays (can be of different length) and interleaves
59
+ # them like [a[0], b[0], a[1], b[1]...]
60
+ def interleave(ary)
61
+ BBLib.interleave(self, ary)
25
62
  end
26
63
 
27
- def keys_to_s clean: false
28
- self.map{ |v| v.is_a?(Hash) || v.is_a?(Array) ? v.keys_to_s : v }
64
+ # Displays all elements between this hash and another hash that are different.
65
+ # @param [Array] ary The ary to compare elements to.
66
+ def diff(ary)
67
+ (self - ary) + (ary - self)
29
68
  end
30
69
 
31
- def to_xml level: 0, key:nil
32
- map do |v|
33
- nested = v.respond_to?(:to_xml)
34
- value = nested ? v.to_xml(level:level + 1, key:key) : v
35
- "\t" * level + "<#{key}>\n" +
36
- (nested ? '' : "\t"*(level+1)) +
37
- "#{value}\n" +
38
- "\t"*level + "</#{key}>\n"
39
- end.join
70
+ # Creates a tree hash wrapper for this array.
71
+ def to_tree_hash
72
+ TreeHash.new(self)
40
73
  end
41
74
 
42
- def interleave b
43
- BBLib.interleave self, b
75
+ # Conventient way to join an array into a comma seperated list with the last two elements
76
+ # seperated by a word like 'and' or 'or'.
77
+ # @param seperator [String] The term or phrase to seperate the last two elements by
78
+ # @param delimiter [String] The delimiter used in the join. This allows something other than ', ' to be used
79
+ # @param encapsulate [String] This will optionally encapsulate each element with a character or string. Useful to wrap all elements in quotes.
80
+ # @returns [String] By default returns a comma seperated list with the final elements seperated by an 'and'. Behavior can be overriden using the params.
81
+ def join_terms(seperator = :and, delimiter: ', ', encapsulate: nil)
82
+ elements = (encapsulate ? map { |element| element.to_s.encapsulate(encapsulate) } : self)
83
+ return elements.join(delimiter) if size <= 1
84
+ return elements.join(" #{seperator} ") if size == 2
85
+ [elements[0..-2].join(delimiter), elements.last].join(" #{seperator} ")
44
86
  end
45
87
 
46
- def diff b
47
- (self-b) + (b-self)
88
+ def hmap
89
+ return map unless block_given?
90
+ map { |v| yield(v) }.to_h
48
91
  end
49
-
50
92
  end
@@ -1,27 +1,27 @@
1
1
  require_relative 'bblib/version'
2
2
  require_relative 'opal/bbopal'
3
3
  require_relative 'object/bbobject'
4
- require_relative 'object/lazy_class'
4
+ require_relative 'hash/bbhash'
5
+ require_relative 'mixins/bbmixins'
6
+ require_relative 'class/effortless'
7
+ require_relative 'hash_path/hash_path'
5
8
  require_relative 'string/bbstring'
6
9
  require_relative 'file/bbfile'
7
10
  require_relative 'time/bbtime'
8
- require_relative 'hash/bbhash'
9
- require_relative 'gem/bbgem'
10
11
  require_relative 'number/bbnumber'
11
12
  require_relative 'array/bbarray'
13
+ require_relative 'system/bbsystem'
14
+ require_relative 'logging/bblogging'
15
+ require_relative 'error/abstract'
16
+ require_relative 'html/bbhtml'
12
17
 
13
- non_opal = ['os/bbos', 'gem/bbgem']
14
-
15
- unless BBLib::in_opal?
16
- non_opal.each{ |i| require_relative i }
17
- end
18
+ non_opal = ['os/bbos']
18
19
 
20
+ non_opal.each { |i| require_relative i } unless BBLib.in_opal?
19
21
 
20
22
  require 'fileutils'
21
- # require 'uri'
23
+ require 'time'
22
24
 
23
25
  module BBLib
24
-
25
- CONFIGS_PATH = 'config/'
26
-
26
+ CONFIGS_PATH = 'config/'.freeze
27
27
  end
@@ -1,3 +1,3 @@
1
- module BBLib
2
- VERSION = "0.3.0"
3
- end
1
+ module BBLib
2
+ VERSION = '0.4.1'.freeze
3
+ end
@@ -0,0 +1,23 @@
1
+
2
+ module BBLib
3
+ module Effortless
4
+ def self.included(base)
5
+ base.extend(BBLib::Attrs)
6
+ base.extend(BBLib::Hooks)
7
+ base.singleton_class.extend(BBLib::Hooks)
8
+ base.extend(BBLib::FamilyTree) unless BBLib.in_opal?
9
+ base.extend(BBLib::Bridge)
10
+ base.send(:include, BBLib::Serializer)
11
+ base.send(:include, BBLib::SimpleInit)
12
+ base.send(:include, BBLib::Logger)
13
+ end
14
+
15
+ def _attrs
16
+ self.class._attrs
17
+ end
18
+ end
19
+
20
+ class EffortlessClass
21
+ include Effortless
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ class AbstractError < StandardError
2
+ # Enhance at some point
3
+ end
@@ -1,91 +1,132 @@
1
1
 
2
-
3
2
  module BBLib
4
-
5
3
  # Takes one or more strings and normalizes slashes to create a consistent file path
6
4
  # Useful when concating two strings that when you don't know if one or both will end or begin with a slash
7
- def self.pathify *strings
8
- start = strings.first.start_with?('/') || strings.first.start_with?('\\')
9
- (start ? '/' : '' ) + strings.map(&:to_s).msplit('/', '\\').map(&:strip).join('/')
5
+ def self.pathify(*strings)
6
+ (strings.first.start_with?('/', '\\') ? '/' : '') + strings.map(&:to_s).msplit('/', '\\').map(&:strip).join('/')
10
7
  end
11
8
 
12
9
  # Scan for files and directories. Can be set to be recursive and can also have filters applied.
13
- def self.scan_dir path = Dir.pwd, filter: nil, recursive: false
14
- if !filter.nil?
15
- filter = [filter].flatten.map{ |f| path.to_s + (recursive ? '/**/' : '/') + f.to_s }
16
- else
17
- filter = (path.to_s + (recursive ? '/**/*' : '/*')).gsub('//', '/')
18
- end
19
- Dir.glob(filter)
10
+ # @param [String] path The directory to scan files from.
11
+ # @param [String..., Regexp...] filters A list of filters to apply. Can be regular expressions or strings.
12
+ # Strings with a * are treated as regular expressions with a .*. If no filters are passed, all files/dirs are returned.
13
+ # @param [Boolean] recursive When true scan will recursively search directories
14
+ # @param [Boolean] files If true, paths to files matching the filter will be returned.
15
+ # @param [Boolean] dirs If true, paths to dirs matching the filter will be returned.
16
+ def self.scan_dir(path, *filters, recursive: false, files: true, dirs: true, &block)
17
+ return [] unless Dir.exist?(path)
18
+ filters = filters.map { |filter| filter.is_a?(Regexp) ? filter : /^#{Regexp.quote(filter).gsub('\\*', '.*')}$/ }
19
+ Dir.foreach(path).flat_map do |item|
20
+ next if item =~ /^\.{1,2}$/
21
+ item = "#{path}/#{item}".gsub('\\', '/')
22
+ if File.file?(item)
23
+ if files && (filters.empty? || filters.any? { |filter| item =~ filter })
24
+ block_given? ? yield(item) : item
25
+ end
26
+ elsif File.directory?(item)
27
+ recur = recursive ? scan_dir(item, *filters, recursive: recursive, files: files, dirs: dirs, &block) : []
28
+ if dirs && (filters.empty? || filters.any? { |filter| item =~ filter })
29
+ (block_given? ? yield(item) : [item] + recur)
30
+ elsif recursive
31
+ recur
32
+ end
33
+ end
34
+ end.compact
20
35
  end
21
36
 
22
- # Uses BBLib.scan_dir but returns only files. Mode can be used to return strings (:path) or File objects (:file)
23
- def self.scan_files path, filter: nil, recursive: false, mode: :path
24
- BBLib.scan_dir(path, filter: filter, recursive: recursive).map{ |f| File.file?(f) ? (mode == :file ? File.new(f) : f) : nil}.reject{ |r| r.nil? }
37
+ # Uses BBLib.scan_dir but returns only files
38
+ def self.scan_files(path, *filters, recursive: false, &block)
39
+ scan_dir(path, *filters, recursive: recursive, dirs: false, &block)
25
40
  end
26
41
 
27
- # Uses BBLib.scan_dir but returns only directories. Mode can be used to return strings (:path) or Dir objects (:dir)
28
- def self.scan_dirs path, filter: nil, recursive: false, mode: :path
29
- BBLib.scan_dir(path, filter: filter, recursive: recursive).map{ |f| File.directory?(f) ? (mode == :dir ? Dir.new(f) : f ) : nil}.reject{ |r| r.nil? }
42
+ # Uses BBLib.scan_dir but returns only directories.
43
+ def self.scan_dirs(path, *filters, recursive: false, &block)
44
+ scan_dir(path, *filters, recursive: recursive, files: false, &block)
30
45
  end
31
46
 
32
- # Shorthand method to write a string to disk. By default the path is created if it doesn't exist.
47
+ # Shorthand method to write a string to disk. By default the
48
+ # path is created if it doesn't exist.
33
49
  # Set mode to w to truncate file or leave at a to append.
34
- def self.string_to_file path, str, mkpath = true, mode: 'a'
35
- if !Dir.exists?(path) && mkpath
36
- FileUtils.mkpath File.dirname(path)
37
- end
38
- File.write(path, str.to_s, mode:mode)
50
+ def self.string_to_file(str, path, mkpath: true, mode: 'a')
51
+ FileUtils.mkpath(File.dirname(path)) if mkpath && !Dir.exist?(path)
52
+ File.write(path, str.to_s, mode: mode)
39
53
  end
40
54
 
41
55
  # A file size parser for strings. Extracts any known patterns for file sizes.
42
- def self.parse_file_size str, output: :byte
43
- output = FILE_SIZES.keys.find{ |f| f == output || FILE_SIZES[f][:exp].include?(output.to_s.downcase) } || :byte
56
+ def self.parse_file_size(str, output: :byte)
57
+ output = FILE_SIZES.keys.find { |fs| fs == output || FILE_SIZES[fs][:exp].include?(output.to_s.downcase) } || :byte
44
58
  bytes = 0.0
45
- FILE_SIZES.each do |k, v|
46
- v[:exp].each do |e|
47
- numbers = str.scan(/(?=\w|\D|^)\d*\.?\d+\s*#{e}s?(?=\W|\d|$)/i)
48
- numbers.each{ |n| bytes+= n.to_f * v[:mult] }
59
+ FILE_SIZES.each do |_k, v|
60
+ v[:exp].each do |exp|
61
+ str.scan(/(?=\w|\D|^)\d*\.?\d+\s*#{exp}s?(?=\W|\d|$)/i)
62
+ .each { |num| bytes += num.to_f * v[:mult] }
49
63
  end
50
64
  end
51
- return bytes / FILE_SIZES[output][:mult]
65
+ bytes / FILE_SIZES[output][:mult]
52
66
  end
53
67
 
54
- FILE_SIZES = {
55
- byte: { mult: 1, exp: ['b', 'byt', 'byte'] },
56
- kilobyte: { mult: 1024, exp: ['kb', 'kilo', 'k', 'kbyte', 'kilobyte'] },
57
- megabyte: { mult: 1048576, exp: ['mb', 'mega', 'm', 'mib', 'mbyte', 'megabyte'] },
58
- gigabyte: { mult: 1073741824, exp: ['gb', 'giga', 'g', 'gbyte', 'gigabyte'] },
59
- terabyte: { mult: 1099511627776, exp: ['tb', 'tera', 't', 'tbyte', 'terabyte'] },
60
- petabyte: { mult: 1125899906842624, exp: ['pb', 'peta', 'p', 'pbyte', 'petabyte'] },
61
- exabyte: { mult: 1152921504606846976, exp: ['eb', 'exa', 'e', 'ebyte', 'exabyte'] },
62
- zettabyte: { mult: 1180591620717411303424, exp: ['zb', 'zetta', 'z', 'zbyte', 'zettabyte'] },
63
- yottabyte: { mult: 1208925819614629174706176, exp: ['yb', 'yotta', 'y', 'ybyte', 'yottabyte'] }
64
- }
68
+ # Takes an integer or float and converts it into a string that represents
69
+ # a file size (e.g. "5 MB 156 kB")
70
+ # @param [Integer, Float] num The number of bytes to convert to a file size string.
71
+ # @param [Symbol] input Sets the value of the input. Default is byte.
72
+ # @param [Symbol] stop Sets a minimum file size to display.
73
+ # e.g. If stop is set to :megabyte, :kilobyte and below will be truncated.
74
+ # @param [Symbol] style The out style, Current options are :short and :long
75
+ def self.to_file_size(num, input: :byte, stop: :byte, style: :short)
76
+ return nil unless num.is_a?(Numeric)
77
+ return '0' if num.zero?
78
+ style = :short unless [:long, :short].include?(style)
79
+ expression = []
80
+ n = num * FILE_SIZES[input.to_sym][:mult]
81
+ done = false
82
+ FILE_SIZES.reverse.each do |k, v|
83
+ next if done
84
+ done = true if k == stop
85
+ div = n / v[:mult]
86
+ next unless div >= 1
87
+ val = (done ? div.round : div.floor)
88
+ expression << "#{val}#{v[:styles][style]}#{val > 1 && style != :short ? 's' : nil}"
89
+ n -= val.to_f * v[:mult]
90
+ end
91
+ expression.join(' ')
92
+ end
65
93
 
94
+ FILE_SIZES = {
95
+ byte: { mult: 1, exp: %w(b byt byte), styles: { short: 'B', long: ' byte' } },
96
+ kilobyte: { mult: 1024, exp: %w(kb kilo k kbyte kilobyte), styles: { short: 'kB', long: ' kilobyte' } },
97
+ megabyte: { mult: 1024**2, exp: %w(mb mega m mib mbyte megabyte), styles: { short: 'MB', long: ' megabyte' } },
98
+ gigabyte: { mult: 1024**3, exp: %w(gb giga g gbyte gigabyte), styles: { short: 'GB', long: ' gigabyte' } },
99
+ terabyte: { mult: 1024**4, exp: %w(tb tera t tbyte terabyte), styles: { short: 'TB', long: ' terabyte' } },
100
+ petabyte: { mult: 1024**5, exp: %w(pb peta p pbyte petabyte), styles: { short: 'PB', long: ' petabyte' } },
101
+ exabyte: { mult: 1024**6, exp: %w(eb exa e ebyte exabyte), styles: { short: 'EB', long: ' exabyte' } },
102
+ zettabyte: { mult: 1024**7, exp: %w(zb zetta z zbyte zettabyte), styles: { short: 'ZB', long: ' zettabyte' } },
103
+ yottabyte: { mult: 1024**8, exp: %w(yb yotta y ybyte yottabyte), styles: { short: 'YB', long: ' yottabyte' } }
104
+ }.freeze
66
105
  end
67
106
 
68
- class File
69
- def dirname
70
- File.dirname(self.path)
107
+ # Monkey patches for the Numeric class
108
+ class Numeric
109
+ def to_file_size(*args)
110
+ BBLib.to_file_size(self, *args)
71
111
  end
72
112
  end
73
113
 
114
+ # Monkey patches for the String class
74
115
  class String
75
- def to_file path, mkpath = true, mode: 'a'
76
- BBLib.string_to_file path, self, mkpath, mode:mode
116
+ def to_file(*args)
117
+ BBLib.string_to_file(self, *args)
77
118
  end
78
119
 
79
- def file_name with_extension = true
80
- self[(self.include?('/') ? self.rindex('/').to_i+1 : 0)..(with_extension ? -1 : self.rindex('.').to_i-1)]
120
+ def file_name(with_extension = true)
121
+ with_extension ? File.basename(self) : File.basename(self, File.extname(self))
81
122
  end
82
123
 
83
124
  def dirname
84
- self.scan(/.*(?=\/)/).first
125
+ File.dirname(self)
85
126
  end
86
127
 
87
- def parse_file_size output: :byte
88
- BBLib.parse_file_size(self, output:output)
128
+ def parse_file_size(*args)
129
+ BBLib.parse_file_size(self, *args)
89
130
  end
90
131
 
91
132
  def pathify