safedb 0.01.0001
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 +7 -0
- data/.gitignore +8 -0
- data/.yardopts +3 -0
- data/Gemfile +10 -0
- data/LICENSE +21 -0
- data/README.md +793 -0
- data/Rakefile +16 -0
- data/bin/safe +5 -0
- data/lib/configs/README.md +58 -0
- data/lib/extension/array.rb +162 -0
- data/lib/extension/dir.rb +35 -0
- data/lib/extension/file.rb +123 -0
- data/lib/extension/hash.rb +33 -0
- data/lib/extension/string.rb +572 -0
- data/lib/factbase/facts.safedb.net.ini +38 -0
- data/lib/interprete.rb +462 -0
- data/lib/keytools/PRODUCE_RAND_SEQ_USING_DEV_URANDOM.txt +0 -0
- data/lib/keytools/kdf.api.rb +243 -0
- data/lib/keytools/kdf.bcrypt.rb +265 -0
- data/lib/keytools/kdf.pbkdf2.rb +262 -0
- data/lib/keytools/kdf.scrypt.rb +190 -0
- data/lib/keytools/key.64.rb +326 -0
- data/lib/keytools/key.algo.rb +109 -0
- data/lib/keytools/key.api.rb +1391 -0
- data/lib/keytools/key.db.rb +330 -0
- data/lib/keytools/key.docs.rb +195 -0
- data/lib/keytools/key.error.rb +110 -0
- data/lib/keytools/key.id.rb +271 -0
- data/lib/keytools/key.ident.rb +243 -0
- data/lib/keytools/key.iv.rb +107 -0
- data/lib/keytools/key.local.rb +259 -0
- data/lib/keytools/key.now.rb +402 -0
- data/lib/keytools/key.pair.rb +259 -0
- data/lib/keytools/key.pass.rb +120 -0
- data/lib/keytools/key.rb +585 -0
- data/lib/logging/gem.logging.rb +132 -0
- data/lib/modules/README.md +43 -0
- data/lib/modules/cryptology/aes-256.rb +154 -0
- data/lib/modules/cryptology/amalgam.rb +70 -0
- data/lib/modules/cryptology/blowfish.rb +130 -0
- data/lib/modules/cryptology/cipher.rb +207 -0
- data/lib/modules/cryptology/collect.rb +138 -0
- data/lib/modules/cryptology/crypt.io.rb +225 -0
- data/lib/modules/cryptology/engineer.rb +99 -0
- data/lib/modules/mappers/dictionary.rb +288 -0
- data/lib/modules/storage/coldstore.rb +186 -0
- data/lib/modules/storage/git.store.rb +399 -0
- data/lib/session/fact.finder.rb +334 -0
- data/lib/session/require.gem.rb +112 -0
- data/lib/session/time.stamp.rb +340 -0
- data/lib/session/user.home.rb +49 -0
- data/lib/usecase/cmd.rb +487 -0
- data/lib/usecase/config/README.md +57 -0
- data/lib/usecase/docker/README.md +146 -0
- data/lib/usecase/docker/docker.rb +49 -0
- data/lib/usecase/edit/README.md +43 -0
- data/lib/usecase/edit/delete.rb +46 -0
- data/lib/usecase/export.rb +40 -0
- data/lib/usecase/files/README.md +37 -0
- data/lib/usecase/files/eject.rb +56 -0
- data/lib/usecase/files/file_me.rb +78 -0
- data/lib/usecase/files/read.rb +169 -0
- data/lib/usecase/files/write.rb +89 -0
- data/lib/usecase/goto.rb +57 -0
- data/lib/usecase/id.rb +36 -0
- data/lib/usecase/import.rb +157 -0
- data/lib/usecase/init.rb +63 -0
- data/lib/usecase/jenkins/README.md +146 -0
- data/lib/usecase/jenkins/jenkins.rb +208 -0
- data/lib/usecase/login.rb +71 -0
- data/lib/usecase/logout.rb +28 -0
- data/lib/usecase/open.rb +71 -0
- data/lib/usecase/print.rb +40 -0
- data/lib/usecase/put.rb +81 -0
- data/lib/usecase/set.rb +44 -0
- data/lib/usecase/show.rb +138 -0
- data/lib/usecase/terraform/README.md +91 -0
- data/lib/usecase/terraform/terraform.rb +121 -0
- data/lib/usecase/token.rb +35 -0
- data/lib/usecase/update/README.md +55 -0
- data/lib/usecase/update/rename.rb +180 -0
- data/lib/usecase/use.rb +41 -0
- data/lib/usecase/verse.rb +20 -0
- data/lib/usecase/view.rb +71 -0
- data/lib/usecase/vpn/README.md +150 -0
- data/lib/usecase/vpn/vpn.ini +31 -0
- data/lib/usecase/vpn/vpn.rb +54 -0
- data/lib/version.rb +3 -0
- data/safedb.gemspec +34 -0
- metadata +193 -0
@@ -0,0 +1,259 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
module SafeDb
|
5
|
+
|
6
|
+
# The command line interface has a high entropy randomly generated
|
7
|
+
# key whose purpose is to <b>lock the application's data key</b> for
|
8
|
+
# the duration of the session which is between a login and a logout.
|
9
|
+
#
|
10
|
+
# These keys are unique to only one shell session on one workstation
|
11
|
+
# and they live lives that are no longer (and mostly shorter) than
|
12
|
+
# the life of the parent shell.
|
13
|
+
#
|
14
|
+
# == The 4 CLI Shell Entities
|
15
|
+
#
|
16
|
+
# The four (4) important entities within the shell session are
|
17
|
+
#
|
18
|
+
# - an obfuscator key for locking the shell key during a session
|
19
|
+
# - a high entropy randomly generated shell key for locking the app data key
|
20
|
+
# - one environment variable whose value embodies three (3) data segments
|
21
|
+
# - a session id derived by pushing the env var through a one-way function
|
22
|
+
class KeyLocal
|
23
|
+
|
24
|
+
|
25
|
+
# The number of Radix64 characters that make up a valid BCrypt salt.
|
26
|
+
# To create a BCrypt salt use
|
27
|
+
BCRYPT_SALT_LENGTH = 22
|
28
|
+
|
29
|
+
|
30
|
+
# There are two digits representing the BCrypt iteration count.
|
31
|
+
# The minimum is 10 and the maximum is 16.
|
32
|
+
BCRYPT_ITER_COUNT_SIZE = 2
|
33
|
+
|
34
|
+
|
35
|
+
# The session token comprises of 3 segments with fixed lengths.
|
36
|
+
# This triply segmented text token that can be used to decrypt
|
37
|
+
# and deliver the shell key.
|
38
|
+
SESSION_TOKEN_SIZE = 128 + 22 + BCRYPT_ITER_COUNT_SIZE
|
39
|
+
|
40
|
+
|
41
|
+
# Given a 152 character session token, what is the index that pinpoints
|
42
|
+
# the beginning of the 22 character BCrypt salt? The answer is given
|
43
|
+
# by this BCRYPT_SALT_START_INDEX constant.
|
44
|
+
BCRYPT_SALT_START_INDEX = SESSION_TOKEN_SIZE - BCRYPT_SALT_LENGTH - BCRYPT_ITER_COUNT_SIZE
|
45
|
+
|
46
|
+
|
47
|
+
# What index pinpoints the end of the BCrypt salt itself.
|
48
|
+
# This is easy as the final 2 characters are the iteration count
|
49
|
+
# so the end index is the length subtract 1 subtract 2.
|
50
|
+
BCRYPT_SALT_END_INDEX = SESSION_TOKEN_SIZE - 1
|
51
|
+
|
52
|
+
|
53
|
+
# Initialize the session by generating a random high entropy shell token
|
54
|
+
# and then generate an obfuscator key which we use to lock the shell
|
55
|
+
# key and return a triply segmented text token that can be used to decrypt
|
56
|
+
# and deliver the shell key as long as the same shell on the same machine
|
57
|
+
# is employed to make the call.
|
58
|
+
#
|
59
|
+
# <b>The 3 Session Token Segments</b>
|
60
|
+
#
|
61
|
+
# The session token is divided up into 3 segments with a total of 150
|
62
|
+
# characters.
|
63
|
+
#
|
64
|
+
# | -------- | ------------ | ------------------------------------- |
|
65
|
+
# | Segment | Length | Purpose |
|
66
|
+
# | -------- | ------------ | ------------------------------------- |
|
67
|
+
# | 1 | 16 bytes | AES Encrypt Initialization Vector(IV) |
|
68
|
+
# | 2 | 80 bytes | Cipher text from Random Key AES crypt |
|
69
|
+
# | 3 | 22 chars | Salt for obfuscator key derivation |
|
70
|
+
# | -------- | ------------ | ------------------------------------- |
|
71
|
+
# | Total | 150 chars | Session Token in Environment Variable |
|
72
|
+
# | -------- | ------------ | ------------------------------------- |
|
73
|
+
#
|
74
|
+
# Why is the <b>16 byte salt and the 80 byte BCrypt ciphertext</b> represented
|
75
|
+
# by <b>128 base64 characters</b>?
|
76
|
+
#
|
77
|
+
# 16 bytes + 80 bytes = 96 bytes
|
78
|
+
# 96 bytes x 8 bits = 768 bits
|
79
|
+
# 768 bits / 6 bits = 128 base64 characters
|
80
|
+
#
|
81
|
+
# @return [String]
|
82
|
+
# return a triply segmented text token that can be used to decrypt
|
83
|
+
# and redeliver the high entropy session shell key on the same machine
|
84
|
+
# and within the same shell on the same machine.
|
85
|
+
def self.generate_shell_key_and_token
|
86
|
+
|
87
|
+
bcrypt_salt_key = KdfBCrypt.generate_bcrypt_salt
|
88
|
+
obfuscator_key = derive_session_crypt_key( bcrypt_salt_key )
|
89
|
+
random_key_ciphertext = obfuscator_key.do_encrypt_key( Key.from_random() )
|
90
|
+
session_token = random_key_ciphertext + bcrypt_salt_key.reverse
|
91
|
+
assert_session_token_size( session_token )
|
92
|
+
|
93
|
+
return session_token
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
# Regenerate the random shell key that was instantiated and locked
|
99
|
+
# during the {instantiate_shell_key_and_generate_token} method.
|
100
|
+
#
|
101
|
+
# To successfully reacquire the randomly generated (and then locked)
|
102
|
+
# shell key we must be provided with five (5) data points, four (4)
|
103
|
+
# of which are embalmed within the 150 character session token
|
104
|
+
# parameter.
|
105
|
+
#
|
106
|
+
# <b>What we need to Regenerate the Shell Key</b>
|
107
|
+
#
|
108
|
+
# Regenerating the shell key is done in two steps when given the
|
109
|
+
# four (4) <b>session token segments</b> described below, and the
|
110
|
+
# shell identity key described in the {SafeDb::Identifier} class.
|
111
|
+
#
|
112
|
+
# The session token is divided up into 4 segments with a total of 152
|
113
|
+
# characters.
|
114
|
+
#
|
115
|
+
# | -------- | ------------ | ------------------------------------- |
|
116
|
+
# | Segment | Length | Purpose |
|
117
|
+
# | -------- | ------------ | ------------------------------------- |
|
118
|
+
# | 1 | 16 bytes | AES Encrypt Initialization Vector(IV) |
|
119
|
+
# | 2 | 80 bytes | Cipher text from Random Key AES crypt |
|
120
|
+
# | 3 | 22 chars | Salt 4 shell identity key derivation |
|
121
|
+
# | 4 | 2 chars | BCrypt iteration parameter (10 to 16) |
|
122
|
+
# | -------- | ------------ | ------------------------------------- |
|
123
|
+
# | Total | 152 chars | Session Token in Environment Variable |
|
124
|
+
# | -------- | ------------ | ------------------------------------- |
|
125
|
+
#
|
126
|
+
# @param session_token [String]
|
127
|
+
# a triply segmented (and one liner) text token instantiated by
|
128
|
+
# {self.instantiate_shell_key_and_generate_token} and provided
|
129
|
+
# here ad verbatim.
|
130
|
+
#
|
131
|
+
# @param use_grandparent_pid [Boolean]
|
132
|
+
#
|
133
|
+
# Optional boolean parameter. If set to true the PID (process ID) used
|
134
|
+
# as part of an obfuscator key and normally acquired from the parent
|
135
|
+
# process should now be acquired from the grandparent's process.
|
136
|
+
#
|
137
|
+
# Set to true when accessing the safe's credentials from a sub process
|
138
|
+
# rather than directly through the logged in shell.
|
139
|
+
#
|
140
|
+
# @return [SafeDb::Key]
|
141
|
+
# an extremely high entropy 256 bit key derived (digested) from 48
|
142
|
+
# random bytes at the beginning of the shell (cli) session.
|
143
|
+
def self.regenerate_shell_key( session_token, use_grandparent_pid = false )
|
144
|
+
|
145
|
+
assert_session_token_size( session_token )
|
146
|
+
bcrypt_salt = session_token[ BCRYPT_SALT_START_INDEX .. BCRYPT_SALT_END_INDEX ].reverse
|
147
|
+
assert_bcrypt_salt_size( bcrypt_salt )
|
148
|
+
|
149
|
+
key_ciphertext = session_token[ 0 .. ( BCRYPT_SALT_START_INDEX - 1 ) ]
|
150
|
+
obfuscator_key = derive_session_crypt_key( bcrypt_salt, use_grandparent_pid )
|
151
|
+
regenerated_key = obfuscator_key.do_decrypt_key( key_ciphertext )
|
152
|
+
|
153
|
+
return regenerated_key
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
# Derive a <b>short term (session scoped) encryption key</b> from the
|
159
|
+
# surrounding shell and workstation (machine) environment with an
|
160
|
+
# important same/different guarantee.
|
161
|
+
#
|
162
|
+
# The <b>same / different guarantee promises</b> us that the derived
|
163
|
+
# key will be
|
164
|
+
#
|
165
|
+
# - <b>the same</b> whenever called from within this executing shell
|
166
|
+
# - <b>different</b> when the shell and/or workstation are different
|
167
|
+
#
|
168
|
+
# This method uses a one-way function to return a combinatorial digested
|
169
|
+
# session identification string using a number of distinct parameters that
|
170
|
+
# deliver the important behaviours of changing in certain circumstances
|
171
|
+
# and remaining unchanged in others.
|
172
|
+
#
|
173
|
+
# <b>Change | When Should the key Change?</b>
|
174
|
+
#
|
175
|
+
# What is really important is that the <b>key changes when</b>
|
176
|
+
#
|
177
|
+
# - the <b>command shell</b> changes
|
178
|
+
# - the workstation <b>shell user is switched</b>
|
179
|
+
# - the host machine <b>workstation</b> is changed
|
180
|
+
# - the user <b>SSH's</b> into another shell
|
181
|
+
#
|
182
|
+
# A distinct workstation is identified by the first MAC address and the
|
183
|
+
# hostname of the machine.
|
184
|
+
#
|
185
|
+
# <b>Unchanged | When Should the Key Remain Unchanged?</b>
|
186
|
+
#
|
187
|
+
# Remaining <b>unchanged</b> in certain scenarios is a feature that is
|
188
|
+
# just as important as changing in others. The key must remain
|
189
|
+
# <b>unchanged</b> when
|
190
|
+
#
|
191
|
+
# - the <b>user returns to a command shell</b>
|
192
|
+
# - the user exits their <b>remote SSH session</b>
|
193
|
+
# - <b>sudo is used</b> to execute the commands
|
194
|
+
# - the user comes back to their <b>workstation</b>
|
195
|
+
# - the clock ticks into another day, month, year ...
|
196
|
+
#
|
197
|
+
# @param bcrypt_salt_key [SafeDb::Key]
|
198
|
+
#
|
199
|
+
# Either use BCrypt to generate the salt or retrieve and post in a
|
200
|
+
# previously generated salt which must hold 22 printable characters.
|
201
|
+
#
|
202
|
+
# @param use_grandparent_pid [Boolean]
|
203
|
+
#
|
204
|
+
# Optional boolean parameter. If set to true the PID (process ID) used
|
205
|
+
# as part of an obfuscator key and normally acquired from the parent
|
206
|
+
# process should now be acquired from the grandparent's process.
|
207
|
+
#
|
208
|
+
# Set to true when accessing the safe's credentials from a sub process
|
209
|
+
# rather than directly through the logged in shell.
|
210
|
+
#
|
211
|
+
# @return [SafeDb::Key]
|
212
|
+
# a digested key suitable for short term (session scoped) use with the
|
213
|
+
# guarantee that the same key will be returned whenever called from within
|
214
|
+
# the same executing shell environment and a different key when not.
|
215
|
+
def self.derive_session_crypt_key bcrypt_salt_key, use_grandparent_pid = false
|
216
|
+
|
217
|
+
shell_id_text = KeyIdent.derive_shell_identifier( use_grandparent_pid )
|
218
|
+
truncate_text = shell_id_text.length > KdfBCrypt::BCRYPT_MAX_IN_TEXT_LENGTH
|
219
|
+
shell_id_trim = shell_id_text unless truncate_text
|
220
|
+
shell_id_trim = shell_id_text[ 0 .. ( KdfBCrypt::BCRYPT_MAX_IN_TEXT_LENGTH - 1 ) ] if truncate_text
|
221
|
+
|
222
|
+
return KdfBCrypt.generate_key( shell_id_trim, bcrypt_salt_key )
|
223
|
+
|
224
|
+
end
|
225
|
+
|
226
|
+
|
227
|
+
private
|
228
|
+
|
229
|
+
|
230
|
+
# 000000000000000000000000000000000000000000000000000000000000000
|
231
|
+
# How to determine the caller.
|
232
|
+
# Better strategy would be just to print the stack trace
|
233
|
+
# That gives you much more bang for the one line buck.
|
234
|
+
# 000000000000000000000000000000000000000000000000000000000000000
|
235
|
+
# calling_module = File.basename caller_locations(1,1).first.absolute_path, ".rb"
|
236
|
+
# calling_method = caller_locations(1,1).first.base_label
|
237
|
+
# calling_lineno = caller_locations(1,1).first.lineno
|
238
|
+
# caller_details = "#{calling_module} | #{calling_method} | (line #{calling_lineno})"
|
239
|
+
# log.info(x) { "### Caller Details =>> =>> #{caller_details}" }
|
240
|
+
# 000000000000000000000000000000000000000000000000000000000000000
|
241
|
+
|
242
|
+
|
243
|
+
def self.assert_session_token_size session_token
|
244
|
+
err_msg = "Session token has #{session_token.length} and not #{SESSION_TOKEN_SIZE} chars."
|
245
|
+
raise RuntimeError, err_msg unless session_token.length == SESSION_TOKEN_SIZE
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
def self.assert_bcrypt_salt_size bcrypt_salt
|
250
|
+
amalgam_length = BCRYPT_SALT_LENGTH + BCRYPT_ITER_COUNT_SIZE
|
251
|
+
err_msg = "Expected BCrypt salt length of #{amalgam_length} not #{bcrypt_salt.length}."
|
252
|
+
raise RuntimeError, err_msg unless bcrypt_salt.length == amalgam_length
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
end
|
257
|
+
|
258
|
+
|
259
|
+
end
|
@@ -0,0 +1,402 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
module SafeDb
|
4
|
+
|
5
|
+
require 'singleton'
|
6
|
+
|
7
|
+
# This stamp sits at the centre of a fundamental DevOps pattern concerned
|
8
|
+
# with infrastructure provisioning and configuraion management.
|
9
|
+
#
|
10
|
+
# The central idea behind the pattern is to link every infrastructure
|
11
|
+
# object created during a session with a reference accurate to the nearest
|
12
|
+
# centi-second denoting the moment the software runtime (session) began.
|
13
|
+
class KeyNow
|
14
|
+
include Singleton
|
15
|
+
|
16
|
+
attr_reader :time_now
|
17
|
+
|
18
|
+
# Return two digit [mo] month index from 01 to 12.
|
19
|
+
# @example 02 => in February
|
20
|
+
#
|
21
|
+
def self.mo
|
22
|
+
return KeyNow.instance.time_now.strftime "%m"
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
# Return three character abbreviated month name.
|
27
|
+
# @example feb => in February
|
28
|
+
#
|
29
|
+
def self.mmm
|
30
|
+
return KeyNow.instance.time_now.strftime( "%b" ).downcase
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
#
|
35
|
+
# Return three character abbreviated day of week.
|
36
|
+
# @example tue => on Tuesday
|
37
|
+
#
|
38
|
+
def self.ddd
|
39
|
+
return KeyNow.instance.time_now.strftime( "%a" ).downcase
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
#
|
44
|
+
# Return two digit (character) hour of day from 00 to 23.
|
45
|
+
# @example 22 => between 22.00.00 and 22.59.59 inclusive
|
46
|
+
#
|
47
|
+
def self.hh
|
48
|
+
return KeyNow.instance.time_now.strftime "%H"
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
#
|
53
|
+
# Return two digit minute of hour from [00] to [59].
|
54
|
+
#
|
55
|
+
def self.mm
|
56
|
+
return KeyNow.instance.time_now.strftime "%M"
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
#
|
61
|
+
# Return two digit second of minute from [00] to [59].
|
62
|
+
#
|
63
|
+
def self.ss
|
64
|
+
return KeyNow.instance.time_now.strftime "%S"
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
#
|
69
|
+
# Return a [3 digit] second and tenth of second
|
70
|
+
# representation.
|
71
|
+
#
|
72
|
+
# The final digit is derived from the 1000 sliced
|
73
|
+
# millisecond of second running from 000 to 999.
|
74
|
+
#
|
75
|
+
# <tt>Truncation (Not Rounding)</tt>
|
76
|
+
#
|
77
|
+
# The [final] digit is acquired by TRUNCATING
|
78
|
+
# (chopping off) the last 2 of the 3 millisecond
|
79
|
+
# digits. No rounding is applied.
|
80
|
+
#
|
81
|
+
# The 3 returned digits comprise of the
|
82
|
+
#
|
83
|
+
# - second of minute => 2 digits | [00] to [59] (and)
|
84
|
+
# - tenth of second => 1 digit from [0] to [9]
|
85
|
+
#
|
86
|
+
# @example
|
87
|
+
#
|
88
|
+
# => The time at the 562nd millisecond of the 49th
|
89
|
+
# second of the minute.
|
90
|
+
#
|
91
|
+
# => 3 chars
|
92
|
+
# => 495
|
93
|
+
#
|
94
|
+
#
|
95
|
+
def self.sst
|
96
|
+
millisec_string = KeyNow.instance.time_now.strftime "%L"
|
97
|
+
return "#{ss}#{millisec_string[0]}"
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
#
|
102
|
+
# Return the [two] digit year (eg 19 for 2019).
|
103
|
+
# that we are currently in.
|
104
|
+
#
|
105
|
+
def self.yy
|
106
|
+
return KeyNow.instance.time_now.strftime("%Y")[2..-1]
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
#
|
111
|
+
# Return the [four] digit year (eg 2019)
|
112
|
+
# that we are currently in.
|
113
|
+
#
|
114
|
+
def self.yyyy
|
115
|
+
return KeyNow.instance.time_now.strftime("%Y")
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
# ------------------------------------------------- -- #
|
120
|
+
# Return 3 digit julian day of year [001] to [366]. -- #
|
121
|
+
# ------------------------------------------------- -- #
|
122
|
+
def self.jjj
|
123
|
+
return KeyNow.instance.time_now.strftime "%j"
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
# [yymo_mmm] returns an amalgam of
|
128
|
+
#
|
129
|
+
# => the two-digit year
|
130
|
+
# => the two-digit month index (starting at 01)
|
131
|
+
# => a period (separator)
|
132
|
+
# => the abbreviated month name
|
133
|
+
#
|
134
|
+
# @example
|
135
|
+
# => 1908.aug
|
136
|
+
# => for August 2019
|
137
|
+
#
|
138
|
+
def self.yymo_mmm
|
139
|
+
return "#{yy}#{mo}.#{mmm}"
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
#
|
144
|
+
# Given two integer parameters (month index and 4 digit year) representing
|
145
|
+
# the month in question this method returns the [PREVIOUS MONTHS] character
|
146
|
+
# amalgam in the format [yymo_mmm] where
|
147
|
+
#
|
148
|
+
# => yy | previous month's two-digit year
|
149
|
+
# => mo | previous month's two-digit month index
|
150
|
+
# => . | a period (separator)
|
151
|
+
# => mmm | previous month's abbreviated month name
|
152
|
+
#
|
153
|
+
# -------------------
|
154
|
+
# Example 1 (Simple)
|
155
|
+
# -------------------
|
156
|
+
#
|
157
|
+
# returns char => 1907.jul
|
158
|
+
# 4 parameters => 8, 2019
|
159
|
+
# representing => August, 2019
|
160
|
+
#
|
161
|
+
# ----------------------
|
162
|
+
# Example 2 (Last Year)
|
163
|
+
# ----------------------
|
164
|
+
#
|
165
|
+
# returns char => 1812.dec
|
166
|
+
# 4 parameters => 1, 2019
|
167
|
+
# representing => January, 2019
|
168
|
+
#
|
169
|
+
def self.previous_month_chars this_month_index, this_4digit_year
|
170
|
+
|
171
|
+
prev_month_index = this_month_index == 1 ? 12 : ( this_month_index - 1 )
|
172
|
+
prev_2dig_mn_pad = sprintf '%02d', prev_month_index
|
173
|
+
prev_4digit_year = this_month_index == 1 ? ( this_4digit_year - 1 ) : this_4digit_year
|
174
|
+
prev_twodigit_yr = "#{prev_4digit_year.to_s}"[2..-1]
|
175
|
+
prev_months_name = Date::ABBR_MONTHNAMES[prev_month_index].downcase
|
176
|
+
|
177
|
+
return "#{prev_twodigit_yr}#{prev_2dig_mn_pad}.#{prev_months_name}"
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
#
|
182
|
+
# Using the current class time this method returns
|
183
|
+
# the character amalgam for the [PREVIOUS MONTH] in
|
184
|
+
# the format [yymo_mmm] where
|
185
|
+
#
|
186
|
+
# => yy | last month's two-digit year
|
187
|
+
# => mo | last month's two-digit month index
|
188
|
+
# => . | a period (separator)
|
189
|
+
# => mmm | last month's abbreviated month name
|
190
|
+
#
|
191
|
+
# -------------------
|
192
|
+
# Example 1 (Simple)
|
193
|
+
# -------------------
|
194
|
+
#
|
195
|
+
# returns => 1907.jul
|
196
|
+
# if this month is => August 2019
|
197
|
+
#
|
198
|
+
# ----------------------
|
199
|
+
# Example 2 (Last Year)
|
200
|
+
# ----------------------
|
201
|
+
#
|
202
|
+
# returns => 1812.dec
|
203
|
+
# if this month is => January 2019
|
204
|
+
#
|
205
|
+
def self.yymo_mmm_prev
|
206
|
+
return previous_month_chars mo.to_i, yyyy.to_i
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
# Return 5 digit amalgam of year and julian day.
|
211
|
+
# eg [19003] for [January 3rd 2019]
|
212
|
+
def self.yyjjj
|
213
|
+
return "#{yy}#{jjj}"
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
# Return the 4 digit amalgam of the hour and minute
|
218
|
+
# using the 24 hour clock.
|
219
|
+
#
|
220
|
+
# @example
|
221
|
+
# => 1525
|
222
|
+
# => 03:25 pm
|
223
|
+
#
|
224
|
+
def self.hhmm
|
225
|
+
return "#{hh}#{mm}"
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
|
+
#
|
230
|
+
# Return the time of day to a TENTH of a second accuracy.
|
231
|
+
# [8] characters will always be returned with the 5th one
|
232
|
+
# being the (period) separator.
|
233
|
+
#
|
234
|
+
# The first (separated) segment delivers a hhmm 24 hour
|
235
|
+
# clock representation of the stamped time.
|
236
|
+
#
|
237
|
+
# The 3 digits of the second segment comprise of
|
238
|
+
#
|
239
|
+
# second of minute => 2 digits | [00] to [59]
|
240
|
+
# tenth of second => 1 digit from [0] to [9]
|
241
|
+
#
|
242
|
+
# @example
|
243
|
+
# => The time at the 562nd millisecond of the 49th
|
244
|
+
# second of the 23rd minute of the 17th hour of
|
245
|
+
# the day ( 17:23:49.562 )
|
246
|
+
#
|
247
|
+
# => 8 chars
|
248
|
+
# => 1723.495
|
249
|
+
#
|
250
|
+
def self.hhmm_sst
|
251
|
+
return "#{hhmm}.#{sst}"
|
252
|
+
end
|
253
|
+
|
254
|
+
|
255
|
+
# Return a string timestampt that is a period separated
|
256
|
+
# amalgam of the 2 digit year, 3 digit julian day, 2 digit
|
257
|
+
# hour, 2 digit minute, 2 digit second and 1 digit rounded
|
258
|
+
# down tenth of second.
|
259
|
+
#
|
260
|
+
# @example
|
261
|
+
# => 19003.1025
|
262
|
+
# => 10:25 am on January 3rd 2019
|
263
|
+
#
|
264
|
+
#
|
265
|
+
# Return the time of day to a TENTH of a second accuracy.
|
266
|
+
# [8] characters will always be returned with the 5th one
|
267
|
+
# being the (period) separator.
|
268
|
+
#
|
269
|
+
# The first (separated) segment delivers a hhmm 24 hour
|
270
|
+
# clock representation of the stamped time.
|
271
|
+
#
|
272
|
+
# The 3 digits of the second segment comprise of
|
273
|
+
#
|
274
|
+
# - second of minute => 2 digits | [00] to [59]
|
275
|
+
# - tenth of second => 1 digit from [0] to [9]
|
276
|
+
#
|
277
|
+
# @example
|
278
|
+
# => The time at the 562nd millisecond of the 49th
|
279
|
+
# second of the 23rd minute of the 17th hour of
|
280
|
+
# the day ( 17:23:49.562 )
|
281
|
+
#
|
282
|
+
# => 8 chars
|
283
|
+
# => 1723.495
|
284
|
+
#
|
285
|
+
def self.yyjjj_hhmm_sst
|
286
|
+
return "#{yyjjj}.#{hhmm}.#{sst}"
|
287
|
+
end
|
288
|
+
|
289
|
+
|
290
|
+
# Return a string timestampt that is a period separated
|
291
|
+
# amalgam of the 2 digit year, 3 digit julian day, 2 digit
|
292
|
+
# hour, 2 digit minute, 2 digit second and <b>9 digit</b>
|
293
|
+
# nanosecond.
|
294
|
+
#
|
295
|
+
# @example
|
296
|
+
# return => 19003.1725.42.836592034
|
297
|
+
# 4 time => 17:25:42 am on January 3rd 2019
|
298
|
+
#
|
299
|
+
# As per the above example, the time returned
|
300
|
+
#
|
301
|
+
# - is the 836592034 <b>nanosecond</b>
|
302
|
+
# - of the 42nd <b>second</b>
|
303
|
+
# - of the 25th <b>minute</b>
|
304
|
+
# - of the 17th <b>hour</b>
|
305
|
+
# - of the 3rd <b>day</b>
|
306
|
+
# - of the 20th <b>year</b>
|
307
|
+
# - of the 21st <b>century</b>
|
308
|
+
#
|
309
|
+
# @return [String]
|
310
|
+
# Return the time of day to nanosecond accuracy.
|
311
|
+
# 23 characters are always returned with three (3) period
|
312
|
+
# separators at the 6th, 11th and 14th positions.
|
313
|
+
def self.yyjjj_hhmm_ss_nanosec
|
314
|
+
nanosec_str = KeyNow.instance.time_now.strftime "%9N"
|
315
|
+
return "#{yyjjj}.#{hhmm}.#{ss}.#{nanosec_str}"
|
316
|
+
end
|
317
|
+
|
318
|
+
|
319
|
+
# Fetch the double barreled time stamp that is an amalgam of
|
320
|
+
# the human readable time now and a machine time representation
|
321
|
+
# from the moment this class was initialized.
|
322
|
+
#
|
323
|
+
# See the {yyjjj_hhmm_ss_nanosec} method for documentation of
|
324
|
+
# the nanosecond accurate time stamp.
|
325
|
+
#
|
326
|
+
# @return [String]
|
327
|
+
# the double barreled time stamp containing a human readable
|
328
|
+
# (right this moment) time and a <b>class initialized time</b>
|
329
|
+
# representation with nanosecond accuracy.
|
330
|
+
def self.fetch
|
331
|
+
return "#{Time.now.ctime} ( #{yyjjj_hhmm_ss_nanosec} )"
|
332
|
+
end
|
333
|
+
|
334
|
+
|
335
|
+
# Grab the double barreled time stamp that is an amalgam of
|
336
|
+
# the human readable time now and a machine time representation
|
337
|
+
# from the moment this class was initialized.
|
338
|
+
#
|
339
|
+
# On Friday June the 8th at about 6:26 pm.
|
340
|
+
# Fri Jun 8 18:26:17 2018 ( 18159.1826.138 )
|
341
|
+
#
|
342
|
+
# See the {yyjjj_hhmm_sst} method for documentation of stamp
|
343
|
+
# that is accurate to the tenth of a second.
|
344
|
+
#
|
345
|
+
# @return [String]
|
346
|
+
# the double barreled time stamp containing a human readable
|
347
|
+
# (right this moment) time and a <b>class initialized time</b>
|
348
|
+
# representation with tenth of a second accuracy.
|
349
|
+
def self.grab
|
350
|
+
time_with_consecutive_spaces = Time.now.ctime
|
351
|
+
human_readable_str = time_with_consecutive_spaces.gsub( " ", " " )
|
352
|
+
return "#{human_readable_str} ( #{yyjjj_hhmm_sst} )"
|
353
|
+
end
|
354
|
+
|
355
|
+
|
356
|
+
# Return the Rubyfied time zone being used.
|
357
|
+
def self.zone
|
358
|
+
return KeyNow.instance.time_now.zone
|
359
|
+
end
|
360
|
+
|
361
|
+
|
362
|
+
# Log segments of time pertaining to the time stamp.
|
363
|
+
# @todo
|
364
|
+
# move method contents into test class
|
365
|
+
def self.log_instance_time
|
366
|
+
|
367
|
+
log.info(x) { "[stamp] -------------- => -------------------------------- #" }
|
368
|
+
log.info(x) { "[stamp] eco time stamp => [#{KeyNow.instance.time_now.ctime}]" }
|
369
|
+
log.info(x) { "[stamp] -------------- => -------------------------------- #" }
|
370
|
+
log.info(x) { "[stamp] Univ Time Zone => #{zone}" }
|
371
|
+
log.info(x) { "[stamp] Month Index is => #{mo}" }
|
372
|
+
log.info(x) { "[stamp] Month Name is => #{mmm}" }
|
373
|
+
log.info(x) { "[stamp] Day Of Week is => #{ddd}" }
|
374
|
+
log.info(x) { "[stamp] -------------- => -------------------------------- #" }
|
375
|
+
log.info(x) { "[stamp] Two Digit Year => #{yy}" }
|
376
|
+
log.info(x) { "[stamp] Julian Cal Day => #{jjj}" }
|
377
|
+
log.info(x) { "[stamp] Yr and Jul Day => #{yyjjj}" }
|
378
|
+
log.info(x) { "[stamp] Hour of Theday => #{hh}" }
|
379
|
+
log.info(x) { "[stamp] Minute of Hour => #{mm}" }
|
380
|
+
log.info(x) { "[stamp] Hour + Minute => #{hhmm}" }
|
381
|
+
log.info(x) { "[stamp] Second of Min => #{ss}" }
|
382
|
+
log.info(x) { "[stamp] 600 Min Slices => #{sst}" }
|
383
|
+
log.info(x) { "[stamp] -------------- => -------------------------------- #" }
|
384
|
+
log.info(x) { "[stamp] The Time Stamp => #{yyjjj_hhmm_sst}" }
|
385
|
+
log.info(x) { "[stamp] -------------- => -------------------------------- #" }
|
386
|
+
|
387
|
+
end
|
388
|
+
|
389
|
+
|
390
|
+
# This singleton (one instance) class sets the time just once.
|
391
|
+
def initialize
|
392
|
+
@time_now = Time.now;
|
393
|
+
end
|
394
|
+
|
395
|
+
|
396
|
+
KeyNow.log_instance_time
|
397
|
+
|
398
|
+
|
399
|
+
end
|
400
|
+
|
401
|
+
|
402
|
+
end
|