patriot-workflow-scheduler 0.6.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.
- checksums.yaml +15 -0
- data/bin/patriot +8 -0
- data/bin/patriot-init +35 -0
- data/lib/patriot.rb +11 -0
- data/lib/patriot/command.rb +71 -0
- data/lib/patriot/command/base.rb +199 -0
- data/lib/patriot/command/command_group.rb +43 -0
- data/lib/patriot/command/command_macro.rb +141 -0
- data/lib/patriot/command/composite.rb +49 -0
- data/lib/patriot/command/parser.rb +78 -0
- data/lib/patriot/command/sh_command.rb +42 -0
- data/lib/patriot/controller.rb +2 -0
- data/lib/patriot/controller/package_controller.rb +81 -0
- data/lib/patriot/controller/worker_admin_controller.rb +159 -0
- data/lib/patriot/job_store.rb +66 -0
- data/lib/patriot/job_store/base.rb +159 -0
- data/lib/patriot/job_store/factory.rb +19 -0
- data/lib/patriot/job_store/in_memory_store.rb +252 -0
- data/lib/patriot/job_store/job.rb +118 -0
- data/lib/patriot/job_store/job_ticket.rb +30 -0
- data/lib/patriot/job_store/rdb_job_store.rb +353 -0
- data/lib/patriot/tool.rb +2 -0
- data/lib/patriot/tool/batch_parser.rb +102 -0
- data/lib/patriot/tool/patriot_command.rb +48 -0
- data/lib/patriot/tool/patriot_commands/execute.rb +92 -0
- data/lib/patriot/tool/patriot_commands/job.rb +62 -0
- data/lib/patriot/tool/patriot_commands/plugin.rb +41 -0
- data/lib/patriot/tool/patriot_commands/register.rb +77 -0
- data/lib/patriot/tool/patriot_commands/upgrade.rb +24 -0
- data/lib/patriot/tool/patriot_commands/validate.rb +84 -0
- data/lib/patriot/tool/patriot_commands/worker.rb +35 -0
- data/lib/patriot/tool/patriot_commands/worker_admin.rb +60 -0
- data/lib/patriot/util.rb +14 -0
- data/lib/patriot/util/config.rb +58 -0
- data/lib/patriot/util/config/base.rb +22 -0
- data/lib/patriot/util/config/inifile_config.rb +63 -0
- data/lib/patriot/util/cron_format_parser.rb +104 -0
- data/lib/patriot/util/date_util.rb +200 -0
- data/lib/patriot/util/db_client.rb +65 -0
- data/lib/patriot/util/db_client/base.rb +142 -0
- data/lib/patriot/util/db_client/hash_record.rb +53 -0
- data/lib/patriot/util/db_client/record.rb +25 -0
- data/lib/patriot/util/logger.rb +24 -0
- data/lib/patriot/util/logger/facade.rb +33 -0
- data/lib/patriot/util/logger/factory.rb +59 -0
- data/lib/patriot/util/logger/log4r_factory.rb +111 -0
- data/lib/patriot/util/logger/webrick_log_factory.rb +47 -0
- data/lib/patriot/util/param.rb +73 -0
- data/lib/patriot/util/retry.rb +30 -0
- data/lib/patriot/util/script.rb +52 -0
- data/lib/patriot/util/system.rb +120 -0
- data/lib/patriot/worker.rb +35 -0
- data/lib/patriot/worker/base.rb +153 -0
- data/lib/patriot/worker/info_server.rb +90 -0
- data/lib/patriot/worker/job_store_server.rb +32 -0
- data/lib/patriot/worker/multi_node_worker.rb +157 -0
- data/lib/patriot/worker/servlet.rb +23 -0
- data/lib/patriot/worker/servlet/job_servlet.rb +128 -0
- data/lib/patriot/worker/servlet/worker_status_servlet.rb +44 -0
- data/skel/batch/sample/daily/test.pbc +4 -0
- data/skel/config/patriot.ini +21 -0
- data/skel/public/css/bootstrap.css +2495 -0
- data/skel/public/css/original.css +54 -0
- data/skel/public/js/bootstrap-alerts.js +124 -0
- data/skel/public/js/bootstrap-buttons.js +64 -0
- data/skel/public/js/bootstrap-dropdown.js +55 -0
- data/skel/public/js/bootstrap-modal.js +260 -0
- data/skel/public/js/bootstrap-popover.js +90 -0
- data/skel/public/js/bootstrap-scrollspy.js +107 -0
- data/skel/public/js/bootstrap-tabs.js +80 -0
- data/skel/public/js/bootstrap-twipsy.js +321 -0
- data/skel/public/js/jquery-1.6.4.min.js +4 -0
- data/skel/public/templates/_jobs.erb +97 -0
- data/skel/public/templates/job.erb +119 -0
- data/skel/public/templates/jobs.erb +21 -0
- data/skel/public/templates/jobs_deleted.erb +6 -0
- data/skel/public/templates/layout.erb +103 -0
- data/skel/public/templates/state_updated.erb +6 -0
- metadata +235 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
module Patriot
|
2
|
+
module Tool
|
3
|
+
module PatriotCommands
|
4
|
+
# remote worker administration tool
|
5
|
+
module WorkerAdmin
|
6
|
+
|
7
|
+
Patriot::Tool::PatriotCommand.class_eval do
|
8
|
+
desc 'worker_admin [options] [start|stop|restart|sleep|wake|status]',
|
9
|
+
'controll remote workers'
|
10
|
+
method_option :all,
|
11
|
+
:aliases => '-a',
|
12
|
+
:type => :boolean,
|
13
|
+
:default => false,
|
14
|
+
:desc => 'target all worker hosts'
|
15
|
+
method_option :host,
|
16
|
+
:aliases => '-h',
|
17
|
+
:type => :string,
|
18
|
+
:desc => 'target host'
|
19
|
+
def worker_admin(sub_cmd)
|
20
|
+
sub_cmd = "#{sub_cmd}_worker" unless sub_cmd == "status"
|
21
|
+
begin
|
22
|
+
opts = symbolize_options(options)
|
23
|
+
conf = {:type => 'worker_admin'}
|
24
|
+
conf[:path] = opts[:config] if opts.has_key?(:config)
|
25
|
+
config = load_config(conf)
|
26
|
+
controller = Patriot::Controller::WorkerAdminController.new(config)
|
27
|
+
result = controller.send(sub_cmd.to_sym, opts)
|
28
|
+
print_mtd ="print_#{sub_cmd}".to_sym
|
29
|
+
self.send(print_mtd, result, opts) if self.respond_to?(print_mtd)
|
30
|
+
rescue => e
|
31
|
+
puts e
|
32
|
+
raise e
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
no_commands do
|
37
|
+
def print_status(result, opts)
|
38
|
+
statuses = {}
|
39
|
+
unless opts[:all]
|
40
|
+
raise "illegal response #{result} from #{opts[:host]}" unless result.size == 1
|
41
|
+
result[opts[:host]] = result.delete(result.keys.first)
|
42
|
+
end
|
43
|
+
result.each do |s,r|
|
44
|
+
if r.nil?
|
45
|
+
statuses[s] = 'HALT'
|
46
|
+
else
|
47
|
+
r = JSON.parse(r)
|
48
|
+
raise "illegal response #{r} from #{s}" unless r.size == 1
|
49
|
+
statuses[s] = r.values[0].nil? ? 'HALT' : r.values[0]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
puts JSON.generate(statuses)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/patriot/util.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module Patriot
|
2
|
+
# namespace for utility functions
|
3
|
+
module Util
|
4
|
+
require 'patriot/util/config'
|
5
|
+
require 'patriot/util/logger'
|
6
|
+
require 'patriot/util/param'
|
7
|
+
require 'patriot/util/date_util'
|
8
|
+
require 'patriot/util/script'
|
9
|
+
require 'patriot/util/retry'
|
10
|
+
require 'patriot/util/db_client'
|
11
|
+
require 'patriot/util/system'
|
12
|
+
require 'patriot/util/cron_format_parser'
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'patriot/util/config/base'
|
2
|
+
require 'patriot/util/config/inifile_config'
|
3
|
+
|
4
|
+
module Patriot
|
5
|
+
module Util
|
6
|
+
# namespace for configuration files
|
7
|
+
module Config
|
8
|
+
# the path to default configuration file
|
9
|
+
DEFAULT_CONFIG = File.join($home || Dir.pwd, 'config', 'patriot.ini')
|
10
|
+
# a configuration parameter key for plugins
|
11
|
+
PLUGIN_KEY = "plugins"
|
12
|
+
# a configuration parameter key for plugins directory
|
13
|
+
PLUGIN_DIR_KEY = "plugins.dir"
|
14
|
+
# default plugins directory
|
15
|
+
DEFAULT_PLUGIN_DIR = "plugins"
|
16
|
+
# plugin directory
|
17
|
+
PLUGIN_LIB_DIR = 'lib'
|
18
|
+
# plugin initiation script
|
19
|
+
PLUGIN_INIT_SCRIPT = 'init.rb'
|
20
|
+
|
21
|
+
# load configuration file
|
22
|
+
# @param option [Hash]
|
23
|
+
# @option option :path [String] path to configuration file
|
24
|
+
# @option option :type [String] load type (differe by tool)
|
25
|
+
# @option option :ignore_plugin [Boolean] set true not to load plugins
|
26
|
+
def load_config(option = {})
|
27
|
+
option = {:path => DEFAULT_CONFIG,
|
28
|
+
:type => nil,
|
29
|
+
:ignore_plugin => false }.merge(option)
|
30
|
+
conf = nil
|
31
|
+
case File.extname(option[:path])
|
32
|
+
when '.ini'
|
33
|
+
conf = Patriot::Util::Config::IniFileConfig.new(option[:path], option[:type])
|
34
|
+
else
|
35
|
+
raise "unsupported config file name: #{conf[:path]}"
|
36
|
+
end
|
37
|
+
load_plugins(conf) unless option[:ignore_plugin]
|
38
|
+
return conf
|
39
|
+
end
|
40
|
+
|
41
|
+
# load plugins
|
42
|
+
# @param conf [Patriot::Util::Config::Base] configuration to load plugins
|
43
|
+
def load_plugins(conf)
|
44
|
+
plugins = conf.get(PLUGIN_KEY)
|
45
|
+
return conf if plugins.nil?
|
46
|
+
plugins = [plugins] unless plugins.is_a?(Array)
|
47
|
+
plugin_dir = conf.get(PLUGIN_DIR_KEY, DEFAULT_PLUGIN_DIR)
|
48
|
+
plugins.each do |plugin|
|
49
|
+
path = File.join($home, plugin_dir, plugin)
|
50
|
+
init_script = File.join(path, PLUGIN_INIT_SCRIPT)
|
51
|
+
raise "Failed to load #{plugin}: #{init_script} does not exist" unless File.file?(init_script)
|
52
|
+
$: << File.join(path, PLUGIN_LIB_DIR)
|
53
|
+
require init_script
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Patriot
|
2
|
+
module Util
|
3
|
+
module Config
|
4
|
+
# base class of configuration classes
|
5
|
+
class Base
|
6
|
+
# get value for the specified key
|
7
|
+
# @param [String] key
|
8
|
+
# @param [Object] default default value
|
9
|
+
# @return [Object] the value for the ke
|
10
|
+
def get(key, default=nil)
|
11
|
+
raise NotImplementedError
|
12
|
+
end
|
13
|
+
|
14
|
+
# get path where this configuration is loaded
|
15
|
+
# @return [String] path to the file
|
16
|
+
def path
|
17
|
+
raise NotImplementedError
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'inifile'
|
2
|
+
module Patriot
|
3
|
+
module Util
|
4
|
+
module Config
|
5
|
+
# a configuration implementation definied by the ini-file format
|
6
|
+
class IniFileConfig < Patriot::Util::Config::Base
|
7
|
+
|
8
|
+
# comman section name
|
9
|
+
COMMON_SECTION = 'common'
|
10
|
+
|
11
|
+
# @param path [String] path to a configuration file
|
12
|
+
# @param type [String] load type (section name to be loaded)
|
13
|
+
def initialize(path, type = nil)
|
14
|
+
raise "path in String is expected but #{path.class}" unless path.is_a?(String)
|
15
|
+
@path = path
|
16
|
+
config = IniFile.load(path)
|
17
|
+
raise "#{path} not found" if config.nil?
|
18
|
+
@config = {}
|
19
|
+
read_section(config, COMMON_SECTION)
|
20
|
+
read_section(config, type)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @private
|
24
|
+
# read configuration from a section
|
25
|
+
# @param config [IniFile] ini file configuration
|
26
|
+
# @param section [String] section name
|
27
|
+
def read_section(config, section)
|
28
|
+
sect = config[section]
|
29
|
+
return if sect.nil?
|
30
|
+
sect.each{|k,v| @config[k.to_sym] = v }
|
31
|
+
end
|
32
|
+
private :read_section
|
33
|
+
|
34
|
+
# @see Patriot::Util::Config::Base
|
35
|
+
def path
|
36
|
+
return @path
|
37
|
+
end
|
38
|
+
|
39
|
+
# @see Patriot::Util::Config::Base
|
40
|
+
def get(name, default=nil)
|
41
|
+
v = @config[name.to_sym]
|
42
|
+
v = split_value(v)
|
43
|
+
return v.nil? ? default : v
|
44
|
+
end
|
45
|
+
|
46
|
+
# split configuration value by a delimiter
|
47
|
+
# @param value [String] the value to be splitted
|
48
|
+
# @param delimiter [String] delimiter for splitting
|
49
|
+
def split_value(value, delimiter = ',')
|
50
|
+
# don't allow spaces around value
|
51
|
+
regexp = Regexp.new("\\s*#{delimiter}\\s*")
|
52
|
+
if value.is_a?(String) && value =~ regexp
|
53
|
+
return value.split(regexp)
|
54
|
+
else
|
55
|
+
return value
|
56
|
+
end
|
57
|
+
end
|
58
|
+
private :split_value
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'date'
|
2
|
+
module Patriot
|
3
|
+
module Util
|
4
|
+
# module for handling cron format interval
|
5
|
+
module CronFormatParser
|
6
|
+
|
7
|
+
# start 00:00:00 everyday
|
8
|
+
DEFAULT_CRON_FIELD = "0 0 * * *"
|
9
|
+
|
10
|
+
# a key word to indicate interval in which jobs are executed on the end of every month
|
11
|
+
END_OF_EVERY_MONTH = 'end_of_every_month'
|
12
|
+
|
13
|
+
# the list of week days
|
14
|
+
WEEKS = 0.upto(6).to_a
|
15
|
+
# the list of months
|
16
|
+
MONTHS = 1.upto(12).to_a
|
17
|
+
# the list of days
|
18
|
+
DAYS = 1.upto(31).to_a
|
19
|
+
# the list of hours
|
20
|
+
HOURS = 0.upto(23).to_a
|
21
|
+
# the list of minutes
|
22
|
+
MINUTES = 0.upto(59).to_a
|
23
|
+
|
24
|
+
# expand a given date to the array of time which should be executed in case of a given cron field
|
25
|
+
# @param date [DateTime] target datetime
|
26
|
+
# @param cron_field [String] interval in cron format
|
27
|
+
# @return [Array<DateTime>] a list of datetime which match the cron format
|
28
|
+
def expand_on_date(date, cron_field = nil)
|
29
|
+
cron_field = DEFAULT_CRON_FIELD if cron_field.nil? || cron_field.empty?
|
30
|
+
if cron_field == END_OF_EVERY_MONTH
|
31
|
+
return date.next.day == 1 ? [date] : []
|
32
|
+
end
|
33
|
+
field_splits = cron_field.split
|
34
|
+
raise "illegal cron field format #{cron_field}" unless field_splits.size == 5
|
35
|
+
minute, hour, day, month, week = field_splits
|
36
|
+
return [] unless is_target_day?(date, day, month, week)
|
37
|
+
return target_hours(hour).map do |h|
|
38
|
+
target_minutes(minute).map do |m|
|
39
|
+
DateTime.new(date.year, date.month, date.day, h, m, 0)
|
40
|
+
end
|
41
|
+
end.flatten
|
42
|
+
end
|
43
|
+
|
44
|
+
# check a given date is a target or not
|
45
|
+
# @param date [DateTime] a datetime to be checked
|
46
|
+
# @param day [String] day field in cron format
|
47
|
+
# @param month [String] month field in cron format
|
48
|
+
# @param week [String] week field in cron format
|
49
|
+
def is_target_day?(date, day, month, week)
|
50
|
+
unless month == "*" || parse_field(month, MONTHS).to_a.include?(date.month)
|
51
|
+
return false
|
52
|
+
end
|
53
|
+
return true if day == "*" && week == "*"
|
54
|
+
target_weeks = parse_field(week, WEEKS)
|
55
|
+
return parse_field(day, DAYS).include?(date.day) if week == "*"
|
56
|
+
return parse_field(week, WEEKS).include?(date.wday)
|
57
|
+
end
|
58
|
+
|
59
|
+
# @param hour [String] hour field in cron format
|
60
|
+
# @return [Array<Integer>] a target hours for the given hour specification
|
61
|
+
def target_hours(hour)
|
62
|
+
return parse_field(hour, HOURS)
|
63
|
+
end
|
64
|
+
|
65
|
+
# @param minute [String] minute field in cron format
|
66
|
+
# @return [Array<Integer>] a target minutes for the given minute specification
|
67
|
+
def target_minutes(minute)
|
68
|
+
return parse_field(minute, MINUTES)
|
69
|
+
end
|
70
|
+
|
71
|
+
# select elements which match a given field from a given domain
|
72
|
+
# @param field [String] one of the cron field
|
73
|
+
# @param domain [Array<Integer>] the domain of the field
|
74
|
+
# @return [Array<Integer>] a list of the domain elements match with the field
|
75
|
+
def parse_field(field, domain)
|
76
|
+
field = field.split("/")
|
77
|
+
raise "illegal cron field format #{field.join("/")}" unless field.size <= 2
|
78
|
+
range = []
|
79
|
+
if field[0] == "*"
|
80
|
+
range = domain
|
81
|
+
else
|
82
|
+
range = field[0].split(",").map do |r|
|
83
|
+
subrange = r.split("-")
|
84
|
+
if subrange.size == 1
|
85
|
+
subrange[0].to_i
|
86
|
+
elsif subrange.size == 2
|
87
|
+
subrange[0].to_i.upto(subrange[1].to_i).to_a
|
88
|
+
else
|
89
|
+
raise "illegal cron field format #{field.join("/")}"
|
90
|
+
end
|
91
|
+
end.flatten
|
92
|
+
end
|
93
|
+
return range if field.size == 1
|
94
|
+
interval = field[1].to_i
|
95
|
+
filtered_range = []
|
96
|
+
range.each_with_index do |r,i|
|
97
|
+
filtered_range << r if (i % interval) == 0
|
98
|
+
end
|
99
|
+
return filtered_range
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
require 'date'
|
2
|
+
module Patriot
|
3
|
+
module Util
|
4
|
+
# namesapce for date functions
|
5
|
+
module DateUtil
|
6
|
+
|
7
|
+
# format date
|
8
|
+
# @param dt [String] date in '%Y-%m-%d'
|
9
|
+
# @param time [String] time in '%H:%M:%S'
|
10
|
+
def date_format(dt, time, fmt, diff={})
|
11
|
+
diff = {:year => 0,
|
12
|
+
:month => 0,
|
13
|
+
:day => 0,
|
14
|
+
:hour => 0,
|
15
|
+
:min => 0,
|
16
|
+
:sec => 0}.merge(diff)
|
17
|
+
|
18
|
+
dt = eval "\"#{dt}\""
|
19
|
+
time = eval "\"#{time}\""
|
20
|
+
|
21
|
+
t = time.split(':')
|
22
|
+
d = dt.split('-')
|
23
|
+
sec = t[2].to_i + diff[:sec]
|
24
|
+
min = t[1].to_i + diff[:min]
|
25
|
+
hour = t[0].to_i + diff[:hour]
|
26
|
+
day = d[2].to_i
|
27
|
+
month = d[1].to_i
|
28
|
+
year = d[0].to_i + diff[:year]
|
29
|
+
|
30
|
+
min = min+ sec/60
|
31
|
+
hour = hour + min/60
|
32
|
+
diff[:day]= diff[:day] + hour/24
|
33
|
+
|
34
|
+
sec = sec%60
|
35
|
+
min = min%60
|
36
|
+
hour = hour%24
|
37
|
+
|
38
|
+
new_dt = DateTime.new(year, month, day, hour, min,sec)
|
39
|
+
new_dt = new_dt >> diff[:month]
|
40
|
+
new_dt = new_dt + diff[:day]
|
41
|
+
return new_dt.strftime(fmt)
|
42
|
+
end
|
43
|
+
|
44
|
+
# add interval to date
|
45
|
+
# @param date [String] date in '%Y-%m-%d'
|
46
|
+
# @param interval [Integer] interval in day
|
47
|
+
# @return [String] date in '%Y-%m-%d'
|
48
|
+
def date_add(date, interval)
|
49
|
+
d = to_date_obj(date)
|
50
|
+
d = d + interval
|
51
|
+
return d.strftime('%Y-%m-%d')
|
52
|
+
end
|
53
|
+
|
54
|
+
# subtract interval from date
|
55
|
+
# @param date [String] date in '%Y-%m-%d'
|
56
|
+
# @param interval [Integer] interval in day
|
57
|
+
# @return [String] date in '%Y-%m-%d'
|
58
|
+
def date_sub(date, interval)
|
59
|
+
d = to_date_obj(date)
|
60
|
+
d = d - interval
|
61
|
+
return d.strftime('%Y-%m-%d')
|
62
|
+
end
|
63
|
+
|
64
|
+
# @param date [String] date in '%Y-%m-%d'
|
65
|
+
# @param interval [Integer] interval in year
|
66
|
+
# @return [String] date of some years later in '%Y-%m-%d'
|
67
|
+
def date_add_year(date, interval)
|
68
|
+
s = date.split("-");
|
69
|
+
return Date.new(s[0].to_i + interval,s[1].to_i,s[2].to_i).strftime("%Y-%m-%d")
|
70
|
+
end
|
71
|
+
|
72
|
+
# @param date [String] date in '%Y-%m-%d'
|
73
|
+
# @param interval [Integer] interval in year
|
74
|
+
# @return [String] date of some years before in '%Y-%m-%d'
|
75
|
+
def date_sub_year(date, interval)
|
76
|
+
s = date.split("-");
|
77
|
+
return Date.new(s[0].to_i - interval,s[1].to_i,s[2].to_i).strftime("%Y-%m-%d")
|
78
|
+
end
|
79
|
+
|
80
|
+
# add interval to month
|
81
|
+
# @param month [String] month in '%Y-%m'
|
82
|
+
# @param interval [Integer] interval in month
|
83
|
+
# @return [String] month in '%Y-%m'
|
84
|
+
def month_add(month, interval)
|
85
|
+
d = to_date_obj("#{month}-01")
|
86
|
+
d = d >> interval
|
87
|
+
return d.strftime('%Y-%m')
|
88
|
+
end
|
89
|
+
|
90
|
+
# subtract interval from month
|
91
|
+
# @param month [String] month in '%Y-%m'
|
92
|
+
# @param interval [Integer] interval in month
|
93
|
+
# @return [String] month in '%Y-%m'
|
94
|
+
def month_sub(month, interval)
|
95
|
+
d = to_date_obj("#{month}-01")
|
96
|
+
d = d << interval
|
97
|
+
return d.strftime('%Y-%m')
|
98
|
+
end
|
99
|
+
|
100
|
+
# convert to month expression
|
101
|
+
# @param date [String] date in '%Y-%m-%d'
|
102
|
+
# @return [String] month in '%Y-%m'
|
103
|
+
def to_month(date)
|
104
|
+
d = to_date_obj(date)
|
105
|
+
return d.strftime('%Y-%m')
|
106
|
+
end
|
107
|
+
|
108
|
+
# get first date of the month
|
109
|
+
# @param date [String] date in '%Y-%m-%d'
|
110
|
+
# @return [String] date in '%Y-%m-%d'
|
111
|
+
def to_start_of_month(date)
|
112
|
+
s = date.split("-");
|
113
|
+
return Date.new(s[0].to_i,s[1].to_i,1).strftime("%Y-%m-%d")
|
114
|
+
end
|
115
|
+
|
116
|
+
# get the last date of the month
|
117
|
+
# @param date [String] date in '%Y-%m-%d'
|
118
|
+
# @return [String] date in '%Y-%m-%d'
|
119
|
+
def to_end_of_month(date)
|
120
|
+
s = date.split("-");
|
121
|
+
return ((Date.new(s[0].to_i,s[1].to_i,1)>>1)-1).strftime("%Y-%m-%d")
|
122
|
+
end
|
123
|
+
|
124
|
+
# get the last date of the last month
|
125
|
+
# @param date [String] date in '%Y-%m-%d'
|
126
|
+
# @return [String] date in '%Y-%m-%d'
|
127
|
+
def to_end_of_last_month(date)
|
128
|
+
d = to_date_obj(date)
|
129
|
+
d = d - d.day
|
130
|
+
return d.strftime('%Y-%m-%d')
|
131
|
+
end
|
132
|
+
|
133
|
+
# @param date [String] date in '%Y-%m-%d'
|
134
|
+
# @return [Integer] week of the date in (0-6)
|
135
|
+
def days_of_week(date)
|
136
|
+
d = to_date_obj(date)
|
137
|
+
return d.wday
|
138
|
+
end
|
139
|
+
|
140
|
+
# @param month [String] month in '%Y-%m'
|
141
|
+
# @return [Array<String>] the list of days in the month
|
142
|
+
def days_of_month(month)
|
143
|
+
s = month.split('-')
|
144
|
+
y = s[0].to_i
|
145
|
+
m = s[1].to_i
|
146
|
+
return 1.upto(31).map{|d| Date.new(y,m,d).strftime('%Y-%m-%d') if Date.valid_date?(y,m,d)}.compact
|
147
|
+
end
|
148
|
+
|
149
|
+
# @param dt [String] date in '%Y-%m-%d'
|
150
|
+
# @return [Array<String>] the list of days between the first date and the given date in the month
|
151
|
+
def days_of_month_until(dt)
|
152
|
+
s = dt.split('-')
|
153
|
+
y = s[0].to_i
|
154
|
+
m = s[1].to_i
|
155
|
+
d = s[2].to_i
|
156
|
+
return 1.upto(d).map{|d| Date.new(y,m,d).strftime('%Y-%m-%d') if Date.valid_date?(y,m,d)}.compact
|
157
|
+
end
|
158
|
+
|
159
|
+
# convert to a Date instance
|
160
|
+
# @param date [String] date in '%Y-%m-%d'
|
161
|
+
# @return [Date]
|
162
|
+
def to_date_obj(date)
|
163
|
+
d = date.split('-')
|
164
|
+
return Date.new(d[0].to_i, d[1].to_i, d[2].to_i)
|
165
|
+
end
|
166
|
+
|
167
|
+
# @deprecated
|
168
|
+
def date_to_month(date)
|
169
|
+
d = date.split('-')
|
170
|
+
return Date.new(d[0].to_i, d[1].to_i, d[2].to_i).strftime('%Y-%m')
|
171
|
+
end
|
172
|
+
|
173
|
+
# @return [Array<String>] a list of hours (00..23)
|
174
|
+
def hours
|
175
|
+
return 0.upto(23).map do |h| h_str = h.to_s.rjust(2, "0") end.flatten
|
176
|
+
end
|
177
|
+
|
178
|
+
# validate date or date range expression
|
179
|
+
def validate_and_parse_dates(date)
|
180
|
+
date_objs = date.split(",").map do |d|
|
181
|
+
unless d.to_s =~ /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/
|
182
|
+
raise ArgumentError, "ERROR: invalid date #{d} in #{date}"
|
183
|
+
end
|
184
|
+
Date.parse(d.to_s)
|
185
|
+
end
|
186
|
+
if date_objs.size == 1
|
187
|
+
return [date]
|
188
|
+
elsif date_objs.size == 2
|
189
|
+
dates = []
|
190
|
+
date_objs[0].upto(date_objs[1]){|d| dates << d.to_s}
|
191
|
+
return dates
|
192
|
+
end
|
193
|
+
raise ArgumentError, "ERROR: invalid date #{date}"
|
194
|
+
end
|
195
|
+
private :validate_and_parse_dates
|
196
|
+
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|