README.md in bblib-0.2.0 vs README.md in bblib-0.2.2
- old
+ new
@@ -1,13 +1,44 @@
# BBLib
-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.
+BBLib (Brandon-Black-Lib) is a collection of various reusable methods and classes to extend the Ruby language.
-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.
+One of the primary goals with the BBLib is to keep it as lightweight as possible. This means you will not find dependencies outside of the Ruby core libraries.
-For a full breakdown of what is currently in this library, scroll down.
+For a full breakdown of what is currently in this library, scroll down. For a quick overview of key features, read the following list.
+* __BBLib HashPath:__ Hash path is an XPath or JSONPath like navigation library for native Ruby hashes. It uses dot ('.') delimited path strings to navigate hash AND array objects. What makes hash path stand out is that it can navigate recursively within hashes, arrays, nested hashes, nested arrays, nested hashes within nested arrays within nested arrays with...well, you get the picture. Not only does it support navigation of hashes, it also comes with many functions to easily manipulate hashes by moving paths, copying paths, deleting paths or processing paths (see below for a few examples).
+
+```ruby
+myhash = {a:1, b:2, c:{d:[3,4,{e:5},6]}, f:7}
+p myhash.hash_path('c.d..e')
+#=> [5]
+p myhash.hash_path('..d')
+#=> [3, 4, {:e=>5}, 6]
+p myhash.hash_path('c.d[1]')
+#=> [4]
+p myhash.hash_path('c.d[0..1]')
+#=> [3, 4]
+
+# Move key/values
+p myhash.hash_path_move('a' => 'c.g.h')
+#=> {:b=>2, :c=>{:d=>[3, 4, {:e=>5}, 6], :g=>{:h=>1}}, :f=>7}
+
+# Copy key/values
+p myhash.hash_path_copy('b' => 'z')
+#=> {:a=>1, :b=>2, :c=>{:d=>[3, 4, {:e=>5}, 6]}, :f=>7, :z=>2}
+```
+* __Deep Merge:__ A deep merge algorithm is included that can merge hashes with nested hashes or nested arrays or nested hashes with nested arrays with nested hashes and so on... It can also combine colliding values into arrays rather than overwriting using a toggle-able overwrite flag.
+* __File & Time Parsing From Strings:__ Have a string such as '1MB 15KB' and want to make it numeric? Look no further. BBLib has methods to parse files size expressions and duration expressions from strings (like '1min 10sec'). Nearly any variant of size or duration expression is supported. For instance, '1sec', '1s', '1 s', '1 second', '1secs' are all properly parsed as 1 second.
+* __Fuzzy String Matching:__ The BBLib has implementations of a few string comparison algorithms. Most noteworthy, it features a simple implementation of the Levenshtein distance algorithm. A class, FuzzyMatcher, is also included to perform weight comparisons of strings using any of the included algorithms.
+* __Convert Roman Numerals__ within strings to Integers and Integers to Roman Numerals.
+* __Normalize articles__ such as 'the', 'an' and 'a' within titles to be displayed in the front, back or be stripped entirely. Helpful for sorting titles or for string comparisons.
+* __Object to Hash:__ Turn any object and its instance variables as well as nested objects and their instance variables into a hash. Handy to have alongside hash path.
+* __TaskTimer:__ A simple and easy to use timer class to compliment benchmarking in code by timing various tasks or groups of tasks. History is kept so that averages, sums, mins and maxes can be checked per task.
+* __Recursive File Scanners:__ A few file and directory scanners are implemented that recursively (by toggle) scan directories looking for files matching given filters.
+* __Plus more...__
+
## Installation
Add this line to your application's Gemfile:
```ruby
@@ -22,20 +53,10 @@
$ gem install bblib
## Usage
-BBLib is currently broken up into the following categories:
-* File
-* Hash
-* Math
-* Net
-* String
-* Time
-
-
-
### File
#### File Scanners
Various simple file scan methods are available. All of these are toggleable-recursive and can be passed filters using any wildcarding supported by the Ruby Dir.glob() method.
@@ -73,11 +94,11 @@
#=> 'C:/path/to/files/license.txt'
#=> 'C:/path/to/files/folder/profile.jpg'
#=> 'C:/path/to/files/folder/another_folder/text.txt'
```
-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.
+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. Similarly, if :dir is passed to _scan_dirs_ a ruby Dir object is returned, rather than just a string.
#### File Size Parsing
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.
@@ -157,22 +178,28 @@
# Don't merge arrays, instead, overwrite them.
h1.deep_merge h2, merge_arrays: false
#=> {:value=>5, :array=>[6, 7], :hash=>{:a=>1, :b_hash=>{:c=>9, :d=>10, :y=>10}, :z=>nil}}
```
-A **!** version of _deep_merge_ is also available to modify the hash in place rather than returning a new hash.
+A ! version of _deep_merge_ is also available to modify the hash in place rather than returning a new hash.
#### Keys To Sym
-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.
+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 in-place (**!**) version of the method is also available.
```ruby
h = {"author" => "Tom Clancy", "books" => ["Rainbow Six", "The Hunt for Red October"]}
h.keys_to_sym
#=> {:author=>"Tom Clancy", :books=>["Rainbow Six", "The Hunt for Red October"]}
```
+_Note: This is similar to what Rails provides, except it even converts keys within nested hashes or nested arrays that contain nested hashes._
+
+#### Keys To Str
+
+The same as keys to sym, but it converts keys to strings rather than symbols.
+
#### Reverse
Similar to reverse for Array. Calling this will reverse the current order of the Hash's keys. An in place version is also available.
The code behind this is based on a method found @ http://stackoverflow.com/questions/800122/best-way-to-convert-strings-to-symbols-in-hash
@@ -205,15 +232,10 @@
#=> -250
```
-### Net
-Currently empty...
-
-
-
### String
#### FuzzyMatcher
FuzzyMatcher (BBLib::FuzzyMatcher) is a class for making fuzzy comparisons with strings. It implements a weighted algorithm system which uses the algorithms listed below to generate a percentage based match between two strings. There are various settings that can be toggled in addition. These settings are:
@@ -299,14 +321,33 @@
'ruby, ruby, ruby'.phrase_similarity 'ruby ruby'
#=> 66.66666666666666
```
-4 - Numeric Similarity (In Progress)
+4 - Numeric Similarity _(In Progress)_
This algorithm is currently undergoing refactoring...
+This is primarily for comparing titles (such as movie or game titles). As an example, other algorithms would conclude that _'Terminator 2'_ is more similar to _'Terminator'_ than _'Terminator 2: Judgement Day'_, but the best match may really be _'Terminator 2: Judgement Day'_. To fix this, the numeric similarity would weight more towards the more appropriate title that contains the same number or numbers as itself. A string with no numbers is effectively considered to include a 1 for comparison's sake.
+
+```ruby
+a = 'Terminator 2'
+b = 'Terminator 2: Judgement Day'
+c = 'Terminator'
+
+puts a.levenshtein_similarity c
+#=> 83.33333333333334
+puts a.numeric_similarity c
+#=> 33.33333333333333
+
+puts a.levenshtein_similarity b
+#=> 44.44444444444444
+puts a.numeric_similarity b
+#=> 100.0
+```
+This algorithm is generally only useful when combined with another algorithm, which is exactly what the FuzzyMatcher class does.
+
5 - QWERTY Similarity
A basic method that compares two strings by measuring the physical difference from one char to another on a QWERTY keyboard (alpha-numeric only). May be useful for detecting typos in words, but becomes less useful depending on the length of the string. This method is still in development and not yet in a final state. Currently a total distance is returned. Eventually, a percentage based match will replace this.
```ruby
@@ -369,70 +410,115 @@
#### Other
**msplit** _aka multi split_
-_msplit_ is similar to the String method split, except it can take an array of string delimiters rather than a single delim. The string is split be each delimiter in order and an Array is returned.
+_msplit_ is similar to the String method split, except it can take an array of string delimiters rather than a single delimiter. The string is split be each delimiter in order and an Array is returned. msplit may also be called on an array to split elements within it.
```ruby
-"This_is.a&&&&test".msplit ['_', '.', '&']
+"This_is.a&&&&test".msplit '_', '.', '&'
#=> ['This', 'is', 'a', 'test']
```
-By default any empty items from the return Array are removed. This behavior can be changed using the _:keep_empty_ named param.
+By default any empty items from the returned Array are removed. This behavior can be changed using the _:keep_empty_ named param.
```ruby
"This_is.a&&&&test".msplit ['_', '.', '&'], keep_empty: true
#=> ['This', 'is', 'a', '', '', '', 'test']
```
-_msplit is only available directly from an instantiated String object._
-
**move_articles**
This method is used to normalize strings that contain titles. It parses a string and checks to see if _the_, _an_ or _a_ are in the title, either preceding or trailing. If they are found they are moved to the front, back or removed depending on the argument passed to _position_.
The method is available via the BBLib module or any instance of String.
```ruby
title = "The Simpsons"
title.move_articles :back
-
#=> "Simpons, The"
title.move_articles :none
-
#=> "Simpsons"
title = "Day to Remember, A"
title.move_articles :front
-
#=> "A Day to Remember"
```
-**drop_symbols**
+**extract_integers**/**extract_floats**/**extract_numbers**
-A simple method to remove all non-alpha, non-numeric and non-whitespace characters from a string. Extended to the String class.
+Three methods to grab numbers from within strings. Integers only nabs numbers with no decimal places, floats gets only numbers with a decimal and numbers gets both integers and floats. The numbers must also be properly formatted, so something like the version number '2.1.1' below will not be extracted.
-**extract_integers**
+```ruby
+s = 'Test 10 2.5 Number 100 aaaa 10.113 Version 2.1.1'
-Returns an array of all integers found within a string. The named param _:convert_ can be set to true to convert the extract numbers into Fixnums. If left false, strings are returned instead.
+p s.extract_integers
+#=> [10, 100]
+p s.extract_floats
+#=> [2.5, 10.113]
+p s.extract_numbers
+#=> [10, 2.5, 100, 10.113]
+```
-**extract_floats**
+### Time
-Performs the same action as _extract_integers_ except it can also pull floats from a string. The _:convert_ param is also available, but converts the strings into floats.
+#### Cron
-**extract_numbers**
+BBLib includes a lightweight cron syntax parser. It can be used to display the runtimes of a cron based on a cron string. Nearly every variant of cron syntax is supported with the ability to intermix ranges, divisors and explicit numbers in the same interval placing.
-See above. Is an alias for _extract_floats_.
+```ruby
+cron = BBLib::Cron.new('* * * * * *')
+puts cron.next
+#=> 2016-04-03 22:01:00 -0600
+puts cron.previous
+#=> 2016-04-03 21:59:00 -0600
-### Time
+p cron.next(5)
+#=> [2016-04-03 22:01:00 -0600, 2016-04-03 22:02:00 -0600, 2016-04-03 22:03:00 -0600, 2016-04-03 22:04:00 -0600, 2016-04-03 22:05:00 -0600]
+# Set the time explicitly. The default is the current system time.
+puts cron.next(time: Time.now+30)
+#=> 2016-04-03 22:31:00 -0600
+```
+
+An instantiated Cron object is not necessary to get the next and previous times.
+
+```ruby
+puts BBLib::Cron.next('* * * * * *')
+#=> 2016-04-03 22:04:00 -0600
+
+puts BBLib::Cron.next('0-5 * * * * *')
+#=> 2016-04-03 22:04:00 -0600
+
+puts BBLib::Cron.next('0 1 1 1 1 *')
+#=> 2018-01-01 01:00:00 -0700
+
+puts BBLib::Cron.next('1 1 1-5 * * 2020')
+#=> 2020-01-01 01:01:00 -0700
+
+puts BBLib::Cron.next('*/5 * * * * *')
+#=> 2016-04-03 22:05:00 -0600
+
+puts BBLib::Cron.next('1-3,4,5,10-11 1-10 */5 * * *')
+#=> 2016-04-06 01:01:00 -0600
+```
+
+Common vixieisms are also supported:
+
+```ruby
+puts BBLib::Cron.next('@daily')
+#=> 2016-04-04 00:00:00 -0600
+
+puts BBLib::Cron.next('@weekly')
+#=> 2016-04-10 00:00:00 -0600
+```
+
#### Duration parser
**Parsing a duration from String**
Similar to the file size parser under the files section, but instead can parse duration from know time patterns in a string. By default the result is returned in seconds, but this can be changed using the named param _:output_. The method is also extended to the String class directly.
@@ -445,17 +531,38 @@
"1hr 10 minutes 11s".parse_duration output: :hour
#=> 1.1697222222222223
```
Output options are:
-* :mili
+* :yocto
+* :zepto
+* :atto
+* :femto
+* :pico
+* :nano
+* :micro
+* :milli
* :sec
* :min
* :hour
* :day
* :week
* :month
* :year
+
+__WARNING:__ _time intervals below microseconds are prone to heavy rounding errors in the current implementation. They are NOT EXACT._
+
+The colon separated duration pattern (eg. '02:30') can also be matched. The last set of digits is treated as seconds with each prior number being one interval greater. The default starting interval can be changed using the __min_interval__ named param. The available options are the same as the output options. This pattern type can even be intermixed with the types shown above and will be added to the total duration.
+
+```ruby
+duration = '04:35'
+
+puts duration.parse_duration
+#=> 275.0
+
+puts duration.parse_duration min_interval: :min
+#=> 16500.0
+```
**Create a duration String from Numeric**
There is also a method to turn a Numeric object into a string representation of a duration. This method is extended to the Numeric class. An input may be specified to tell the method what the input number represents. The options for this are the same as the output options listed above. A stop can be added using any of those same options. This will prevent the string from containing anything below the specified time type. For instance, specifying _stop: :sec_ will prevent milliseconds from being included if there are any. There are also three options that can be passed to the _:style_ argument to change the output (options are _:full_, _:medium_ and _:short:).