activewarehouse-etl 0.9.0 → 0.9.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.
- data/CHANGELOG +22 -2
- data/README +12 -0
- data/Rakefile +64 -59
- data/bin/etl +0 -0
- data/bin/etl.cmd +8 -0
- data/examples/database.example.yml +11 -1
- data/lib/etl.rb +9 -21
- data/lib/etl/builder.rb +2 -1
- data/lib/etl/builder/date_dimension_builder.rb +67 -54
- data/lib/etl/builder/time_dimension_builder.rb +31 -0
- data/lib/etl/commands/etl.rb +1 -2
- data/lib/etl/control/control.rb +46 -18
- data/lib/etl/control/destination.rb +201 -138
- data/lib/etl/control/destination/database_destination.rb +10 -5
- data/lib/etl/control/source.rb +1 -1
- data/lib/etl/control/source/database_source.rb +8 -10
- data/lib/etl/core_ext/time/calculations.rb +4 -2
- data/lib/etl/engine.rb +35 -10
- data/lib/etl/execution/migration.rb +21 -9
- data/lib/etl/generator/generator.rb +1 -1
- data/lib/etl/http_tools.rb +21 -7
- data/lib/etl/parser/apache_combined_log_parser.rb +3 -1
- data/lib/etl/parser/delimited_parser.rb +1 -1
- data/lib/etl/parser/parser.rb +1 -1
- data/lib/etl/processor/block_processor.rb +14 -0
- data/lib/etl/processor/bulk_import_processor.rb +5 -1
- data/lib/etl/processor/check_exist_processor.rb +1 -0
- data/lib/etl/processor/encode_processor.rb +55 -0
- data/lib/etl/transform/date_to_string_transform.rb +1 -0
- data/lib/etl/transform/foreign_key_lookup_transform.rb +67 -2
- data/lib/etl/transform/string_to_date_transform.rb +6 -1
- data/lib/etl/transform/string_to_datetime_transform.rb +1 -1
- data/lib/etl/transform/string_to_time_transform.rb +1 -1
- data/lib/etl/version.rb +1 -1
- metadata +94 -78
data/CHANGELOG
CHANGED
@@ -165,7 +165,7 @@
|
|
165
165
|
0.8.4 - May 24, 2007
|
166
166
|
* Added fix for backslash in file writer
|
167
167
|
|
168
|
-
0.9.0 -
|
168
|
+
0.9.0 - August 9, 2007
|
169
169
|
* Added support for batch processing through .ebf files. These files are
|
170
170
|
essentially control files that apply settings to an entire ETL process.
|
171
171
|
* Implemented support for screen blocks. These blocks can be used to test
|
@@ -175,4 +175,24 @@
|
|
175
175
|
connection information in the control files.
|
176
176
|
* Implemented temp table support throughout.
|
177
177
|
* DateDimensionBuilder now included in ActiveWarehouse ETL directly.
|
178
|
-
* Time calculations for fiscal year now included in ActiveWarehouse ETL.
|
178
|
+
* Time calculations for fiscal year now included in ActiveWarehouse ETL.
|
179
|
+
|
180
|
+
0.9.1 -
|
181
|
+
* SQLResolver now uses ETL::Engine.table so it may utilize temp tables. (aeden)
|
182
|
+
* Added Thibaut Barrère's encode processor.
|
183
|
+
* Added MockSource and MockDestination test helpers (thbar)
|
184
|
+
* Added the block processor. Can call a block once (pre/post processor)
|
185
|
+
or once for each row (after_read/before_write row processor) (thbar)
|
186
|
+
* Changed temp table to use new AdapterExtension copy_table method (aeden)
|
187
|
+
* Added bin/etl.cmd windows batch - just add the bin folder to your PATH
|
188
|
+
and it will let you call etl on an unpacked/pistoned version of AW-ETL (thbar)
|
189
|
+
* Upgraded to support Rails 2.1. No longer compatible with older versions of Rails.
|
190
|
+
* Added ETL::Builder::TimeDimensionBuilder
|
191
|
+
* Added :default option to ForeignKeyLookupTransform that will be used if no
|
192
|
+
foreign key is found.
|
193
|
+
* Added :cache option to ForeignKeyLookupTransform that will preload the FK
|
194
|
+
mappings if the underlying resolver supports it. Currently supported by
|
195
|
+
SQLResolver.
|
196
|
+
* A Class extending ETL::Transform::Transform may now be passed as a transformer.
|
197
|
+
For example, in the control file you would define the transform as:
|
198
|
+
transform :a_field, MyTransform, {:option1 => 'option1'}.
|
data/README
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
Ruby Extract-Transform-Load (ETL) tool.
|
2
2
|
|
3
|
+
== Requirements
|
4
|
+
|
5
|
+
* Ruby 1.8.5 or higher
|
6
|
+
* Rubygems
|
7
|
+
|
8
|
+
== Online Documentation
|
9
|
+
|
10
|
+
Available at http://activewarehouse.rubyforge.org/docs/activewarehouse-etl.html
|
11
|
+
|
3
12
|
== Features
|
4
13
|
|
5
14
|
Current supported features:
|
@@ -67,6 +76,9 @@ Command line options:
|
|
67
76
|
== Control File Examples
|
68
77
|
Control file examples can be found in the examples directory.
|
69
78
|
|
79
|
+
== Running Tests
|
80
|
+
The tests require Shoulda 1.x.
|
81
|
+
|
70
82
|
== Feedback
|
71
83
|
This is a work in progress. Comments should be made on the
|
72
84
|
activewarehouse-discuss mailing list at the moment. Contributions are always
|
data/Rakefile
CHANGED
@@ -7,16 +7,13 @@ require 'rake/contrib/rubyforgepublisher'
|
|
7
7
|
|
8
8
|
require File.join(File.dirname(__FILE__), 'lib/etl', 'version')
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
RUBY_FORGE_PROJECT = "activewarehouse"
|
19
|
-
RUBY_FORGE_USER = "aeden"
|
10
|
+
module AWETL
|
11
|
+
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
12
|
+
PKG_NAME = 'activewarehouse-etl'
|
13
|
+
PKG_VERSION = ETL::VERSION::STRING + PKG_BUILD
|
14
|
+
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
15
|
+
PKG_DESTINATION = ENV["PKG_DESTINATION"] || "../#{PKG_NAME}"
|
16
|
+
end
|
20
17
|
|
21
18
|
desc 'Default: run unit tests.'
|
22
19
|
task :default => :test
|
@@ -45,54 +42,62 @@ namespace :rcov do
|
|
45
42
|
mkdir 'coverage' unless File.exist?('coverage')
|
46
43
|
rcov = "rcov --aggregate coverage.data --text-summary -Ilib"
|
47
44
|
system("#{rcov} test/*_test.rb")
|
48
|
-
system("open coverage/index.html") if PLATFORM['darwin']
|
45
|
+
# system("open coverage/index.html") if PLATFORM['darwin']
|
49
46
|
end
|
50
47
|
end
|
51
48
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
49
|
+
# Gem Spec
|
50
|
+
|
51
|
+
module AWETL
|
52
|
+
def self.package_files(package_prefix)
|
53
|
+
FileList[
|
54
|
+
"#{package_prefix}CHANGELOG",
|
55
|
+
"#{package_prefix}LICENSE",
|
56
|
+
"#{package_prefix}README",
|
57
|
+
"#{package_prefix}TODO",
|
58
|
+
"#{package_prefix}Rakefile",
|
59
|
+
"#{package_prefix}bin/**/*",
|
60
|
+
"#{package_prefix}doc/**/*",
|
61
|
+
"#{package_prefix}lib/**/*",
|
62
|
+
"#{package_prefix}examples/**/*",
|
63
|
+
] - [ "#{package_prefix}test" ]
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.spec(package_prefix = '')
|
67
|
+
Gem::Specification.new do |s|
|
68
|
+
s.name = 'activewarehouse-etl'
|
69
|
+
s.version = AWETL::PKG_VERSION
|
70
|
+
s.summary = "Pure Ruby ETL package."
|
71
|
+
s.description = <<-EOF
|
72
|
+
ActiveWarehouse ETL is a pure Ruby Extract-Transform-Load application for loading data into a database.
|
73
|
+
EOF
|
74
|
+
|
75
|
+
s.add_dependency('rake', '>= 0.7.1')
|
76
|
+
s.add_dependency('activesupport', '>= 1.3.1')
|
77
|
+
s.add_dependency('activerecord', '>= 1.14.4')
|
78
|
+
s.add_dependency('fastercsv', '>= 1.2.0')
|
79
|
+
s.add_dependency('adapter_extensions', '>= 0.1.0')
|
80
|
+
|
81
|
+
s.rdoc_options << '--exclude' << '.'
|
82
|
+
s.has_rdoc = false
|
83
|
+
|
84
|
+
s.files = package_files(package_prefix).to_a.delete_if {|f| f.include?('.svn')}
|
85
|
+
s.require_path = 'lib'
|
86
|
+
|
87
|
+
s.bindir = "#{package_prefix}bin" # Use these for applications.
|
88
|
+
s.executables = ['etl']
|
89
|
+
s.default_executable = "etl"
|
90
|
+
|
91
|
+
s.author = "Anthony Eden"
|
92
|
+
s.email = "anthonyeden@gmail.com"
|
93
|
+
s.homepage = "http://activewarehouse.rubyforge.org/etl"
|
94
|
+
s.rubyforge_project = "activewarehouse"
|
95
|
+
end
|
96
|
+
end
|
92
97
|
end
|
93
98
|
|
94
|
-
Rake::GemPackageTask.new(spec) do |pkg|
|
95
|
-
pkg.gem_spec = spec
|
99
|
+
Rake::GemPackageTask.new(AWETL.spec) do |pkg|
|
100
|
+
pkg.gem_spec = AWETL.spec
|
96
101
|
pkg.need_tar = true
|
97
102
|
pkg.need_zip = true
|
98
103
|
end
|
@@ -112,10 +117,10 @@ task :lines do
|
|
112
117
|
codelines += 1
|
113
118
|
end
|
114
119
|
puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}"
|
115
|
-
|
120
|
+
|
116
121
|
total_lines += lines
|
117
122
|
total_codelines += codelines
|
118
|
-
|
123
|
+
|
119
124
|
lines, codelines = 0, 0
|
120
125
|
end
|
121
126
|
|
@@ -127,7 +132,7 @@ task :release => [ :package ] do
|
|
127
132
|
`rubyforge login`
|
128
133
|
|
129
134
|
for ext in %w( gem tgz zip )
|
130
|
-
release_command = "rubyforge add_release activewarehouse #{PKG_NAME} 'REL #{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}"
|
135
|
+
release_command = "rubyforge add_release activewarehouse #{AWETL::PKG_NAME} 'REL #{AWETL::PKG_VERSION}' pkg/#{AWETL::PKG_NAME}-#{AWETL::PKG_VERSION}.#{ext}"
|
131
136
|
puts release_command
|
132
137
|
system(release_command)
|
133
138
|
end
|
@@ -143,6 +148,6 @@ task :reinstall => [:package] do
|
|
143
148
|
windows = RUBY_PLATFORM =~ /mswin/
|
144
149
|
sudo = windows ? '' : 'sudo'
|
145
150
|
gem = windows ? 'gem.bat' : 'gem'
|
146
|
-
`#{sudo} #{gem} uninstall
|
147
|
-
`#{sudo} #{gem} install pkg/#{PKG_NAME}-#{PKG_VERSION}`
|
148
|
-
end
|
151
|
+
`#{sudo} #{gem} uninstall #{AWETL::PKG_NAME} -x`
|
152
|
+
`#{sudo} #{gem} install pkg/#{AWETL::PKG_NAME}-#{AWETL::PKG_VERSION}`
|
153
|
+
end
|
data/bin/etl
CHANGED
File without changes
|
data/bin/etl.cmd
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
@echo off
|
2
|
+
|
3
|
+
rem The purpose of this Windows script is to let you use the etl command line with a non-gem version of AW-ETL (eg: unpacked gem, pistoned trunk).
|
4
|
+
rem Just add the current folder on top of your PATH variable to use it instead of the etl command provided with the gem release.
|
5
|
+
|
6
|
+
rem %~dp0 returns the absolute path where the current script is. We just append 'etl' to it, and forward all the arguments with %*
|
7
|
+
|
8
|
+
ruby "%~dp0etl" %*
|
@@ -3,4 +3,14 @@ etl_execution:
|
|
3
3
|
username: root
|
4
4
|
host: localhost
|
5
5
|
database: etl_execution
|
6
|
-
encoding: utf8
|
6
|
+
encoding: utf8
|
7
|
+
datawarehouse:
|
8
|
+
adapter: mysql
|
9
|
+
username: root
|
10
|
+
host: localhost
|
11
|
+
database: datawarehouse_development
|
12
|
+
operational:
|
13
|
+
adapter: mysql
|
14
|
+
username: root
|
15
|
+
host: localhost
|
16
|
+
database: operational_production
|
data/lib/etl.rb
CHANGED
@@ -31,29 +31,15 @@ require 'erb'
|
|
31
31
|
|
32
32
|
require 'rubygems'
|
33
33
|
|
34
|
-
unless
|
35
|
-
|
34
|
+
unless defined?(REXML::VERSION)
|
35
|
+
require 'rexml/rexml'
|
36
|
+
REXML::VERSION = REXML::Version
|
36
37
|
end
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
unless defined?(ActiveRecord)
|
44
|
-
gem 'activerecord'
|
45
|
-
require 'active_record'
|
46
|
-
end
|
47
|
-
|
48
|
-
unless defined?(AdapterExtensions)
|
49
|
-
gem 'adapter_extensions'
|
50
|
-
require 'adapter_extensions'
|
51
|
-
end
|
52
|
-
|
53
|
-
unless defined?(FasterCSV)
|
54
|
-
gem 'fastercsv'
|
55
|
-
require 'faster_csv'
|
56
|
-
end
|
39
|
+
require 'active_support'
|
40
|
+
require 'active_record'
|
41
|
+
require 'adapter_extensions'
|
42
|
+
require 'faster_csv'
|
57
43
|
|
58
44
|
$:.unshift(File.dirname(__FILE__))
|
59
45
|
|
@@ -79,6 +65,8 @@ module ETL #:nodoc:
|
|
79
65
|
end
|
80
66
|
class DefinitionError < ControlError #:nodoc:
|
81
67
|
end
|
68
|
+
class ConfigurationError < ControlError #:nodoc:
|
69
|
+
end
|
82
70
|
class MismatchError < ETLError #:nodoc:
|
83
71
|
end
|
84
72
|
class ResolverError < ETLError #:nodoc:
|
data/lib/etl/builder.rb
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
require 'etl/builder/date_dimension_builder'
|
1
|
+
require 'etl/builder/date_dimension_builder'
|
2
|
+
require 'etl/builder/time_dimension_builder'
|
@@ -11,6 +11,9 @@ module ETL #:nodoc:
|
|
11
11
|
|
12
12
|
# Define any holiday indicators
|
13
13
|
attr_accessor :holiday_indicators
|
14
|
+
|
15
|
+
# Add offset month for fiscal year
|
16
|
+
attr_accessor :fiscal_year_offset_month
|
14
17
|
|
15
18
|
# Define the weekday indicators. The default array begins on Sunday and goes to Saturday.
|
16
19
|
cattr_accessor :weekday_indicators
|
@@ -20,64 +23,74 @@ module ETL #:nodoc:
|
|
20
23
|
#
|
21
24
|
# * <tt>start_date</tt>: The start date. Defaults to 5 years ago from today.
|
22
25
|
# * <tt>end_date</tt>: The end date. Defaults to now.
|
23
|
-
def initialize(start_date=Time.now.years_ago(5), end_date=Time.now)
|
24
|
-
@start_date = start_date
|
25
|
-
@end_date = end_date
|
26
|
+
def initialize(start_date=Time.now.years_ago(5), end_date=Time.now, fiscal_year_offset_month=10)
|
27
|
+
@start_date = start_date.to_date
|
28
|
+
@end_date = end_date.to_date
|
29
|
+
@fiscal_year_offset_month = fiscal_year_offset_month.to_i
|
26
30
|
@holiday_indicators = []
|
27
31
|
end
|
28
32
|
|
29
|
-
# Returns an array of hashes representing records in the dimension.
|
30
|
-
# accessed by name.
|
33
|
+
# Returns an array of hashes representing records in the dimension.
|
31
34
|
def build(options={})
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
35
|
+
(start_date..end_date).map { |date| record_from_date(date) }
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# Returns a hash representing a record in the dimension. The values for each record are
|
41
|
+
# accessed by name.
|
42
|
+
def record_from_date(date)
|
43
|
+
time = date.to_time # need methods only available in Time
|
44
|
+
record = {}
|
45
|
+
record[:date] = time.strftime("%m/%d/%Y")
|
46
|
+
record[:full_date_description] = time.strftime("%B %d,%Y")
|
47
|
+
record[:day_of_week] = time.strftime("%A")
|
48
|
+
record[:day_in_week] = record[:day_of_week] # alias
|
49
|
+
#record[:day_number_in_epoch] = time.to_i / 24
|
50
|
+
#record[:week_number_in_epoch] = time.to_i / (24 * 7)
|
51
|
+
#record[:month_number_in_epoch] = time.to_i / (24 * 7 * 30)
|
52
|
+
record[:day_number_in_calendar_month] = time.day
|
53
|
+
record[:day_number_in_calendar_year] = time.yday
|
54
|
+
record[:day_number_in_fiscal_month] = time.day # should this be different from CY?
|
55
|
+
record[:day_number_in_fiscal_year] = time.fiscal_year_yday(fiscal_year_offset_month)
|
56
|
+
#record[:last_day_in_week_indicator] =
|
57
|
+
#record[:last_day_in_month_indicator] =
|
58
|
+
#record[:calendar_week_ending_date] =
|
59
|
+
record[:calendar_week] = "Week #{time.week}"
|
60
|
+
record[:calendar_week_number] = time.week
|
61
|
+
record[:calendar_week_number_in_year] = time.week # DEPRECATED
|
62
|
+
record[:calendar_month_name] = time.strftime("%B")
|
63
|
+
record[:calendar_month_number_in_year] = time.month # DEPRECATED
|
64
|
+
record[:calendar_month_number] = time.month
|
65
|
+
record[:calendar_year_month] = time.strftime("%Y-%m")
|
66
|
+
record[:calendar_quarter] = "Q#{time.quarter}"
|
67
|
+
record[:calendar_quarter_number] = time.quarter
|
68
|
+
record[:calendar_quarter_number_in_year] = time.quarter # DEPRECATED
|
69
|
+
record[:calendar_year_quarter] = "#{time.strftime('%Y')}-#{record[:calendar_quarter]}"
|
70
|
+
#record[:calendar_half_year] =
|
71
|
+
record[:calendar_year] = "#{time.year}"
|
72
|
+
record[:fiscal_week] = "FY Week #{time.fiscal_year_week(fiscal_year_offset_month)}"
|
73
|
+
record[:fiscal_week_number_in_year] = time.fiscal_year_week(fiscal_year_offset_month) # DEPRECATED
|
74
|
+
record[:fiscal_week_number] = time.fiscal_year_week(fiscal_year_offset_month)
|
75
|
+
record[:fiscal_month] = time.fiscal_year_month(fiscal_year_offset_month)
|
76
|
+
record[:fiscal_month_number] = time.fiscal_year_month(fiscal_year_offset_month)
|
77
|
+
record[:fiscal_month_number_in_year] = time.fiscal_year_month(fiscal_year_offset_month) # DEPRECATED
|
78
|
+
record[:fiscal_year_month] = "FY#{time.fiscal_year(fiscal_year_offset_month)}-" + time.fiscal_year_month(fiscal_year_offset_month).to_s.rjust(2, '0')
|
79
|
+
record[:fiscal_quarter] = "FY Q#{time.fiscal_year_quarter(fiscal_year_offset_month)}"
|
80
|
+
record[:fiscal_year_quarter] = "FY#{time.fiscal_year(fiscal_year_offset_month)}-Q#{time.fiscal_year_quarter(fiscal_year_offset_month)}"
|
81
|
+
record[:fiscal_quarter_number] = time.fiscal_year_quarter(fiscal_year_offset_month) # DEPRECATED
|
82
|
+
record[:fiscal_year_quarter_number] = time.fiscal_year_quarter(fiscal_year_offset_month)
|
83
|
+
#record[:fiscal_half_year] =
|
84
|
+
record[:fiscal_year] = "FY#{time.fiscal_year(fiscal_year_offset_month)}"
|
85
|
+
record[:fiscal_year_number] = time.fiscal_year(fiscal_year_offset_month)
|
86
|
+
record[:holiday_indicator] = holiday_indicators.include?(date) ? 'Holiday' : 'Nonholiday'
|
87
|
+
record[:weekday_indicator] = weekday_indicators[time.wday]
|
88
|
+
record[:selling_season] = 'None'
|
89
|
+
record[:major_event] = 'None'
|
90
|
+
record[:sql_date_stamp] = date
|
91
|
+
|
92
|
+
record
|
80
93
|
end
|
81
94
|
end
|
82
95
|
end
|
83
|
-
end
|
96
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module ETL #:nodoc:
|
2
|
+
module Builder #:nodoc:
|
3
|
+
# Builder that creates a simple time dimension.
|
4
|
+
class TimeDimensionBuilder
|
5
|
+
def initialize
|
6
|
+
# Returns an array of hashes representing records in the dimension. The values for each record are
|
7
|
+
# accessed by name.
|
8
|
+
def build(options={})
|
9
|
+
records = []
|
10
|
+
0.upto(23) do |t_hour|
|
11
|
+
0.upto(59) do |t_minute|
|
12
|
+
0.upto(59) do |t_second|
|
13
|
+
t_hour_string = t_hour.to_s.rjust(2, '0')
|
14
|
+
t_minute_string = t_minute.to_s.rjust(2, '0')
|
15
|
+
t_second_string = t_second.to_s.rjust(2, '0')
|
16
|
+
record = {}
|
17
|
+
record[:hour] = t_hour
|
18
|
+
record[:minute] = t_minute
|
19
|
+
record[:second] = t_second
|
20
|
+
record[:minute_description] = "#{t_hour_string}:#{t_minute_string}"
|
21
|
+
record[:full_description] = "#{t_hour_string}:#{t_minute_string}:#{t_second_string}"
|
22
|
+
records << record
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
records
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|