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.
Files changed (79) hide show
  1. checksums.yaml +15 -0
  2. data/bin/patriot +8 -0
  3. data/bin/patriot-init +35 -0
  4. data/lib/patriot.rb +11 -0
  5. data/lib/patriot/command.rb +71 -0
  6. data/lib/patriot/command/base.rb +199 -0
  7. data/lib/patriot/command/command_group.rb +43 -0
  8. data/lib/patriot/command/command_macro.rb +141 -0
  9. data/lib/patriot/command/composite.rb +49 -0
  10. data/lib/patriot/command/parser.rb +78 -0
  11. data/lib/patriot/command/sh_command.rb +42 -0
  12. data/lib/patriot/controller.rb +2 -0
  13. data/lib/patriot/controller/package_controller.rb +81 -0
  14. data/lib/patriot/controller/worker_admin_controller.rb +159 -0
  15. data/lib/patriot/job_store.rb +66 -0
  16. data/lib/patriot/job_store/base.rb +159 -0
  17. data/lib/patriot/job_store/factory.rb +19 -0
  18. data/lib/patriot/job_store/in_memory_store.rb +252 -0
  19. data/lib/patriot/job_store/job.rb +118 -0
  20. data/lib/patriot/job_store/job_ticket.rb +30 -0
  21. data/lib/patriot/job_store/rdb_job_store.rb +353 -0
  22. data/lib/patriot/tool.rb +2 -0
  23. data/lib/patriot/tool/batch_parser.rb +102 -0
  24. data/lib/patriot/tool/patriot_command.rb +48 -0
  25. data/lib/patriot/tool/patriot_commands/execute.rb +92 -0
  26. data/lib/patriot/tool/patriot_commands/job.rb +62 -0
  27. data/lib/patriot/tool/patriot_commands/plugin.rb +41 -0
  28. data/lib/patriot/tool/patriot_commands/register.rb +77 -0
  29. data/lib/patriot/tool/patriot_commands/upgrade.rb +24 -0
  30. data/lib/patriot/tool/patriot_commands/validate.rb +84 -0
  31. data/lib/patriot/tool/patriot_commands/worker.rb +35 -0
  32. data/lib/patriot/tool/patriot_commands/worker_admin.rb +60 -0
  33. data/lib/patriot/util.rb +14 -0
  34. data/lib/patriot/util/config.rb +58 -0
  35. data/lib/patriot/util/config/base.rb +22 -0
  36. data/lib/patriot/util/config/inifile_config.rb +63 -0
  37. data/lib/patriot/util/cron_format_parser.rb +104 -0
  38. data/lib/patriot/util/date_util.rb +200 -0
  39. data/lib/patriot/util/db_client.rb +65 -0
  40. data/lib/patriot/util/db_client/base.rb +142 -0
  41. data/lib/patriot/util/db_client/hash_record.rb +53 -0
  42. data/lib/patriot/util/db_client/record.rb +25 -0
  43. data/lib/patriot/util/logger.rb +24 -0
  44. data/lib/patriot/util/logger/facade.rb +33 -0
  45. data/lib/patriot/util/logger/factory.rb +59 -0
  46. data/lib/patriot/util/logger/log4r_factory.rb +111 -0
  47. data/lib/patriot/util/logger/webrick_log_factory.rb +47 -0
  48. data/lib/patriot/util/param.rb +73 -0
  49. data/lib/patriot/util/retry.rb +30 -0
  50. data/lib/patriot/util/script.rb +52 -0
  51. data/lib/patriot/util/system.rb +120 -0
  52. data/lib/patriot/worker.rb +35 -0
  53. data/lib/patriot/worker/base.rb +153 -0
  54. data/lib/patriot/worker/info_server.rb +90 -0
  55. data/lib/patriot/worker/job_store_server.rb +32 -0
  56. data/lib/patriot/worker/multi_node_worker.rb +157 -0
  57. data/lib/patriot/worker/servlet.rb +23 -0
  58. data/lib/patriot/worker/servlet/job_servlet.rb +128 -0
  59. data/lib/patriot/worker/servlet/worker_status_servlet.rb +44 -0
  60. data/skel/batch/sample/daily/test.pbc +4 -0
  61. data/skel/config/patriot.ini +21 -0
  62. data/skel/public/css/bootstrap.css +2495 -0
  63. data/skel/public/css/original.css +54 -0
  64. data/skel/public/js/bootstrap-alerts.js +124 -0
  65. data/skel/public/js/bootstrap-buttons.js +64 -0
  66. data/skel/public/js/bootstrap-dropdown.js +55 -0
  67. data/skel/public/js/bootstrap-modal.js +260 -0
  68. data/skel/public/js/bootstrap-popover.js +90 -0
  69. data/skel/public/js/bootstrap-scrollspy.js +107 -0
  70. data/skel/public/js/bootstrap-tabs.js +80 -0
  71. data/skel/public/js/bootstrap-twipsy.js +321 -0
  72. data/skel/public/js/jquery-1.6.4.min.js +4 -0
  73. data/skel/public/templates/_jobs.erb +97 -0
  74. data/skel/public/templates/job.erb +119 -0
  75. data/skel/public/templates/jobs.erb +21 -0
  76. data/skel/public/templates/jobs_deleted.erb +6 -0
  77. data/skel/public/templates/layout.erb +103 -0
  78. data/skel/public/templates/state_updated.erb +6 -0
  79. 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
@@ -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
+