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.
Files changed (90) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.yardopts +3 -0
  4. data/Gemfile +10 -0
  5. data/LICENSE +21 -0
  6. data/README.md +793 -0
  7. data/Rakefile +16 -0
  8. data/bin/safe +5 -0
  9. data/lib/configs/README.md +58 -0
  10. data/lib/extension/array.rb +162 -0
  11. data/lib/extension/dir.rb +35 -0
  12. data/lib/extension/file.rb +123 -0
  13. data/lib/extension/hash.rb +33 -0
  14. data/lib/extension/string.rb +572 -0
  15. data/lib/factbase/facts.safedb.net.ini +38 -0
  16. data/lib/interprete.rb +462 -0
  17. data/lib/keytools/PRODUCE_RAND_SEQ_USING_DEV_URANDOM.txt +0 -0
  18. data/lib/keytools/kdf.api.rb +243 -0
  19. data/lib/keytools/kdf.bcrypt.rb +265 -0
  20. data/lib/keytools/kdf.pbkdf2.rb +262 -0
  21. data/lib/keytools/kdf.scrypt.rb +190 -0
  22. data/lib/keytools/key.64.rb +326 -0
  23. data/lib/keytools/key.algo.rb +109 -0
  24. data/lib/keytools/key.api.rb +1391 -0
  25. data/lib/keytools/key.db.rb +330 -0
  26. data/lib/keytools/key.docs.rb +195 -0
  27. data/lib/keytools/key.error.rb +110 -0
  28. data/lib/keytools/key.id.rb +271 -0
  29. data/lib/keytools/key.ident.rb +243 -0
  30. data/lib/keytools/key.iv.rb +107 -0
  31. data/lib/keytools/key.local.rb +259 -0
  32. data/lib/keytools/key.now.rb +402 -0
  33. data/lib/keytools/key.pair.rb +259 -0
  34. data/lib/keytools/key.pass.rb +120 -0
  35. data/lib/keytools/key.rb +585 -0
  36. data/lib/logging/gem.logging.rb +132 -0
  37. data/lib/modules/README.md +43 -0
  38. data/lib/modules/cryptology/aes-256.rb +154 -0
  39. data/lib/modules/cryptology/amalgam.rb +70 -0
  40. data/lib/modules/cryptology/blowfish.rb +130 -0
  41. data/lib/modules/cryptology/cipher.rb +207 -0
  42. data/lib/modules/cryptology/collect.rb +138 -0
  43. data/lib/modules/cryptology/crypt.io.rb +225 -0
  44. data/lib/modules/cryptology/engineer.rb +99 -0
  45. data/lib/modules/mappers/dictionary.rb +288 -0
  46. data/lib/modules/storage/coldstore.rb +186 -0
  47. data/lib/modules/storage/git.store.rb +399 -0
  48. data/lib/session/fact.finder.rb +334 -0
  49. data/lib/session/require.gem.rb +112 -0
  50. data/lib/session/time.stamp.rb +340 -0
  51. data/lib/session/user.home.rb +49 -0
  52. data/lib/usecase/cmd.rb +487 -0
  53. data/lib/usecase/config/README.md +57 -0
  54. data/lib/usecase/docker/README.md +146 -0
  55. data/lib/usecase/docker/docker.rb +49 -0
  56. data/lib/usecase/edit/README.md +43 -0
  57. data/lib/usecase/edit/delete.rb +46 -0
  58. data/lib/usecase/export.rb +40 -0
  59. data/lib/usecase/files/README.md +37 -0
  60. data/lib/usecase/files/eject.rb +56 -0
  61. data/lib/usecase/files/file_me.rb +78 -0
  62. data/lib/usecase/files/read.rb +169 -0
  63. data/lib/usecase/files/write.rb +89 -0
  64. data/lib/usecase/goto.rb +57 -0
  65. data/lib/usecase/id.rb +36 -0
  66. data/lib/usecase/import.rb +157 -0
  67. data/lib/usecase/init.rb +63 -0
  68. data/lib/usecase/jenkins/README.md +146 -0
  69. data/lib/usecase/jenkins/jenkins.rb +208 -0
  70. data/lib/usecase/login.rb +71 -0
  71. data/lib/usecase/logout.rb +28 -0
  72. data/lib/usecase/open.rb +71 -0
  73. data/lib/usecase/print.rb +40 -0
  74. data/lib/usecase/put.rb +81 -0
  75. data/lib/usecase/set.rb +44 -0
  76. data/lib/usecase/show.rb +138 -0
  77. data/lib/usecase/terraform/README.md +91 -0
  78. data/lib/usecase/terraform/terraform.rb +121 -0
  79. data/lib/usecase/token.rb +35 -0
  80. data/lib/usecase/update/README.md +55 -0
  81. data/lib/usecase/update/rename.rb +180 -0
  82. data/lib/usecase/use.rb +41 -0
  83. data/lib/usecase/verse.rb +20 -0
  84. data/lib/usecase/view.rb +71 -0
  85. data/lib/usecase/vpn/README.md +150 -0
  86. data/lib/usecase/vpn/vpn.ini +31 -0
  87. data/lib/usecase/vpn/vpn.rb +54 -0
  88. data/lib/version.rb +3 -0
  89. data/safedb.gemspec +34 -0
  90. 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