bblib 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +211 -0
- data/Rakefile +6 -0
- data/bblib.gemspec +33 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/bblib.rb +13 -0
- data/lib/bblib/version.rb +3 -0
- data/lib/file/bbfile.rb +78 -0
- data/lib/hash/bbhash.rb +33 -0
- data/lib/math/bbmath.rb +12 -0
- data/lib/net/bbnet.rb +1 -0
- data/lib/string/bbstring.rb +97 -0
- data/lib/string/fuzzy_matcher.rb +75 -0
- data/lib/string/matching.rb +119 -0
- data/lib/string/roman.rb +70 -0
- data/lib/time/bbtime.rb +84 -0
- metadata +108 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0ee410b186ccd9feaaf7a4bc76bb6919aa1ecc3a
|
4
|
+
data.tar.gz: 260e264c246c8a894078386376874d09ee0ac877
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 113718b874eb1e0c9fae613e6c6d1ece6f5e1cbef055c2f8f7cd5903911803b2d48d43b3d595e6acd4596038e34a61af0d0954b9459b06b78433d9453cae6dc6
|
7
|
+
data.tar.gz: 045421131ce65287668aa2c89b835f1b566bca620d346b2f4783b400aa6b714c1626cd7bb459c2116a9d96475ee3bf41eb8b274ab84ccb24302c69d776468022
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
12
|
+
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Brandon
|
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,211 @@
|
|
1
|
+
# BBLib
|
2
|
+
|
3
|
+
BBLib (Brandon-Black-Lib) is a collection of various reusable methods and classes to extend the Ruby language. Currently the library is in an early state and is being written generally for education purposes. As such, large changes will likely occur and some functions may be incomplete or inaccurate until 1.0.
|
4
|
+
|
5
|
+
One of my primary goals with the core BBLib code is to keep it as lightweight as possible. This means you will not find dependencies outside of the Ruby core libraries in this code. Further modules that do have larger dependencies will be released in separate gems.
|
6
|
+
|
7
|
+
For a full breakdown of what is currently in this library, scroll down.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'bblib'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install bblib
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
BBLib is currently broken up into the following categories:
|
28
|
+
* File
|
29
|
+
* Hash
|
30
|
+
* Math
|
31
|
+
* Net
|
32
|
+
* String
|
33
|
+
* Time
|
34
|
+
|
35
|
+
### File
|
36
|
+
#### File Scanners
|
37
|
+
|
38
|
+
Various simple file scan methods are available. All of these are toggleable-recursive and can be passed filters using an wildcarding supported by the Ruby Dir.glob() method.
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
# Scan for any files or folders in a path
|
42
|
+
BBLib.scan_dir 'C:/path/to/files'
|
43
|
+
|
44
|
+
#=> 'C:/path/to/files/readme.md'
|
45
|
+
#=> 'C:/path/to/files/license.txt'
|
46
|
+
#=> 'C:/path/to/files/folder/'
|
47
|
+
```
|
48
|
+
|
49
|
+
If you need only files or dirs but not both, the following two convenience methods are also available:
|
50
|
+
```ruby
|
51
|
+
# Scan for files ONLY
|
52
|
+
BBLib.scan_files 'C:/path/to/files'
|
53
|
+
|
54
|
+
#=> 'C:/path/to/files/readme.md'
|
55
|
+
#=> 'C:/path/to/files/license.txt'
|
56
|
+
|
57
|
+
# Scan for folders ONLY
|
58
|
+
BBLib.scan_dirs 'C:/path/to/files'
|
59
|
+
|
60
|
+
#=> 'C:/path/to/files/folder/'
|
61
|
+
```
|
62
|
+
|
63
|
+
All of the scan methods also allow for the following named arguments:
|
64
|
+
* **recursive**: Default is false. Set to true to recursively scan directories
|
65
|
+
* **filter**: Default is nil. Can take either a String or Array of Strings. These strings will be used as filters so that only matching files or dirs are returned (Ex: '*.jpg', which would return all jpg files.)
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
# Scan for any 'txt' or 'jpg' files recursively in a dir
|
69
|
+
BBLib.scan_dir 'C:/path/to/files', recursive: true, filter: ['*.jpg', '*.txt']
|
70
|
+
|
71
|
+
#=> 'C:/path/to/files/license.txt'
|
72
|
+
#=> 'C:/path/to/files/folder/profile.jpg'
|
73
|
+
#=> 'C:/path/to/files/folder/another_folder/text.txt'
|
74
|
+
```
|
75
|
+
|
76
|
+
In addition, both _scan_files_ and _scan_dirs_ also support a **mode** named argument. By default, this argument is set to :path. In _scan_files_ if :file is passed to :mode, a ruby File object will be returned rather than a String representation of the path. Similarily, if :dir is passed to _scan_dirs_ a ruby Dir object is returned.
|
77
|
+
|
78
|
+
#### File Size Parsing
|
79
|
+
|
80
|
+
A file size parser is available that analyzes known patterns in a string to construct a numeric file size. This is very useful for parsing the output from outside applications or from web scrapers.
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
# Turn a string into a file size (in bytes)
|
84
|
+
BBLib.parse_file_size "1MB 100KB"
|
85
|
+
|
86
|
+
#=> 1150976.0
|
87
|
+
```
|
88
|
+
|
89
|
+
By default the output is in bytes, however, this can be modified using the named argument **output**.
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
# Turn a string into a file size (in bytes)
|
93
|
+
BBLib.parse_file_size "1MB 100KB", output: :megabyte
|
94
|
+
|
95
|
+
#=> 1.09765625
|
96
|
+
|
97
|
+
# The method can also be called directly on a string
|
98
|
+
|
99
|
+
"1.5 Mb".parse_file_size output: :kilobyte
|
100
|
+
|
101
|
+
#=> 1536.0
|
102
|
+
```
|
103
|
+
|
104
|
+
All of the following are options for output:
|
105
|
+
* :byte
|
106
|
+
* :kilobyte
|
107
|
+
* :megabyte
|
108
|
+
* :gigabyte
|
109
|
+
* :terabtye
|
110
|
+
* :petabtye
|
111
|
+
* :exabtye
|
112
|
+
* :zettabtye
|
113
|
+
* :yottabtye
|
114
|
+
|
115
|
+
Additionally, ANY matching pattern in the string is added to the total, so a string such as "1MB 1megabyte" would yield the equivalent of "2MB". File sizes can also be intermingled with any other text, so "The file is 2 megabytes in size." would successfully parse the file size as 2 megabytes.
|
116
|
+
|
117
|
+
#### Other Methods
|
118
|
+
|
119
|
+
**string_to_file**
|
120
|
+
|
121
|
+
This method is a convenient way to write a string to disk as file. It simply takes a path and a string. By default if the path does not exist it will attempt to create it. This can be controlled using the mkpath argument.
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
# Write a string to disk
|
125
|
+
string = "This is my wonderful string."
|
126
|
+
BBLib.string_to_file '/home/user/my_file', string
|
127
|
+
|
128
|
+
# OR to avoid the creation of the path if it doesn't exist:
|
129
|
+
|
130
|
+
BBLib.string_to_file '/home/user/my_file', string, false
|
131
|
+
|
132
|
+
# OR call the method directly on the string
|
133
|
+
|
134
|
+
string.to_file '/home/user/another_file', true
|
135
|
+
```
|
136
|
+
|
137
|
+
### Hash
|
138
|
+
|
139
|
+
#### Deep Merge
|
140
|
+
|
141
|
+
A simple implementation of a deep merge algorithm that merges two hashes including nested hashes within them. It can also merge arrays (default) within the hashes and merge values into arrays (not default) rather than overwriting the values with the right side hash.
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
h1 = ({value: 1231, array: [1, 2], hash: {a: 1, b_hash: {c: 2, d:3}}})
|
145
|
+
h2 = ({value: 5, array: [6, 7], hash: {a: 1, z: nil, b_hash: {c: 9, d:10, y:10}}})
|
146
|
+
|
147
|
+
# Default behavior merges arrays and overwrites non-array/hash values
|
148
|
+
h1.deep_merge h2
|
149
|
+
|
150
|
+
#=> {:value=>5, :array=>[1, 2, 6, 7], :hash=>{:a=>1, :b_hash=>{:c=>9, :d=>10, :y=>10}, :z=>nil}}
|
151
|
+
|
152
|
+
# Don't overwrite colliding values, instead, place them into an array together
|
153
|
+
h1.deep_merge h2, overwrite_vals: false
|
154
|
+
|
155
|
+
#=> {:value=>[1231, 5], :array=>[1, 2, 6, 7], :hash=>{:a=>[1, 1], :b_hash=>{:c=>[2, 9], :d=>[3, 10], :y=>10}, :z=>nil}}
|
156
|
+
|
157
|
+
# Don't merge arrays, instead, overwrite them.
|
158
|
+
h1.deep_merge h2, merge_arrays: false
|
159
|
+
|
160
|
+
#=> {:value=>5, :array=>[6, 7], :hash=>{:a=>1, :b_hash=>{:c=>9, :d=>10, :y=>10}, :z=>nil}}
|
161
|
+
```
|
162
|
+
|
163
|
+
A **!** version of _deep_merge_ is also available to modify the hash in place rather than returning a new hash.
|
164
|
+
|
165
|
+
#### Keys To Sym
|
166
|
+
|
167
|
+
Convert all keys within a hash (including nested keys) to symbols. This is useful after parsing json if you prefer to work with symbols rather than strings. An inplace (**!**) version of the method is also available.
|
168
|
+
|
169
|
+
```ruby
|
170
|
+
h = {"author" => "Tom Clancy", "books" => ["Rainbow Six", "The Hunt for Red October"]}
|
171
|
+
h.keys_to_sym
|
172
|
+
|
173
|
+
#=> {:author=>"Tom Clancy", :books=>["Rainbow Six", "The Hunt for Red October"]}
|
174
|
+
```
|
175
|
+
|
176
|
+
#### Reverse
|
177
|
+
|
178
|
+
Similar to reverse for Array. Calling this will reverse the current order of the Hash's keys. An inplace version is also available.
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
h = {a:1, b:2, c:3, d:4}
|
182
|
+
h.reverse
|
183
|
+
|
184
|
+
#=> {:d=>4, :c=>3, :b=>2, :a=>1}
|
185
|
+
```
|
186
|
+
|
187
|
+
### Math
|
188
|
+
|
189
|
+
|
190
|
+
### Net
|
191
|
+
Currently empty...
|
192
|
+
|
193
|
+
### String
|
194
|
+
|
195
|
+
|
196
|
+
### Time
|
197
|
+
|
198
|
+
|
199
|
+
## Development
|
200
|
+
|
201
|
+
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.
|
202
|
+
|
203
|
+
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).
|
204
|
+
|
205
|
+
## Contributing
|
206
|
+
|
207
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/bblack16/bblib. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
208
|
+
|
209
|
+
## License
|
210
|
+
|
211
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bblib.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
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{See summary for now...}
|
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
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +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
|
data/bin/setup
ADDED
data/lib/bblib.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative "bblib/version"
|
2
|
+
require_relative "string/bbstring"
|
3
|
+
require_relative "file/bbfile"
|
4
|
+
require_relative "time/bbtime"
|
5
|
+
require_relative "hash/bbhash"
|
6
|
+
require_relative "math/bbmath"
|
7
|
+
require 'fileutils'
|
8
|
+
|
9
|
+
module BBLib
|
10
|
+
|
11
|
+
CONFIGS_PATH = 'config/'
|
12
|
+
|
13
|
+
end
|
data/lib/file/bbfile.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module BBLib
|
4
|
+
|
5
|
+
# Scan for files and directories. Can be set to be recursive and can also have filters applied.
|
6
|
+
def self.scan_dir path = Dir.pwd, filter: nil, recursive: false
|
7
|
+
if !filter.nil?
|
8
|
+
filter = [filter].flatten.map{ |f| path.to_s + (recursive ? '/**/' : '/') + f.to_s }
|
9
|
+
else
|
10
|
+
filter = path.to_s + (recursive ? '/**/*' : '/*')
|
11
|
+
end
|
12
|
+
Dir.glob(filter)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Uses BBLib.scan_dir but returns only files. Mode can be used to return strings (:path) or File objects (:file)
|
16
|
+
def self.scan_files path, filter: nil, recursive: false, mode: :path
|
17
|
+
BBLib.scan_dir(path, filter: filter, recursive: recursive).map{ |f| File.file?(f) ? (mode == :file ? File.new(f) : f) : nil}.reject{ |r| r.nil? }
|
18
|
+
end
|
19
|
+
|
20
|
+
# Uses BBLib.scan_dir but returns only directories. Mode can be used to return strings (:path) or Dir objects (:dir)
|
21
|
+
def self.scan_dirs path, filter: nil, recursive: false, mode: :path
|
22
|
+
BBLib.scan_dir(path, filter: filter, recursive: recursive).map{ |f| File.directory?(f) ? (mode == :dir ? Dir.new(f) : f ) : nil}.reject{ |r| r.nil? }
|
23
|
+
end
|
24
|
+
|
25
|
+
# Shorthand method to write a string to dist. By default the path is created if it doesn't exist.
|
26
|
+
def self.string_to_file path, str, mkpath = true
|
27
|
+
if !Dir.exists?(path) && mkpath
|
28
|
+
FileUtils.mkpath File.dirname(path)
|
29
|
+
end
|
30
|
+
File.write(path, str.to_s)
|
31
|
+
end
|
32
|
+
|
33
|
+
# A file size parser for strings. Extracts any known patterns for file sizes.
|
34
|
+
def self.parse_file_size str, output: :byte
|
35
|
+
output = FILE_SIZES.keys.find{ |f| f == output || FILE_SIZES[f][:exp].include?(output.to_s.downcase) } || :byte
|
36
|
+
bytes = 0.0
|
37
|
+
FILE_SIZES.each do |k, v|
|
38
|
+
v[:exp].each do |e|
|
39
|
+
numbers = str.scan(/(?=\w|\D|\A)\d?\.?\d+[[:space:]]*#{e}(?=\W|\d|\z)/i)
|
40
|
+
numbers.each{ |n| bytes+= n.to_f * v[:mult] }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
return bytes / FILE_SIZES[output][:mult]
|
44
|
+
end
|
45
|
+
|
46
|
+
FILE_SIZES = {
|
47
|
+
byte: { mult: 1, exp: ['b', 'byt', 'byte'] },
|
48
|
+
kilobyte: { mult: 1024, exp: ['kb', 'kilo', 'k', 'kbyte', 'kilobtye'] },
|
49
|
+
megabyte: { mult: 1048576, exp: ['mb', 'mega', 'm', 'mib', 'mbyte', 'megabtye'] },
|
50
|
+
gigabyte: { mult: 1073741824, exp: ['gb', 'giga', 'g', 'gbyte', 'gigabtye'] },
|
51
|
+
terabtye: { mult: 1099511627776, exp: ['tb', 'tera', 't', 'tbyte', 'terabtye'] },
|
52
|
+
petabtye: { mult: 1125899906842624, exp: ['pb', 'peta', 'p', 'pbyte', 'petabtye'] },
|
53
|
+
exabtye: { mult: 1152921504606846976, exp: ['eb', 'exa', 'e', 'ebyte', 'exabtye'] },
|
54
|
+
zettabtye: { mult: 1180591620717411303424, exp: ['zb', 'zetta', 'z', 'zbyte', 'zettabtye'] },
|
55
|
+
yottabtye: { mult: 1208925819614629174706176, exp: ['yb', 'yotta', 'y', 'ybyte', 'yottabtye'] }
|
56
|
+
}
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
class File
|
61
|
+
def dirname
|
62
|
+
File.dirname(self.path)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class String
|
67
|
+
def to_file path, mkpath = true
|
68
|
+
BBLib.string_to_file path, self, mkpath
|
69
|
+
end
|
70
|
+
|
71
|
+
def file_name with_extension = true
|
72
|
+
self[(self.include?('/') ? self.rindex('/').to_i+1 : 0)..(with_extension ? -1 : self.rindex('.').to_i-1)]
|
73
|
+
end
|
74
|
+
|
75
|
+
def parse_file_size output: :byte
|
76
|
+
BBLib.parse_file_size(self, output:output)
|
77
|
+
end
|
78
|
+
end
|
data/lib/hash/bbhash.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
class Hash
|
2
|
+
|
3
|
+
# Merges with another hash but also merges all nested hashes and arrays/values.
|
4
|
+
# Based on method found @ http://stackoverflow.com/questions/9381553/ruby-merge-nested-hash
|
5
|
+
def deep_merge with, merge_arrays: true, overwrite_vals: true
|
6
|
+
merger = proc{ |k, v1, v2| v1.is_a?(Hash) && v2.is_a?(Hash) ? v1.merge(v2, &merger) : (merge_arrays && v1.is_a?(Array) && v2.is_a?(Array) ? (v1 + v2) : (overwrite_vals ? v2 : [v1, v2].flatten)) }
|
7
|
+
self.merge(with, &merger)
|
8
|
+
end
|
9
|
+
|
10
|
+
def deep_merge! with, merge_arrays: true, overwrite_vals: true
|
11
|
+
replace self.deep_merge(with, merge_arrays: merge_arrays, overwrite_vals: overwrite_vals)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Converts the keys of the hash as well as any nested hashes to symbols.
|
15
|
+
# Based on method found @ http://stackoverflow.com/questions/800122/best-way-to-convert-strings-to-symbols-in-hash
|
16
|
+
def keys_to_sym
|
17
|
+
self.inject({}){|memo,(k,v)| memo[k.to_sym] = (Hash === v ? v.keys_to_sym : v); memo}
|
18
|
+
end
|
19
|
+
|
20
|
+
def keys_to_sym!
|
21
|
+
replace self.keys_to_sym
|
22
|
+
end
|
23
|
+
|
24
|
+
# Reverses the order of keys in the Hash
|
25
|
+
def reverse
|
26
|
+
self.to_a.reverse.to_h
|
27
|
+
end
|
28
|
+
|
29
|
+
def reverse!
|
30
|
+
replace self.reverse
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/lib/math/bbmath.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
module BBLib
|
2
|
+
|
3
|
+
# Used to keep any numeric number between a set of bounds. Passing nil as min or max represents no bounds in that direction.
|
4
|
+
# min and max are inclusive to the allowed bounds.
|
5
|
+
def self.keep_between num, min, max
|
6
|
+
raise "Argument must be numeric: #{num} (#{num.class})" unless Numeric === num
|
7
|
+
if !min.nil? && num < min then num = min end
|
8
|
+
if !max.nil? && num > max then num = max end
|
9
|
+
return num
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
data/lib/net/bbnet.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# TO BE WRITTEN
|
@@ -0,0 +1,97 @@
|
|
1
|
+
|
2
|
+
require_relative 'matching.rb'
|
3
|
+
require_relative 'roman.rb'
|
4
|
+
require_relative 'fuzzy_matcher.rb'
|
5
|
+
|
6
|
+
module BBLib
|
7
|
+
|
8
|
+
##############################################
|
9
|
+
# General Functions
|
10
|
+
##############################################
|
11
|
+
|
12
|
+
# Quickly remove any symbols from a string leaving onl alpha-numeric characters and white space.
|
13
|
+
def self.drop_symbols str
|
14
|
+
str.gsub(/[^\w\s\d]|_/, '')
|
15
|
+
end
|
16
|
+
|
17
|
+
# Extract all integers from a string. Use extract_floats if numbers may contain decimal places.
|
18
|
+
def self.extract_integers str, convert: true
|
19
|
+
str.scan(/\d+/).map{ |d| convert ? d.to_i : d }
|
20
|
+
end
|
21
|
+
|
22
|
+
# Extracts all integers or decimals from a string into an array.
|
23
|
+
def self.extract_floats str, convert: true
|
24
|
+
str.scan(/\d+\.?\d+|\d+/).map{ |f| convert ? f.to_f : f }
|
25
|
+
end
|
26
|
+
|
27
|
+
# Alias for extract_floats
|
28
|
+
def self.extract_numbers str, convert: true
|
29
|
+
BBLib.extract_floats str, convert:convert
|
30
|
+
end
|
31
|
+
|
32
|
+
# Used to move the position of the articles 'the', 'a' and 'an' in strings for normalization.
|
33
|
+
def self.move_articles str, position = :front, capitalize = true
|
34
|
+
return str unless [:front, :back, :none].include? position
|
35
|
+
articles = ["the", "a", "an"]
|
36
|
+
articles.each do |a|
|
37
|
+
starts, ends = str.downcase.start_with?(a + ' '), str.downcase.end_with?(' ' + a)
|
38
|
+
if starts && position != :front
|
39
|
+
if position == :none
|
40
|
+
str = str[(a.length + 1)..str.length]
|
41
|
+
elsif position == :back
|
42
|
+
str = str[(a.length + 1)..str.length] + (!ends ? ", #{capitalize ? a.capitalize : a}" : '')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
if ends && position != :back
|
46
|
+
if position == :none
|
47
|
+
str = str[0..-(a.length + 2)]
|
48
|
+
elsif position == :front
|
49
|
+
str = (!starts ? "#{capitalize ? a.capitalize : a} " : '') + str[0..-(a.length + 2)]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
while str.strip.end_with?(',')
|
54
|
+
str.strip!
|
55
|
+
str.chop!
|
56
|
+
end
|
57
|
+
str
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
class String
|
63
|
+
# Multi-split. Similar to split, but can be passed an array of delimiters to split on.
|
64
|
+
def msplit delims, keep_empty: false
|
65
|
+
return [self] unless !delims.nil? && !delims.empty?
|
66
|
+
ar = [self]
|
67
|
+
delims.each do |d|
|
68
|
+
ar.map!{ |a| a.split d }
|
69
|
+
ar.flatten!
|
70
|
+
end
|
71
|
+
keep_empty ? ar : ar.reject{ |l| l.empty? }
|
72
|
+
end
|
73
|
+
|
74
|
+
def move_articles position, capitalize = true
|
75
|
+
BBLib.move_articles self, position, capitalize
|
76
|
+
end
|
77
|
+
|
78
|
+
def move_articles! position, capitalize = true
|
79
|
+
replace BBLib.move_articles(self, position, capitalize)
|
80
|
+
end
|
81
|
+
|
82
|
+
def drop_symbols
|
83
|
+
BBLib.drop_symbols self
|
84
|
+
end
|
85
|
+
|
86
|
+
def drop_symbols!
|
87
|
+
replace BBLib.drop_symbols(self)
|
88
|
+
end
|
89
|
+
|
90
|
+
def extract_integers convert: true
|
91
|
+
BBLib.extract_integers self, convert:convert
|
92
|
+
end
|
93
|
+
|
94
|
+
def extract_numbers convert: true
|
95
|
+
BBLib.extract_numbers self, convert:convert
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
|
2
|
+
module BBLib
|
3
|
+
|
4
|
+
class FuzzyMatcher
|
5
|
+
attr_reader :threshold
|
6
|
+
attr_accessor :case_sensitive, :remove_symbols, :move_articles, :convert_roman
|
7
|
+
|
8
|
+
def initialize threshold: 75, case_sensitive: true, remove_symbols: false, move_articles: false, convert_roman: true
|
9
|
+
self.threshold = threshold
|
10
|
+
@case_sensitive, @remove_symbols, @move_articles, @convert_roman = case_sensitive, remove_symbols, move_articles, convert_roman
|
11
|
+
end
|
12
|
+
|
13
|
+
# Calculates a percentage match between string a and string b.
|
14
|
+
def similarity a, b
|
15
|
+
return 100.0 if a == b
|
16
|
+
prep_strings a, b
|
17
|
+
score, total_weight = 0, ALGORITHMS.map{|a, v| v[:weight] }.inject{ |sum, w| sum+=w }
|
18
|
+
ALGORITHMS.each do |algo, vals|
|
19
|
+
next unless vals[:weight] > 0
|
20
|
+
score+= @a.send(vals[:signature], @b) * vals[:weight]
|
21
|
+
end
|
22
|
+
score / total_weight
|
23
|
+
end
|
24
|
+
|
25
|
+
# Checks to see if the match percentage between Strings a and b are equal to or greater than the threshold.
|
26
|
+
def match? a, b
|
27
|
+
similarity(a, b) >= @threshold.to_f
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the best match from array b to string a based on percent.
|
31
|
+
def best_match a, b
|
32
|
+
similarities(a, b).max_by{ |k, v| v}[0]
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns a hash of array 'b' with the percentage match to a. If sort is true, the hash is sorted desc by match percent.
|
36
|
+
def similarities a, b, sort: false
|
37
|
+
matches = Hash.new
|
38
|
+
[b].flatten.each{ |m| matches[m] = self.similarity(a, m) }
|
39
|
+
sort ? matches.sort_by{ |k, v| v }.reverse.to_h : matches
|
40
|
+
end
|
41
|
+
|
42
|
+
def threshold= threshold
|
43
|
+
@threshold = BBLib.keep_between(threshold, 0, 100)
|
44
|
+
end
|
45
|
+
|
46
|
+
def set_weight algorithm, weight
|
47
|
+
return nil unless ALGORITHMS.include? algorithm
|
48
|
+
ALGORITHMS[algorithm] = BBLib.keep_between(weight, 0, nil)
|
49
|
+
end
|
50
|
+
|
51
|
+
def algorithms
|
52
|
+
ALGORITHMS.keys
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
ALGORITHMS = {
|
58
|
+
levenshtein: {weight: 10, signature: :levenshtein_similarity},
|
59
|
+
composition: {weight: 5, signature: :composition_similarity},
|
60
|
+
numeric: {weight: 0, signature: :numeric_similarity},
|
61
|
+
phrase: {weight: 0, signature: :phrase_similarity},
|
62
|
+
qwerty: {weight: 0, signature: :qwerty_similarity}
|
63
|
+
}
|
64
|
+
|
65
|
+
def prep_strings a, b
|
66
|
+
@a, @b = a.to_s.dup.strip, b.to_s.dup.strip
|
67
|
+
if !@case_sensitive then @a.downcase!; @b.downcase! end
|
68
|
+
if @remove_symbols then @a.drop_symbols!; @b.drop_symbols! end
|
69
|
+
if @convert_roman then @a.from_roman!; @b.from_roman! end
|
70
|
+
if @move_articles then @a.move_articles!(:front, @case_sensitive); @b.move_articles! :front, @case_sensitive end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
##############################################
|
2
|
+
# String Comparison Algorithms
|
3
|
+
##############################################
|
4
|
+
|
5
|
+
module BBLib
|
6
|
+
|
7
|
+
# A simple rendition of the levenshtein distance algorithm
|
8
|
+
def self.levenshtein_distance a, b, case_sensitive = false
|
9
|
+
if !case_sensitive then a, b = a.downcase, b.downcase end
|
10
|
+
costs = (0..b.length).to_a
|
11
|
+
(1..a.length).each do |i|
|
12
|
+
costs[0], nw = i, i - 1
|
13
|
+
(1..b.length).each do |j|
|
14
|
+
costs[j], nw = [costs[j] + 1, costs[j-1] + 1, a[i-1] == b[j-1] ? nw : nw + 1].min, costs[j]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
costs[b.length]
|
18
|
+
end
|
19
|
+
|
20
|
+
# Calculates a percentage based match using the levenshtein distance algorithm
|
21
|
+
def self.levenshtein_similarity a, b, case_sensitive = false
|
22
|
+
distance = BBLib.levenshtein_distance a, b, case_sensitive
|
23
|
+
max = [a.length, b.length].max.to_f
|
24
|
+
return ((max - distance.to_f) / max) * 100.0
|
25
|
+
end
|
26
|
+
|
27
|
+
# Calculates a percentage based match of two strings based on their character composition.
|
28
|
+
def self.composition_similarity a, b, case_sensitive = false
|
29
|
+
if !case_sensitive then a, b = a.downcase, b.downcase end
|
30
|
+
if a.length <= b.length then t = a; a = b; b = t; end
|
31
|
+
matches, temp = 0, b
|
32
|
+
a.chars.each do |c|
|
33
|
+
if temp.chars.include? c
|
34
|
+
matches+=1
|
35
|
+
temp.sub! c, ''
|
36
|
+
end
|
37
|
+
end
|
38
|
+
(matches / [a.length, b.length].max.to_f )* 100.0
|
39
|
+
end
|
40
|
+
|
41
|
+
# Calculates a percentage based match between two strings based on the similarity of word matches.
|
42
|
+
def self.phrase_similarity a, b, case_sensitive = false
|
43
|
+
if !case_sensitive then a, b = a.downcase, b.downcase end
|
44
|
+
temp = b.split ' '
|
45
|
+
matches = 0
|
46
|
+
a.split(' ').each do |w|
|
47
|
+
if temp.include? w
|
48
|
+
matches+=1
|
49
|
+
temp.delete_at temp.find_index w
|
50
|
+
end
|
51
|
+
end
|
52
|
+
(matches.to_f / [a.split(' ').size, b.split(' ').size].max.to_f) * 100.0
|
53
|
+
end
|
54
|
+
|
55
|
+
# Extracts all numbers from two strings and compares them and generates a percentage of match.
|
56
|
+
# Percentage calculations here need to be weighted better...TODO
|
57
|
+
def self.numeric_similarity a, b, case_sensitive = false
|
58
|
+
if !case_sensitive then a, b = a.downcase, b.downcase end
|
59
|
+
a, b = a.extract_numbers, b.extract_numbers
|
60
|
+
return 100.0 if a.empty? && b.empty?
|
61
|
+
matches = []
|
62
|
+
for i in 0..[a.size, b.size].max-1
|
63
|
+
matches << 1.0 / ([a[i].to_f, b[i].to_f].max - [a[i].to_f, b[i].to_f].min + 1.0)
|
64
|
+
end
|
65
|
+
(matches.inject{ |sum, m| sum + m } / matches.size.to_f) * 100.0
|
66
|
+
end
|
67
|
+
|
68
|
+
# A simple character distance calculator that uses qwerty key positions to determine how similar two strings are.
|
69
|
+
# May be useful for typo detection.
|
70
|
+
def self.qwerty_similarity a, b
|
71
|
+
a, b = a.downcase.strip, b.downcase.strip
|
72
|
+
if a.length <= b.length then t = a; a = b; b = t; end
|
73
|
+
qwerty = {
|
74
|
+
1 => ['1','2','3','4','5','6','7','8','9','0'],
|
75
|
+
2 => ['q','w','e','r','t','y','u','i','o','p'],
|
76
|
+
3 => ['a','s','d','f','g','h','j','k','l'],
|
77
|
+
4 => ['z','x','c','v','b','n','m']
|
78
|
+
}
|
79
|
+
count, offset = 0, 0
|
80
|
+
a.chars.each do |c|
|
81
|
+
if b.length <= count
|
82
|
+
offset+=10
|
83
|
+
else
|
84
|
+
ai = qwerty.keys.find{ |f| qwerty[f].include? c }.to_i
|
85
|
+
bi = qwerty.keys.find{ |f| qwerty[f].include? b.chars[count] }.to_i
|
86
|
+
offset+= (ai - bi).abs
|
87
|
+
offset+= (qwerty[ai].index(c) - qwerty[bi].index(b.chars[count])).abs
|
88
|
+
end
|
89
|
+
count+=1
|
90
|
+
end
|
91
|
+
offset
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class String
|
96
|
+
def levenshtein_distance str, case_sensitive = false
|
97
|
+
BBLib.levenshtein_distance self, str, case_sensitive
|
98
|
+
end
|
99
|
+
|
100
|
+
def levenshtein_similarity str, case_sensitive = false
|
101
|
+
BBLib.levenshtein_similarity self, str, case_sensitive
|
102
|
+
end
|
103
|
+
|
104
|
+
def composition_similarity str, case_sensitive = false
|
105
|
+
BBLib.composition_similarity self, str, case_sensitive
|
106
|
+
end
|
107
|
+
|
108
|
+
def phrase_similarity str, case_sensitive = false
|
109
|
+
BBLib.phrase_similarity self, str, case_sensitive
|
110
|
+
end
|
111
|
+
|
112
|
+
def numeric_similarity str, case_sensitive = false
|
113
|
+
BBLib.numeric_similarity self, str, case_sensitive
|
114
|
+
end
|
115
|
+
|
116
|
+
def qwerty_similarity str
|
117
|
+
BBLib.qwerty_similarity self, str
|
118
|
+
end
|
119
|
+
end
|
data/lib/string/roman.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
|
2
|
+
module BBLib
|
3
|
+
|
4
|
+
# Converts any integer up to 1000 to a roman numeral string_a
|
5
|
+
def self.to_roman num
|
6
|
+
roman = {1000 => 'M', 900 => 'CM', 500 => 'D', 400 => 'CD', 100 => 'C', 90 => 'XC', 50 => 'L',
|
7
|
+
40 => 'XL', 10 => 'X', 9 => 'IX', 5 => 'V', 4 => 'IV', 3 => 'III', 2 => 'II', 1 => 'I'}
|
8
|
+
numeral = ""
|
9
|
+
roman.each do |n, r|
|
10
|
+
if num >= n
|
11
|
+
num-= n
|
12
|
+
numeral+= r
|
13
|
+
end
|
14
|
+
end
|
15
|
+
numeral
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.string_to_roman str
|
19
|
+
sp = str.split ' '
|
20
|
+
sp.map! do |s|
|
21
|
+
if s.to_i.to_s == s
|
22
|
+
BBLib.to_roman s.to_i
|
23
|
+
else
|
24
|
+
s
|
25
|
+
end
|
26
|
+
end
|
27
|
+
sp.join ' '
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def self.from_roman str
|
32
|
+
sp = str.split(' ')
|
33
|
+
(0..1000).each do |n|
|
34
|
+
num = BBLib.to_roman n
|
35
|
+
if !sp.select{ |i| i[/#{num}/i]}.empty?
|
36
|
+
for i in 0..(sp.length-1)
|
37
|
+
if sp[i].upcase == num
|
38
|
+
sp[i] = n.to_s
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
sp.join ' '
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
class Numeric
|
49
|
+
def to_roman
|
50
|
+
BBLib.to_roman self.to_i
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class String
|
55
|
+
def from_roman
|
56
|
+
BBLib.from_roman self
|
57
|
+
end
|
58
|
+
|
59
|
+
def from_roman!
|
60
|
+
replace self.from_roman
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_roman
|
64
|
+
BBLib.string_to_roman self
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_roman!
|
68
|
+
replace self.to_roman
|
69
|
+
end
|
70
|
+
end
|
data/lib/time/bbtime.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module BBLib
|
4
|
+
|
5
|
+
# Parses known time based patterns out of a string to construct a numeric duration.
|
6
|
+
def self.parse_duration str, output: :sec
|
7
|
+
secs = 0.0
|
8
|
+
TIME_EXPS.each do |k, v|
|
9
|
+
v[:exp].each do |e|
|
10
|
+
numbers = str.downcase.scan(/(?=\w|\D|\A)\d?\.?\d+[[:space:]]*#{e}(?=\W|\d|\z)/i)
|
11
|
+
numbers.each do |n|
|
12
|
+
secs+= n.to_i * v[:mult]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
secs / (TIME_EXPS[output][:mult].to_f rescue 1)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Turns a numeric input into a time string.
|
20
|
+
def self.to_duration num, input: :sec, stop: :mili, style: :medium
|
21
|
+
return nil unless Numeric === num || num > 0
|
22
|
+
if ![:full, :medium, :short].include?(style) then style = :medium end
|
23
|
+
expression = []
|
24
|
+
n, done = num * TIME_EXPS[input.to_sym][:mult], false
|
25
|
+
TIME_EXPS.reverse.each do |k, v|
|
26
|
+
next unless !done
|
27
|
+
div = n / v[:mult]
|
28
|
+
if div > 1
|
29
|
+
expression << "#{div.floor} #{v[:styles][style]}#{div.floor > 1 && style != :short ? "s" : nil}"
|
30
|
+
n-= div.floor * v[:mult]
|
31
|
+
end
|
32
|
+
if k == stop then done = true end
|
33
|
+
end
|
34
|
+
expression.join ' '
|
35
|
+
end
|
36
|
+
|
37
|
+
TIME_EXPS = {
|
38
|
+
mili: {
|
39
|
+
mult: 0.001,
|
40
|
+
styles: {full: 'milisecond', medium: 'mili', short: 'ms'},
|
41
|
+
exp: ['ms', 'mil', 'mils', 'mili', 'milis', 'milisecond', 'miliseconds', 'milsec', 'milsecs', 'msec', 'msecs', 'msecond', 'mseconds']},
|
42
|
+
sec: {
|
43
|
+
mult: 1,
|
44
|
+
styles: {full: 'second', medium: 'sec', short: 's'},
|
45
|
+
exp: ['s', 'sec', 'secs', 'second', 'seconds']},
|
46
|
+
min: {
|
47
|
+
mult: 60,
|
48
|
+
styles: {full: 'minute', medium: 'min', short: 'm'},
|
49
|
+
exp: ['m', 'mn', 'mns', 'min', 'mins', 'minute', 'minutes']},
|
50
|
+
hour: {
|
51
|
+
mult: 3600,
|
52
|
+
styles: {full: 'hour', medium: 'hr', short: 'h'},
|
53
|
+
exp: ['h', 'hr', 'hrs', 'hour', 'hours']},
|
54
|
+
day: {
|
55
|
+
mult: 86400,
|
56
|
+
styles: {full: 'day', medium: 'day', short: 'd'},
|
57
|
+
exp: ['d', 'day' 'days']},
|
58
|
+
week: {
|
59
|
+
mult: 604800,
|
60
|
+
styles: {full: 'week', medium: 'wk', short: 'w'},
|
61
|
+
exp: ['w', 'wk', 'wks', 'week', 'weeks']},
|
62
|
+
month: {
|
63
|
+
mult: 2592000,
|
64
|
+
styles: {full: 'month', medium: 'mo', short: 'mo'},
|
65
|
+
exp: ['mo', 'mon', 'mons', 'month', 'months', 'mnth', 'mnths', 'mth', 'mths']},
|
66
|
+
year: {
|
67
|
+
mult: 31536000,
|
68
|
+
styles: {full: 'year', medium: 'yr', short: 'y'},
|
69
|
+
exp: ['y', 'yr', 'yrs', 'year', 'years']}
|
70
|
+
}
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
class String
|
75
|
+
def parse_duration to = :sec
|
76
|
+
BBLib.parse_duration self, to
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class Numeric
|
81
|
+
def to_duration input: :sec, stop: :mili, style: :medium
|
82
|
+
BBLib.to_duration self, input: input, stop: stop, style: style
|
83
|
+
end
|
84
|
+
end
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bblib
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brandon Black
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-01-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: See summary for now...
|
56
|
+
email:
|
57
|
+
- d2sm10@hotmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- ".rspec"
|
64
|
+
- ".travis.yml"
|
65
|
+
- CODE_OF_CONDUCT.md
|
66
|
+
- Gemfile
|
67
|
+
- LICENSE.txt
|
68
|
+
- README.md
|
69
|
+
- Rakefile
|
70
|
+
- bblib.gemspec
|
71
|
+
- bin/console
|
72
|
+
- bin/setup
|
73
|
+
- lib/bblib.rb
|
74
|
+
- lib/bblib/version.rb
|
75
|
+
- lib/file/bbfile.rb
|
76
|
+
- lib/hash/bbhash.rb
|
77
|
+
- lib/math/bbmath.rb
|
78
|
+
- lib/net/bbnet.rb
|
79
|
+
- lib/string/bbstring.rb
|
80
|
+
- lib/string/fuzzy_matcher.rb
|
81
|
+
- lib/string/matching.rb
|
82
|
+
- lib/string/roman.rb
|
83
|
+
- lib/time/bbtime.rb
|
84
|
+
homepage: https://github.com/bblack16/bblib-ruby
|
85
|
+
licenses:
|
86
|
+
- MIT
|
87
|
+
metadata: {}
|
88
|
+
post_install_message:
|
89
|
+
rdoc_options: []
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
requirements: []
|
103
|
+
rubyforge_project:
|
104
|
+
rubygems_version: 2.4.8
|
105
|
+
signing_key:
|
106
|
+
specification_version: 4
|
107
|
+
summary: A library containing many reusable, basic functions.
|
108
|
+
test_files: []
|