date_holidays-reader 0.9.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.nvmrc +1 -0
- data/.rspec +3 -0
- data/.rubocop.yml +27 -0
- data/.ruby-version +1 -0
- data/.travis.yml +17 -0
- data/CHANGELOG.md +6 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Dockerfile +19 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +105 -0
- data/Guardfile +44 -0
- data/LICENSE +17 -0
- data/README.md +176 -0
- data/Rakefile +18 -0
- data/bin/add-copyright-header.pl +16 -0
- data/bin/console +21 -0
- data/bin/date-holidays-version.js +21 -0
- data/bin/holidays-to-json.js +70 -0
- data/bin/setup +17 -0
- data/date-holiday-reader.gemspec +49 -0
- data/lib/date_holidays/reader.rb +26 -0
- data/lib/date_holidays/reader/config.rb +54 -0
- data/lib/date_holidays/reader/holiday.rb +78 -0
- data/lib/date_holidays/reader/js_bridge.rb +75 -0
- data/lib/date_holidays/reader/locale.rb +109 -0
- data/lib/date_holidays/reader/version.rb +67 -0
- data/package.json +12 -0
- data/yarn.lock +1686 -0
- metadata +245 -0
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
7
|
+
|
8
|
+
task default: :spec
|
9
|
+
|
10
|
+
CLOBBER.include(%w[pkg node_bin])
|
11
|
+
|
12
|
+
desc 'Compile the Node dependencies'
|
13
|
+
task :node_compile do
|
14
|
+
sh 'yarn run pkg bin/holidays-to-json.js --out-path=node_bin --targets=macos-x64,linux-x64'
|
15
|
+
end
|
16
|
+
|
17
|
+
# Add node_compile as a dependency to the existing build task defined by bundler:
|
18
|
+
task build: :node_compile
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env perl -i -p
|
2
|
+
|
3
|
+
# Adds copyright headers to files passed in.
|
4
|
+
# Example:
|
5
|
+
# find . -name '*.rb' -exec bin/add-copyright-header.pl {} \;
|
6
|
+
|
7
|
+
$header = <<'END_HEADER';
|
8
|
+
#
|
9
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
10
|
+
#
|
11
|
+
# This source code is licensed under the MIT license found in the
|
12
|
+
# LICENSE file in the root directory of this source tree.
|
13
|
+
#
|
14
|
+
END_HEADER
|
15
|
+
|
16
|
+
s/(# frozen_string_literal: true\n)/$1\n$header/
|
data/bin/console
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
require "bundler/setup"
|
11
|
+
require "date_holidays/reader"
|
12
|
+
|
13
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
14
|
+
# with your gem easier. You can also use a different console, if you like.
|
15
|
+
|
16
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
17
|
+
# require "pry"
|
18
|
+
# Pry.start
|
19
|
+
|
20
|
+
require "irb"
|
21
|
+
IRB.start(__FILE__)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
|
3
|
+
/*
|
4
|
+
Copyright(c) 2019 - present, Blue Marble Payroll, LLC
|
5
|
+
|
6
|
+
This source code is licensed under the MIT license found in the
|
7
|
+
LICENSE file in the root directory of this source tree.
|
8
|
+
*/
|
9
|
+
|
10
|
+
'use strict'
|
11
|
+
|
12
|
+
const path = require('path');
|
13
|
+
const fs = require('fs');
|
14
|
+
|
15
|
+
const dateHolidaysIndexJsPath = require.resolve('date-holidays');
|
16
|
+
|
17
|
+
const dateHolidaysPackageJsonPath = path.join(
|
18
|
+
path.dirname(dateHolidaysIndexJsPath), '..', 'package.json'
|
19
|
+
);
|
20
|
+
|
21
|
+
console.log(JSON.parse(fs.readFileSync(dateHolidaysPackageJsonPath)).version);
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
|
3
|
+
/*
|
4
|
+
Copyright(c) 2019 - present, Blue Marble Payroll, LLC
|
5
|
+
|
6
|
+
This source code is licensed under the MIT license found in the
|
7
|
+
LICENSE file in the root directory of this source tree.
|
8
|
+
*/
|
9
|
+
|
10
|
+
'use strict'
|
11
|
+
|
12
|
+
/**
|
13
|
+
* Adapted from sample.js (https://github.com/commenthol/date-holidays/blob/master/test/sample.js).
|
14
|
+
* holidays-to-json.js sub_command [country.state.region] [year] [--lang en] [--state ca]
|
15
|
+
* e.g.
|
16
|
+
* holidays-to-json.js holidays at.b 2015
|
17
|
+
*/
|
18
|
+
var Holidays = require('date-holidays');
|
19
|
+
|
20
|
+
const COMMANDS = Object.freeze({
|
21
|
+
holidays: (hd, opts) => hd.getHolidays(opts.year),
|
22
|
+
countries: (hd, _) => hd.getCountries(),
|
23
|
+
// Note that the language isn't working here. This appears to be a bug in the node module.
|
24
|
+
states: (hd, opts) => hd.getStates(opts.country, opts.languages.first),
|
25
|
+
regions: (hd, opts) => hd.getRegions(opts.country, opts.state, opts.languages.first),
|
26
|
+
languages: (hd, _) => hd.getLanguages(),
|
27
|
+
time_zones: (hd, _) => hd.getTimezones(),
|
28
|
+
});
|
29
|
+
const DEFAULT_LANGUAGE = 'en';
|
30
|
+
|
31
|
+
function extractData(cmd, hd, opts) {
|
32
|
+
const extractor = COMMANDS[cmd]
|
33
|
+
let result = {};
|
34
|
+
|
35
|
+
if (extractor) {
|
36
|
+
result = extractor(hd, opts);
|
37
|
+
} else {
|
38
|
+
console.error("Uknown sub-command: " + cmd);
|
39
|
+
console.error("Valid sub-commands are: " + Object.keys(COMMANDS).join(', '));
|
40
|
+
process.exit(1);
|
41
|
+
}
|
42
|
+
|
43
|
+
// Always return the empty object so that it converts nicely to JSON.
|
44
|
+
return result || {};
|
45
|
+
}
|
46
|
+
|
47
|
+
if (module === require.main) {
|
48
|
+
const opts = {};
|
49
|
+
const args = process.argv.slice(2);
|
50
|
+
const cmd = args.shift();
|
51
|
+
var arg;
|
52
|
+
|
53
|
+
while ((arg = args.shift())) {
|
54
|
+
if (arg === '--lang') {
|
55
|
+
opts.languages = args.shift();
|
56
|
+
} else if (arg === '--state') {
|
57
|
+
opts.state = args.shift();
|
58
|
+
} else if (/^\d{4}$/.test(arg)) {
|
59
|
+
opts.year = arg;
|
60
|
+
} else if (/^[a-zA-Z]{2}/.test(arg)) {
|
61
|
+
opts.country = arg;
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
opts.year = opts.year || (new Date()).getFullYear();
|
66
|
+
opts.languages = opts.languages || [DEFAULT_LANGUAGE];
|
67
|
+
|
68
|
+
var hd = new Holidays(opts.country, { languages: opts.languages, state: opts.state } );
|
69
|
+
console.log(JSON.stringify(extractData(cmd, hd, opts)));
|
70
|
+
}
|
data/bin/setup
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
set -euo pipefail
|
10
|
+
IFS=$'\n\t'
|
11
|
+
set -vx
|
12
|
+
|
13
|
+
bundle install
|
14
|
+
yarn
|
15
|
+
rake build
|
16
|
+
|
17
|
+
# Do any other automated setup that you need to do here
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'date_holidays/reader/version'
|
6
|
+
|
7
|
+
# rubocop:disable Metrics/BlockLength
|
8
|
+
Gem::Specification.new do |spec|
|
9
|
+
spec.name = 'date_holidays-reader'
|
10
|
+
spec.version = DateHolidays::Reader::VERSION
|
11
|
+
spec.authors = ['Ryan Gerry']
|
12
|
+
spec.email = ['rgerry@bluemarblepayroll.com']
|
13
|
+
|
14
|
+
spec.summary = 'A read only Ruby wrapper for the date-holidays Node module'
|
15
|
+
spec.description = <<~DESCRIPTION
|
16
|
+
This provides a read only interace over the data provided by the
|
17
|
+
date-holidays Node module available at https://github.com/commenthol/date-holidays .'
|
18
|
+
DESCRIPTION
|
19
|
+
spec.homepage = 'http://www.github.com/bluemarblepayroll/date_holidays-reader/'
|
20
|
+
spec.metadata['source_code_uri'] = 'https://github.com/bluemarblepayroll/date_holidays-reader'
|
21
|
+
spec.metadata['changelog_uri'] = 'https://github.com/bluemarblepayroll/date_holidays-reader/blob/master/CHANGELOG.md'
|
22
|
+
|
23
|
+
# Specify which files should be added to the gem when it is released.
|
24
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
25
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
26
|
+
git_files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
27
|
+
node_bin = Dir.glob('node_bin/*')
|
28
|
+
|
29
|
+
git_files + node_bin
|
30
|
+
end
|
31
|
+
spec.bindir = 'exe'
|
32
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
33
|
+
spec.require_paths = ['lib']
|
34
|
+
|
35
|
+
spec.add_dependency 'acts_as_hashable', '~> 1'
|
36
|
+
spec.add_dependency 'caution'
|
37
|
+
spec.add_dependency 'os', '~> 1'
|
38
|
+
|
39
|
+
spec.add_development_dependency 'bundler', '~> 1.17'
|
40
|
+
spec.add_development_dependency 'guard'
|
41
|
+
spec.add_development_dependency 'guard-rspec'
|
42
|
+
spec.add_development_dependency 'pry'
|
43
|
+
spec.add_development_dependency 'pry-byebug'
|
44
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
45
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
46
|
+
spec.add_development_dependency 'rubocop'
|
47
|
+
spec.add_development_dependency 'terminal-notifier-guard'
|
48
|
+
end
|
49
|
+
# rubocop:enable Metrics/BlockLength
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'acts_as_hashable'
|
11
|
+
require 'caution'
|
12
|
+
require 'json'
|
13
|
+
require 'os'
|
14
|
+
|
15
|
+
require 'date_holidays/reader/config'
|
16
|
+
require 'date_holidays/reader/holiday'
|
17
|
+
require 'date_holidays/reader/js_bridge'
|
18
|
+
require 'date_holidays/reader/locale'
|
19
|
+
require 'date_holidays/reader/version'
|
20
|
+
|
21
|
+
module DateHolidays
|
22
|
+
# Defines the outermost module for the gem.
|
23
|
+
module Reader
|
24
|
+
# Your code goes here...
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
module DateHolidays
|
11
|
+
module Reader
|
12
|
+
# Tells the gem how to interact with Node and provides a list of countries.
|
13
|
+
# See the configuration section of the Readme for more inforamtion.
|
14
|
+
class Config
|
15
|
+
SUPPORTED_CPU_BITS = 64
|
16
|
+
private_constant :SUPPORTED_CPU_BITS
|
17
|
+
|
18
|
+
class << self
|
19
|
+
attr_reader :node_path
|
20
|
+
attr_writer :default
|
21
|
+
|
22
|
+
def node_path=(path)
|
23
|
+
# Clear out the cached config when the node path changes:
|
24
|
+
@default = nil
|
25
|
+
@node_path = path
|
26
|
+
end
|
27
|
+
|
28
|
+
def default
|
29
|
+
@default ||= new(node_path: node_path)
|
30
|
+
end
|
31
|
+
|
32
|
+
def countries
|
33
|
+
JsBridge.new.extract(:countries)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_reader :node_path
|
38
|
+
|
39
|
+
def initialize(node_path: nil)
|
40
|
+
@node_path = node_path
|
41
|
+
|
42
|
+
freeze
|
43
|
+
end
|
44
|
+
|
45
|
+
def native_mac?
|
46
|
+
OS.osx? && OS.bits == SUPPORTED_CPU_BITS
|
47
|
+
end
|
48
|
+
|
49
|
+
def native_linux?
|
50
|
+
OS.linux? && OS.bits == SUPPORTED_CPU_BITS
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
# Use time from the standard library (instead of core) which has .strptime:
|
11
|
+
require 'time'
|
12
|
+
|
13
|
+
module DateHolidays
|
14
|
+
module Reader
|
15
|
+
# A holiday which includes a date, start and end times, name, and type.
|
16
|
+
# Based on https://github.com/commenthol/date-holidays#holiday-object .
|
17
|
+
#
|
18
|
+
# The date is represented as a Ruby Date instance as it is the
|
19
|
+
# holiday start date in the local time zone.
|
20
|
+
#
|
21
|
+
# Start and end times are represented as Time instances in UTC. Note that
|
22
|
+
# New Year's day in the US has a start time of January 1st at 5 AM UTC as
|
23
|
+
# Eastern Standard Time is five hours after UTC.
|
24
|
+
class Holiday
|
25
|
+
acts_as_hashable
|
26
|
+
|
27
|
+
attr_reader :date, :start_time, :end_time, :name, :type, :note
|
28
|
+
|
29
|
+
# rubocop:disable Metrics/ParameterLists
|
30
|
+
# This cop defaults to five parameters which seems a little low for
|
31
|
+
# keyword arguments. This is a value object which gets frozen after
|
32
|
+
# initialization so I'd rather pass in everything as needed right away.
|
33
|
+
def initialize(date:, start_time:, end_time:, name:, type:, substitute: false, note: nil)
|
34
|
+
# rubocop:enable Metrics/ParameterLists
|
35
|
+
@date = date.is_a?(Date) ? date : Date.strptime(date, '%Y-%m-%d')
|
36
|
+
@start_time = parse_time(start_time)
|
37
|
+
@end_time = parse_time(end_time)
|
38
|
+
@name = name
|
39
|
+
@type = type.to_sym
|
40
|
+
@substitute = substitute
|
41
|
+
@note = note
|
42
|
+
|
43
|
+
freeze
|
44
|
+
end
|
45
|
+
|
46
|
+
def substitute?
|
47
|
+
@substitute
|
48
|
+
end
|
49
|
+
|
50
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
51
|
+
# I could cut down on those cops by iterating over instance variable and
|
52
|
+
# using meta programming. However, that would obscure intent.
|
53
|
+
def ==(other)
|
54
|
+
other.is_a?(self.class) &&
|
55
|
+
other.date == date &&
|
56
|
+
other.start_time == start_time &&
|
57
|
+
other.end_time == end_time &&
|
58
|
+
other.name == name &&
|
59
|
+
other.type == type &&
|
60
|
+
other.substitute? == substitute? &&
|
61
|
+
other.note == note
|
62
|
+
end
|
63
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def parse_time(time)
|
68
|
+
# Example time string: "2018-01-01T05:00:00.000Z"
|
69
|
+
time.is_a?(Time) ? time : Time.xmlschema(time)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# For reference:
|
76
|
+
# Core Time: https://ruby-doc.org/core-2.3.6/Time.html
|
77
|
+
# Stdlib Time: https://ruby-doc.org/stdlib-2.3.8/libdoc/time/rdoc/Time.html
|
78
|
+
# Stdlib Date: http://ruby-doc.org/stdlib-2.3.8/libdoc/date/rdoc/Date.html
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
module DateHolidays
|
11
|
+
module Reader
|
12
|
+
# A communication bridge to the JavaScript process which houses the date-holidays node module.
|
13
|
+
class JsBridge
|
14
|
+
BIN_PATH = File.expand_path('../../../bin', __dir__).freeze
|
15
|
+
|
16
|
+
JS_PROGRAM_PATH = File.expand_path(File.join(BIN_PATH, '/holidays-to-json.js')).freeze
|
17
|
+
NODE_BIN_PATH = File.expand_path('../../../node_bin', __dir__).freeze
|
18
|
+
private_constant :JS_PROGRAM_PATH, :NODE_BIN_PATH
|
19
|
+
|
20
|
+
attr_reader :config, :debug
|
21
|
+
|
22
|
+
# TODO: initialize with a configuration object for how to run the command
|
23
|
+
def initialize(config = Config.default, debug: false)
|
24
|
+
@config = config
|
25
|
+
@debug = debug
|
26
|
+
|
27
|
+
freeze
|
28
|
+
end
|
29
|
+
|
30
|
+
def extract(sub_cmd, *args)
|
31
|
+
JSON.parse(get_output(holidays_to_json_command + [sub_cmd, *args]))
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_output(args)
|
35
|
+
args = Array(args)
|
36
|
+
cmd_tokens_as_strings = args.map(&:to_s)
|
37
|
+
output = nil
|
38
|
+
|
39
|
+
print_command(cmd_tokens_as_strings) if debug
|
40
|
+
|
41
|
+
IO.popen(cmd_tokens_as_strings, err: %i[child out]) { |cmd_io| output = cmd_io.read }
|
42
|
+
output
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# Returns an array of strings containing the tokens to execute the
|
48
|
+
# date-holidays wrapper depending on configuration.
|
49
|
+
def holidays_to_json_command
|
50
|
+
if config.node_path
|
51
|
+
[config.node_path, JS_PROGRAM_PATH]
|
52
|
+
elsif pre_compiled_program
|
53
|
+
[File.join(NODE_BIN_PATH, pre_compiled_program)]
|
54
|
+
else
|
55
|
+
# Fallback: a node path has not been configured and there is no
|
56
|
+
# pre-compiled program for this OS so fallback to the shebang line in
|
57
|
+
# the JS file.
|
58
|
+
[JS_PROGRAM_PATH]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def pre_compiled_program
|
63
|
+
if config.native_mac?
|
64
|
+
'holidays-to-json-macos'
|
65
|
+
elsif config.native_linux?
|
66
|
+
'holidays-to-json-linux'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def output_command(cmd_args)
|
71
|
+
puts "#{self.class}: about to invoke: '#{cmd_args.join(' ')}'"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|