FatCore
fat-core
is a simple gem to collect core extensions and a few new classes
that I find useful in multiple projects. The emphasis is on extending the
Date class to make it more useful in financial applications.
Installation
Add this line to your application’s Gemfile:
gem 'fat_core', :git => 'https://github.com/ddoherty03/fat_core.git'
And then execute:
$ bundle
Or install it yourself as:
$ gem install fat_core
Usage
You can extend classes individually by requiring the corresponding file:
require 'fat_core/array'
require 'fat_core/bigdecimal'
require 'fat_core/date'
require 'fat_core/enumerable'
require 'fat_core/hash'
require 'fat_core/kernel'
require 'fat_core/numeric'
require 'fat_core/range'
require 'fat_core/string'
require 'fat_core/symbol'
Or, you can require them all:
require 'fat_core/all'
Many of these have little that is of general interest, but there are a few goodies.
Date
Constants
FatCore
adds two constants to the Date
class, Date::BOT and Date::EOT.
These represent the earliest and latest dates of practical commercial
interest. The exact values are rather arbitrary, but they prove useful in
date ranges, for example. They are defined as:
Date::BOT
- January 1, 1900
Date::EOT
- December 31, 3000
Date::FEDERAL_DECREED_HOLIDAYS
- an Array of dates declared as non-work days for federal employees by presidential proclamation
Date::PRESIDENTIAL_FUNERALS
- an Array of dates of presidential funerals, which are observed with a closing of most federal agencies
Formatting
FatCore
provides some concise methods for printing string versions of dates
that are often useful:
require 'fat_core/date'
d = Date.parse('1957-09-22')
puts "ISO: #{d.iso}"
puts "All Numbers: #{d.num}"
puts "Emacs Org Mode Inactive: #{d.org}"
puts "Emacs Org Mode Active: #{d.org(true)}"
puts "LaTeX: #{d.tex_quote}"
puts "English: #{d.eng}"
puts "American: #{d.american}"
ISO: 1957-09-22 All Numbers: 19570922 Emacs Org Mode Inactive: [1957-09-22 Sun] Emacs Org Mode Active: <1957-09-22 Sun> LaTeX: 1957--09--22 English: September 22, 1957 American: 9/22/1957
Most of these are self-explanatory, but a couple are not. The #org
method
formats a date as an Emacs org-mode timestamp, by default an inactive
timestamp that does not show up in the org agenda, but can be made active with
the optional parameter set to a truthy value. See
https://orgmode.org/manual/Timestamps.html#Timestamps.
The #tex_quote
method formats the date in iso form but using TeX’s
convention of using en-dashes to separate the components.
Chunks
Many of the methods provided by FatCore
deal with various calendar periods
that are less common than those provided by the Ruby Standard Library or gems
such as active_suupor
. This documentation refers to these calendar periods
as “chunks”, and they are the following:
- year,
- half,
- quarter,
- bimonth,
- month,
- semimonth,
- biweek,
- week, and
- day
FatCore
provides methods that query whether the date falls on the beginning
or end of each of these chunks:
require 'fat_core/date'
tab = []
d = Date.parse('2017-06-30')
%i[beginning end].each do |side|
%i(year half quarter bimonth month semimonth biweek week ).each do |chunk|
meth = "#{side}_of_#{chunk}?".to_sym
tab << [d.iso, meth.to_s, "#{d.send(meth)}"]
end
end
tab
| 2017-06-30 | beginning_of_year? | false | | 2017-06-30 | beginning_of_half? | false | | 2017-06-30 | beginning_of_quarter? | false | | 2017-06-30 | beginning_of_bimonth? | false | | 2017-06-30 | beginning_of_month? | false | | 2017-06-30 | beginning_of_semimonth? | false | | 2017-06-30 | beginning_of_biweek? | false | | 2017-06-30 | beginning_of_week? | false | | 2017-06-30 | end_of_year? | false | | 2017-06-30 | end_of_half? | true | | 2017-06-30 | end_of_quarter? | true | | 2017-06-30 | end_of_bimonth? | true | | 2017-06-30 | end_of_month? | true | | 2017-06-30 | end_of_semimonth? | true | | 2017-06-30 | end_of_biweek? | false | | 2017-06-30 | end_of_week? | false |
It also provides corresponding methods that return the date at the beginning or end of the calendar chunk, starting at the given date:
require 'fat_core/date'
tab = []
d = Date.parse('2017-04-21')
%i[beginning end].each do |side|
%i(year half quarter bimonth month semimonth biweek week ).each do |chunk|
meth = "#{side}_of_#{chunk}".to_sym
tab << [d.iso, "d.#{meth}", "#{d.send(meth)}"]
end
end
tab
| 2017-04-21 | d.beginning_of_year | 2017-01-01 | | 2017-04-21 | d.beginning_of_half | 2017-01-01 | | 2017-04-21 | d.beginning_of_quarter | 2017-04-01 | | 2017-04-21 | d.beginning_of_bimonth | 2017-03-01 | | 2017-04-21 | d.beginning_of_month | 2017-04-01 | | 2017-04-21 | d.beginning_of_semimonth | 2017-04-16 | | 2017-04-21 | d.beginning_of_biweek | 2017-04-10 | | 2017-04-21 | d.beginning_of_week | 2017-04-17 | | 2017-04-21 | d.end_of_year | 2017-12-31 | | 2017-04-21 | d.end_of_half | 2017-06-30 | | 2017-04-21 | d.end_of_quarter | 2017-06-30 | | 2017-04-21 | d.end_of_bimonth | 2017-04-30 | | 2017-04-21 | d.end_of_month | 2017-04-30 | | 2017-04-21 | d.end_of_semimonth | 2017-04-30 | | 2017-04-21 | d.end_of_biweek | 2017-04-23 | | 2017-04-21 | d.end_of_week | 2017-04-23 |
You can query which numerical half, quarter, etc. that a given date falls in:
require 'fat_core/date'
tab = []
%i(year half quarter bimonth month semimonth biweek week ).each do |chunk|
d = Date.parse('2017-04-21') + rand(100)
meth = "#{chunk}".to_sym
tab << [d.iso, "d.#{meth}", "in #{chunk} number #{d.send(meth)}"]
end
tab
| 2017-07-05 | d.year | in year number 2017 | | 2017-06-03 | d.half | in half number 1 | | 2017-05-30 | d.quarter | in quarter number 2 | | 2017-07-08 | d.bimonth | in bimonth number 4 | | 2017-06-28 | d.month | in month number 6 | | 2017-05-14 | d.semimonth | in semimonth number 9 | | 2017-07-25 | d.biweek | in biweek number 15 | | 2017-06-19 | d.week | in week number 25 |
Parsing
FatCore
also adds some convenience methods for parsing strings as Date
objects.
American Dates
Americans often write dates in the form M/d/Y, and the normal parse method
will parse such a string as d/M/Y, often resulting in invalid date errors.
FatCore
adds the specialty parsing method, Date.parse_american
to handle
such strings.
require 'fat_core/date'
begin
ss = '9/22/1957'
Date.parse(ss)
rescue Date::Error => ex
puts "Date.parse('#{ss}') raises #{ex.class} (#{ex}), but"
puts "Date.parse_american('#{ss}') => #{Date.parse_american(ss)}"
end
Date.parse('9/22/1957') raises Date::Error (invalid date), but Date.parse_american('9/22/1957') => 1957-09-22
Date Specs
Holidays and Workdays
- weekend?
- weekday?
Weekdays in Month
Easter
The Date
class extension adds two methods for determining whether a given
date is a US federal holiday as defined by federal law, including such things
as federal holidays established by executive decree:
require 'fat_core/date'
Date.parse('2014-05-18').fed_holiday? => true # It's a weekend
Date.parse('2014-01-01').fed_holiday? => true # It's New Years
Likewise, days on which the NYSE is closed can be gotten with:
Date.parse('2014-04-18').nyse_holiday? => true # It's Good Friday
Conversely, Date#fed_workday?
and Date#nyse_workday?
return true if the
federal government and the NYSE respectively are open for business on those
days.
In addition, the Date class, as extended by FatCore, adds #next_<chunk>
methods for calendar periods in addition to those provided by the core Date
class: #next_half
, #next_quarter
, #next_bimonth
, and #next_semimonth
,
#next_biweek
. There are also #prior_<chunk>
variants of these, as well as
methods for finding the end and beginning of all these periods (e.g.,
#beginning_of_bimonth
) and for querying whether a Date is at the beginning or
end of these periods (e.g., #beginning_of_bimonth?
, #end_of_bimonth?
, etc.).
FatCore also provides convenience formatting methods, such as Date#iso
for
quickly converting a Date to a string of the form ‘YYYY-MM-DD’, Date#org
for
formatting a Date as an Emacs org-mode timestamp, and several others.
Finally, it provides a #parse_spec
method for parsing a string, typically
provided by a user, allowing all the period chunks to be conveniently and
tersely specified by a user. For example, the string ‘2Q’ will be parsed as the
second calendar quarter of the current year, while ‘2014-3Q’ will be parsed as
the third quarter of the year 2014.
Range
You can also extend the Range class with several useful methods that emphasize
coverage of one range by one or more others (#spanned_by?
and #gaps
),
contiguity of Ranges to one another (#contiguous?
, #left_contiguous?
, and
#right_contiguous?
, #join
), and the testing of overlaps between ranges
(#overlaps?
, #overlaps_among?
). These are put to good use in the
‘fat_period’ (https://github.com/ddoherty03/fat_period) gem, which combines
fat_core’s extended Range class with its extended Date class to make a useful
Period class for date ranges, and you may find fat_core’s extended Range class
likewise useful.
For example, you can use the #gaps
method to find the gaps left in the
coverage on one Range by an Array of other Ranges:
require 'fat_core/range'
(0..12).gaps([(0..2), (5..7), (10..12)]) => [(3..4), (8..9)]
**
Enumerable
FatCore::Enumerable extends Enumerable with the #each_with_flags
method that
yields the elements of the Enumerable but also yields two booleans, first
and
last
that are set to true on respectively, the first and last element of the
Enumerable. This makes it easy to treat these two cases specially without
testing the index as in #each_with_index
.
Hash
FatCore::Hash extends the Hash class with some useful methods for element
deletion (#delete_with_value
) and for manipulating the keys
(#keys_with_value
, #remap_keys
and #replace_keys
) of a Hash. It also
provides #each_pair_with_flags
as an analog to Enumerable’s
#each_with_flags
.
It also provides the shovel operator as a convenient alias for Hash#merge
,
so that
{a: 'A', b: 'B', c: 'C'} << {c: 'CC', d: 'DD'} << {e: 'EEE'} => {a: 'A', b: 'B', c: 'CC', d: 'DD', e: 'EEE'}
String
FatCore::String has methods for performing matching of one string with another
(#matches_with
, #fuzzy_match
), for converting a string to title-case as
might by used in the title of a book (#entitle
), for converting a String into
a useable Symbol (#as_sym
) and vice-versa (#as_string
also
Symbol#as_string
), for wrapping with an optional hanging indent (#wrap
),
cleaning up errant spaces (#clean
), and computing the Damerau-Levenshtein
distance between strings (#distance
). And several others.
TeX Quoting
Several of the extension, most notably ‘fat_core/string’, provides a
#tex_quote
method for quoting the string version of an object so as to allow
its inclusion in a TeX document and quote characters such as ‘$’ or ‘%’ that
have a special meaning for TeX.
Numbers
FatCore::Numeric has methods for inserting grouping commas into a number
(#commas
and #group
), for converting seconds to HH:MM:SS.dd format
(#secs_to_hms
), for testing for integrality (#whole?
and #int_if_whole
), and
testing for sign (#signum
).
Contributing
- Fork it (http://github.com/ddoherty03/fat_core/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (~git commit -am ‘Add some feature’~)
- Push to the branch (
git push origin my-new-feature
) - Create new Pull Request