clir 0.22.0
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/.travis.yml +6 -0
- data/CHANGELOG.md +229 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +44 -0
- data/Manual/Manuel_fr.md +222 -0
- data/Manual/Manuel_fr.pdf +0 -0
- data/README.md +63 -0
- data/REFLEXIONS.md +8 -0
- data/Rakefile +10 -0
- data/TODO.md +9 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/clir.gemspec +40 -0
- data/lib/clir/Array.ext.rb +9 -0
- data/lib/clir/CLI.mod.rb +237 -0
- data/lib/clir/CSV_extension.rb +177 -0
- data/lib/clir/Config.cls.rb +25 -0
- data/lib/clir/Date_utils.rb +161 -0
- data/lib/clir/File_extension.rb +127 -0
- data/lib/clir/Integer.ext.rb +43 -0
- data/lib/clir/Labelizor.rb +231 -0
- data/lib/clir/Replayer.cls.rb +90 -0
- data/lib/clir/String.ext.rb +304 -0
- data/lib/clir/Symbol.ext.rb +20 -0
- data/lib/clir/TTY-Prompt.cls.rb +415 -0
- data/lib/clir/Table.rb +369 -0
- data/lib/clir/console_methods.rb +42 -0
- data/lib/clir/helpers_methods.rb +68 -0
- data/lib/clir/state_methods.rb +48 -0
- data/lib/clir/utils_methods.rb +57 -0
- data/lib/clir/utils_numbers.rb +50 -0
- data/lib/clir/version.rb +3 -0
- data/lib/clir.rb +36 -0
- metadata +136 -0
data/lib/clir/CLI.mod.rb
ADDED
@@ -0,0 +1,237 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
A command line is composed by:
|
4
|
+
|
5
|
+
- app command (for example: 'rake')
|
6
|
+
- main command (main_command): firts element not leading with '-'
|
7
|
+
dans without '=' (for example: 'build' in 'rake build')
|
8
|
+
- options: elements that lead with '-' or '--'
|
9
|
+
- params : elments that contain '=' (key=value pairs)
|
10
|
+
- components: all other elements
|
11
|
+
|
12
|
+
We can get this elments with:
|
13
|
+
|
14
|
+
CLI.main_command
|
15
|
+
CLI.options[:<key>]
|
16
|
+
CLI.option(:key)
|
17
|
+
CLI.params[:<key>]
|
18
|
+
CLI.components[<index>]
|
19
|
+
|
20
|
+
App can define its own short options (to long options) with:
|
21
|
+
|
22
|
+
CLI.set_options_table({short: long, short: long...})
|
23
|
+
|
24
|
+
Note : il faut obligatoirement mettre la version courte (souvent
|
25
|
+
une seule lettre) en Symbol :
|
26
|
+
CLI.set_options_table({e: :edition})
|
27
|
+
|
28
|
+
|
29
|
+
=end
|
30
|
+
module CLI
|
31
|
+
MARKER_TESTS_FILE = File.expand_path(File.join('.','.MARKER_TESTS'))
|
32
|
+
class << self
|
33
|
+
|
34
|
+
##
|
35
|
+
# First class method
|
36
|
+
# (call it at start-up)
|
37
|
+
#
|
38
|
+
def init
|
39
|
+
parse(ARGV)
|
40
|
+
Q.init if Q.respond_to?(:init)
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# @return command line options
|
45
|
+
# @options is set while parse method. If it is not set, we
|
46
|
+
# CLI.init first.
|
47
|
+
#
|
48
|
+
def options
|
49
|
+
defined?(@options) || self.init
|
50
|
+
@options
|
51
|
+
end
|
52
|
+
|
53
|
+
# @return option of key +key+
|
54
|
+
def option(key)
|
55
|
+
options[key.to_sym]
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# @main command (after command name)
|
60
|
+
def main_command
|
61
|
+
defined?(@main_command) || self.init
|
62
|
+
@main_command
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# @return command line parameters
|
67
|
+
#
|
68
|
+
def params
|
69
|
+
defined?(@params) || self.init
|
70
|
+
@params
|
71
|
+
end
|
72
|
+
|
73
|
+
def param(key)
|
74
|
+
@params[key.to_sym]
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# @return command line components
|
79
|
+
# 'components' are elements of command line that are not options
|
80
|
+
# (with leading '-'), that are not parameters (key=value paire) and
|
81
|
+
# that are not main_command
|
82
|
+
#
|
83
|
+
def components
|
84
|
+
defined?(@components) || self.init
|
85
|
+
@components
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
##
|
90
|
+
# Command name
|
91
|
+
#
|
92
|
+
# Don't confuse with 'main command' which is the very first
|
93
|
+
# argument in command line
|
94
|
+
#
|
95
|
+
def command_name
|
96
|
+
@command_name ||= begin
|
97
|
+
File.basename($PROGRAM_NAME,File.extname($PROGRAM_NAME))
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
##
|
102
|
+
# Main method which parse command line to get:
|
103
|
+
# - main command
|
104
|
+
# - options (leadings with -/--)
|
105
|
+
# - parameters (key=value pairs)
|
106
|
+
#
|
107
|
+
def parse(argv)
|
108
|
+
argv = argv.split(' ') if argv.is_a?(String)
|
109
|
+
if replay_it?(argv)
|
110
|
+
#
|
111
|
+
# Replay last command (if unable)
|
112
|
+
#
|
113
|
+
puts "Je dois apprendre à replayer la commande précédente".jaune
|
114
|
+
puts "Pour ça, je dois enregistrer les inputs précédents.".jaune
|
115
|
+
else
|
116
|
+
#
|
117
|
+
# Regular run
|
118
|
+
#
|
119
|
+
reset
|
120
|
+
@raw_command_line = ([command_name]+argv).join(' ')
|
121
|
+
argv.each do |arg|
|
122
|
+
if arg.start_with?('--')
|
123
|
+
arg, val = key_and_value_in(arg[2..-1])
|
124
|
+
@options.merge!(arg.to_sym => val)
|
125
|
+
elsif arg.start_with?('-')
|
126
|
+
arg, val = key_and_value_in(arg[1..-1])
|
127
|
+
arg = long_option_for(arg)
|
128
|
+
@options.merge!(arg.to_sym => val)
|
129
|
+
elsif arg.match?('.=.')
|
130
|
+
key, val = key_and_value_in(arg)
|
131
|
+
@params.merge!(key.to_sym => val)
|
132
|
+
elsif @main_command.nil?
|
133
|
+
@main_command = arg
|
134
|
+
else
|
135
|
+
@components << arg
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def set_options_table(table)
|
142
|
+
@_app_options_table = table
|
143
|
+
end
|
144
|
+
|
145
|
+
##
|
146
|
+
# For Replayer, return data
|
147
|
+
def get_command_line_data
|
148
|
+
{
|
149
|
+
raw_command_line: @raw_command_line,
|
150
|
+
command_name: command_name,
|
151
|
+
main_command: main_command,
|
152
|
+
components: components,
|
153
|
+
options: options,
|
154
|
+
params: params,
|
155
|
+
table_short2long_options: table_short2long_options
|
156
|
+
}
|
157
|
+
end
|
158
|
+
|
159
|
+
def set_command_line_data(data)
|
160
|
+
data.each do |k, v| instance_variable_set("@#{k}", v) end
|
161
|
+
end
|
162
|
+
|
163
|
+
# --- Tests Methods ---
|
164
|
+
|
165
|
+
def set_tests_on_with_marker
|
166
|
+
File.write(MARKER_TESTS_FILE, "#{Time.now}")
|
167
|
+
end
|
168
|
+
|
169
|
+
def unset_tests_on_with_marker
|
170
|
+
if File.exist?(MARKER_TESTS_FILE)
|
171
|
+
File.delete(MARKER_TESTS_FILE)
|
172
|
+
else
|
173
|
+
puts "Weirdly, the test marker file (CLI::MARKER_TESTS_FILE) doesn't exist…".rouge
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
|
178
|
+
private
|
179
|
+
|
180
|
+
def reset
|
181
|
+
@main_command = nil
|
182
|
+
@components = []
|
183
|
+
@options = {}
|
184
|
+
@params = {}
|
185
|
+
Clir::State.reset
|
186
|
+
CLI::Replayer.init_for_recording
|
187
|
+
@table_short2long_options = nil
|
188
|
+
end
|
189
|
+
|
190
|
+
##
|
191
|
+
# @return the long option for +short+
|
192
|
+
#
|
193
|
+
def long_option_for(short)
|
194
|
+
short = short.to_sym
|
195
|
+
if table_short2long_options.key?(short)
|
196
|
+
table_short2long_options[short]
|
197
|
+
else
|
198
|
+
short
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
##
|
203
|
+
# @return conversion table from short option (pe 'v') to
|
204
|
+
# long option (pe 'verbose').
|
205
|
+
# App can define its own table in CLI.set_options_table
|
206
|
+
def table_short2long_options
|
207
|
+
@table_short2long_options ||= begin
|
208
|
+
{
|
209
|
+
:h => :help,
|
210
|
+
:q => :quiet,
|
211
|
+
:v => :verbose,
|
212
|
+
:x => :debug,
|
213
|
+
}.merge(app_options_table)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
##
|
218
|
+
# @return the app table of options conversions
|
219
|
+
def app_options_table
|
220
|
+
@_app_options_table ||= {}
|
221
|
+
end
|
222
|
+
|
223
|
+
##
|
224
|
+
# @return [key, value] in +foo+ if +foo+ contains '='
|
225
|
+
# [foo, default] otherwise
|
226
|
+
def key_and_value_in(foo, default = true)
|
227
|
+
foo.match?('=') ? foo.split('=') : [foo, default]
|
228
|
+
end
|
229
|
+
|
230
|
+
##
|
231
|
+
# @return TRUE if replay character is used and only
|
232
|
+
# replay character
|
233
|
+
def replay_it?(argv)
|
234
|
+
argv.count == 1 && argv[0] == ::Config[:replay_character]
|
235
|
+
end
|
236
|
+
end #/<< self CLI
|
237
|
+
end #/module CLI
|
@@ -0,0 +1,177 @@
|
|
1
|
+
require 'csv'
|
2
|
+
class CSV
|
3
|
+
class << self
|
4
|
+
|
5
|
+
# Pour pouvoir tester l'entête dans les tests
|
6
|
+
attr_reader :headers_for_test
|
7
|
+
|
8
|
+
# Lecture d'un fichier CSV à partir de la fin
|
9
|
+
#
|
10
|
+
# Les arguments sont les mêmes que pour readlines
|
11
|
+
#
|
12
|
+
# Pour rappel :
|
13
|
+
# +options+ peut contenir
|
14
|
+
# :headers À true, on tient compte des entêtes et on
|
15
|
+
# retourne des CSV::Row. Sinon, on retourne une
|
16
|
+
# Array simple.
|
17
|
+
# :headers_converters
|
18
|
+
# Convertisseur pour les noms des colonnes.
|
19
|
+
# :downcase ou :symbol
|
20
|
+
# :converters
|
21
|
+
# Liste de convertisseurs pour les données.
|
22
|
+
# Principalement :numeric, :date
|
23
|
+
# :col_sep
|
24
|
+
# Séparateur de colonne. Par défaut une virgule.
|
25
|
+
#
|
26
|
+
def readlines_backward(path, **options, &block)
|
27
|
+
|
28
|
+
options ||= {}
|
29
|
+
|
30
|
+
file = File.new(path)
|
31
|
+
size = File.size(file)
|
32
|
+
|
33
|
+
if size < ( 1 << 16 )
|
34
|
+
return readlines_backward_in_small_file(path, **options, &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Si options[:headers] est true, il faut récupérer la première
|
39
|
+
# ligne et la transformer en entête
|
40
|
+
#
|
41
|
+
if options[:headers]
|
42
|
+
begin
|
43
|
+
fileh = File.open(path,'r')
|
44
|
+
header = fileh.gets.chomp
|
45
|
+
ensure
|
46
|
+
fileh.close
|
47
|
+
end
|
48
|
+
table = CSV.parse(header, **options)
|
49
|
+
headers = table.headers
|
50
|
+
@headers_for_test = headers # pour les tests
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
if block_given?
|
55
|
+
#
|
56
|
+
# Les options pour CSV.parse
|
57
|
+
# On garde seulement les convertisseurs de données et on met
|
58
|
+
# toujours headers à false (puisque c'est seulement la ligne
|
59
|
+
# de données qui sera parser)
|
60
|
+
#
|
61
|
+
# line_csv_options = {headers:false, converters: options[:converters]}
|
62
|
+
line_csv_options = options
|
63
|
+
#
|
64
|
+
# Avec un bloc fourni, on va lire ligne par ligne en partant
|
65
|
+
# de la fin.
|
66
|
+
#
|
67
|
+
buffer_size = 10000 # disons que c'est la longueur maximale d'une ligne
|
68
|
+
|
69
|
+
#
|
70
|
+
# On se positionne de la fin - la longueur du tampo dans le
|
71
|
+
# fichier à lire
|
72
|
+
#
|
73
|
+
file.seek(-buffer_size, IO::SEEK_END)
|
74
|
+
#
|
75
|
+
# Le tampon courant (il contient tout jusqu'à ce qu'il y ait
|
76
|
+
# au moins une ligne)
|
77
|
+
#
|
78
|
+
buffer = ""
|
79
|
+
#
|
80
|
+
# On boucle tant qu'on n'interrompt pas (pour le moment)
|
81
|
+
#
|
82
|
+
# QUESTIONS
|
83
|
+
# 1. Comment repère-t-on la fin ? En comparant la position
|
84
|
+
# actuelle du pointeur avec 0 ?
|
85
|
+
# 2. Comment met-on fin à la recherche (c'est presque la
|
86
|
+
# même question)
|
87
|
+
while true
|
88
|
+
#
|
89
|
+
# On lit la longueur du tampon en l'ajoutant à ce qu'on a
|
90
|
+
# déjà lu ou ce qui reste.
|
91
|
+
# Celui pourra contenir une ou plusieurs lignes, la première
|
92
|
+
# pourra être tronquée
|
93
|
+
#
|
94
|
+
buffer = file.read(buffer_size) + buffer
|
95
|
+
#
|
96
|
+
# Nombre de lignes
|
97
|
+
# (utile ?)
|
98
|
+
nombre_lignes = buffer.count("\n")
|
99
|
+
#
|
100
|
+
# On traite les lignes du buffer (en gardant ce qui dépasse)
|
101
|
+
#
|
102
|
+
if nombre_lignes > 0
|
103
|
+
# puts "Position dans le fichier : #{file.pos}".bleu
|
104
|
+
# puts "Nombre de lignes : #{nombre_lignes}".bleu
|
105
|
+
lines = buffer.split("\n").reverse
|
106
|
+
#
|
107
|
+
# On laisse la dernière ligne, certainement incomplète, dans
|
108
|
+
# le tampon. Elle sera ajoutée à la fin de ce qui précède
|
109
|
+
#
|
110
|
+
buffer = lines.pop
|
111
|
+
#
|
112
|
+
# Boucle sur les lignes
|
113
|
+
#
|
114
|
+
# @note : un break, dans le &block, interrompra la boucle
|
115
|
+
#
|
116
|
+
lines.each do |line|
|
117
|
+
line = line.chomp
|
118
|
+
line = "#{header}\n#{line}\n" if options[:headers]
|
119
|
+
# puts "line parsée : #{line.inspect}".bleu
|
120
|
+
line_csv = CSV.parse(line, **line_csv_options)
|
121
|
+
# puts "line_csv: #{line_csv.inspect}::#{line_csv.class}".orange
|
122
|
+
yield line_csv[0]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
#
|
126
|
+
# On remonte de deux fois la longueur du tampon. Une fois pour
|
127
|
+
# revenir au point de départ, une fois pour remonter à la
|
128
|
+
# portion suivante, à partir de la position courante, évide-
|
129
|
+
# ment
|
130
|
+
#
|
131
|
+
new_pos = file.pos - 2 * buffer_size
|
132
|
+
if new_pos < 0
|
133
|
+
file.seek(new_pos)
|
134
|
+
else
|
135
|
+
file.seek(- 2 * buffer_size, IO::SEEK_CUR)
|
136
|
+
end
|
137
|
+
#
|
138
|
+
# Si on se trouve à 0, on doit s'arrêter
|
139
|
+
#
|
140
|
+
break if file.pos <= 0
|
141
|
+
# puts "Nouvelle position dans le fichier : #{file.pos}".bleu
|
142
|
+
end
|
143
|
+
else
|
144
|
+
#
|
145
|
+
# Sans bloc fourni, on renvoie tout le code du fichier
|
146
|
+
#
|
147
|
+
# À vos risques et périls
|
148
|
+
# self.readlines(path, **options).to_a.reverse
|
149
|
+
self.foreach(path, **options).reverse
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
alias :readlines_backwards :readlines_backward
|
154
|
+
alias :foreach_backward :readlines_backward
|
155
|
+
alias :foreach_backwards :readlines_backward
|
156
|
+
|
157
|
+
# Lecture à l'envers dans un petit fichier
|
158
|
+
#
|
159
|
+
def readlines_backward_in_small_file(path, **options, &block)
|
160
|
+
if block_given?
|
161
|
+
self.foreach(path, **options).reverse_each do |row|
|
162
|
+
yield row
|
163
|
+
end
|
164
|
+
# liste2reverse = []
|
165
|
+
# self.readlines(path, **options).each { |row| liste2reverse << row }
|
166
|
+
# liste2reverse.reverse.each do |row|
|
167
|
+
# yield row
|
168
|
+
# end
|
169
|
+
else
|
170
|
+
# Lecture toute simple de la table
|
171
|
+
# return self.readlines(path, **options).to_a.reverse
|
172
|
+
return self.foreach(path, **options).reverse
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
end #/<< self class CSV
|
177
|
+
end #/class CSV
|
@@ -0,0 +1,25 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
Pour le moment, on met la configuration comme ça, en dur, mais
|
4
|
+
plus tard on pourra la modifier pour chaque application.
|
5
|
+
|
6
|
+
=end
|
7
|
+
module Clir
|
8
|
+
class Configuration
|
9
|
+
|
10
|
+
def [](key)
|
11
|
+
data[key]
|
12
|
+
end
|
13
|
+
|
14
|
+
# TODO
|
15
|
+
def data
|
16
|
+
{
|
17
|
+
replay_character: '_'
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
end #/class Configuration
|
22
|
+
end #/module CLIR
|
23
|
+
|
24
|
+
# Expose outside
|
25
|
+
Config = Clir::Configuration.new
|
@@ -0,0 +1,161 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
Usefull methods for date & time
|
4
|
+
|
5
|
+
@author: Philippe Perret <philippe.perret@yahoo.fr>
|
6
|
+
|
7
|
+
=end
|
8
|
+
require 'date'
|
9
|
+
|
10
|
+
MOIS = {
|
11
|
+
1 => {court: 'jan', long: 'janvier'},
|
12
|
+
2 => {court: 'fév', long: 'février'},
|
13
|
+
3 => {court: 'mars', long: 'mars'},
|
14
|
+
4 => {court: 'avr', long: 'avril'},
|
15
|
+
5 => {court: 'mai', long: 'mai'},
|
16
|
+
6 => {court: 'juin', long: 'juin'},
|
17
|
+
7 => {court: 'juil', long: 'juillet'},
|
18
|
+
8 => {court: 'aout', long: 'aout'},
|
19
|
+
9 => {court: 'sept', long: 'septembre'},
|
20
|
+
10 => {court: 'oct', long: 'octobre'},
|
21
|
+
11 => {court: 'nov', long: 'novembre'},
|
22
|
+
12 => {court: 'déc', long: 'décembre'}
|
23
|
+
}
|
24
|
+
|
25
|
+
DAYNAMES = [
|
26
|
+
'Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'
|
27
|
+
]
|
28
|
+
|
29
|
+
|
30
|
+
# @return [String] Une date formatée avec le moins verbal
|
31
|
+
#
|
32
|
+
# @param [Time|Date|NIl] La date. Si non fournie, on prend maintenant
|
33
|
+
# @param [Hash] options Les options de formatage
|
34
|
+
# @option lenght [Symbol] :court (pour le mois court)
|
35
|
+
def human_date(ladate = nil, **options)
|
36
|
+
ladate ||= Time.now
|
37
|
+
options.key?(:length) || options.merge!(length: :long)
|
38
|
+
lemois = MOIS[ladate.month][options[:length]]
|
39
|
+
lemois = "#{lemois}." if options[:length] == :court
|
40
|
+
"#{ladate.day} #{lemois} #{ladate.year}"
|
41
|
+
end
|
42
|
+
alias :date_humaine :human_date
|
43
|
+
|
44
|
+
|
45
|
+
# @return A date for a file, now
|
46
|
+
# @example
|
47
|
+
# date_for_file # => "2022-12-14"
|
48
|
+
# date_for_file(nil, true) # => "2022-12-14-23-11"
|
49
|
+
def date_for_file(time = nil, with_hour = false, del = '-')
|
50
|
+
time ||= Time.now
|
51
|
+
fmt = "%Y#{del}%m#{del}%d"
|
52
|
+
fmt = "#{fmt}#{del}%H#{del}%M" if with_hour
|
53
|
+
time.strftime(fmt)
|
54
|
+
end
|
55
|
+
|
56
|
+
# @reçoit une date et la retourne sous la forme "YYYY-MM-DD"
|
57
|
+
def ymd(time = nil, delimitor = '-')
|
58
|
+
time ||= Time.now
|
59
|
+
time.strftime("%Y#{delimitor}%m#{delimitor}%d")
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
# @return Date corresponding to +foo+
|
64
|
+
# @param [String|Integer|Time|Date] foo
|
65
|
+
# - [Time] Return itself
|
66
|
+
# - [Date] Return itself.to_time
|
67
|
+
# - [String] JJ/MM/AAAA or 'YYYY/MM/DD'
|
68
|
+
# - [Integer] Number of seconds
|
69
|
+
# @note
|
70
|
+
# Alias :time_from (more semanticaly correct)
|
71
|
+
#
|
72
|
+
def date_from(foo)
|
73
|
+
case foo
|
74
|
+
when Time then foo
|
75
|
+
when Date then foo.to_time
|
76
|
+
when Integer then Time.at(foo)
|
77
|
+
when String
|
78
|
+
a, b, c = foo.split('/')
|
79
|
+
if c.length == 4
|
80
|
+
Time.new(c.to_i, b.to_i, a.to_i)
|
81
|
+
else
|
82
|
+
Time.new(a.to_i, b.to_i, c.to_i)
|
83
|
+
end
|
84
|
+
else
|
85
|
+
raise "Unable to transform #{foo.inspect} to Time."
|
86
|
+
end
|
87
|
+
end
|
88
|
+
alias :time_from :date_from
|
89
|
+
|
90
|
+
##
|
91
|
+
# Formate de date as JJ MM AAAA (or MM JJ AAAA in english)
|
92
|
+
# @param {Time} date
|
93
|
+
# @param [Hash] options table:
|
94
|
+
# @option options [Boolean] :verbal If true, the format will be "month the day-th etc."
|
95
|
+
# @option options [Boolean] :no_time If true, only day, without time
|
96
|
+
# @option options [Boolean] :seconds If true, add seconds with time
|
97
|
+
# @option options [Boolean] :update_format If true, the format is updated. Otherwise, the last format is used for all next date
|
98
|
+
# @option options [Boolean] :sentence If true, on met "le ... à ...."
|
99
|
+
#
|
100
|
+
def formate_date(date, options = nil)
|
101
|
+
options ||= {}
|
102
|
+
@last_format = nil if options[:update_format] || options[:template]
|
103
|
+
@last_format ||= begin
|
104
|
+
as_verbal = options[:verbal]||options[:sentence]
|
105
|
+
if options[:template]
|
106
|
+
options[:template]
|
107
|
+
else
|
108
|
+
fmt = []
|
109
|
+
fmt << 'le ' if options[:sentence]
|
110
|
+
if as_verbal
|
111
|
+
forday = date.day == 1 ? '1er' : '%-d'
|
112
|
+
fmt << "#{forday} #{MOIS[date.month][:long]} %Y"
|
113
|
+
else
|
114
|
+
fmt << '%d %m %Y'
|
115
|
+
end
|
116
|
+
delh = options[:sentence] ? 'à' : '-'
|
117
|
+
unless options[:no_time]
|
118
|
+
fmt << (as_verbal ? " à %H h %M" : " #{delh} %H:%M")
|
119
|
+
end
|
120
|
+
if options[:seconds]
|
121
|
+
fmt << (as_verbal ? ' mn et %S s' : ':%S' )
|
122
|
+
end
|
123
|
+
fmt.join('')
|
124
|
+
end
|
125
|
+
end
|
126
|
+
date.strftime(@last_format)
|
127
|
+
end
|
128
|
+
|
129
|
+
class Time
|
130
|
+
|
131
|
+
# @return [String] French date with separator
|
132
|
+
# @example
|
133
|
+
# Time.now.jj_mm_yyyy # => "28/12/2022"
|
134
|
+
# @param separator [String] Separator to use (default: '/')
|
135
|
+
def jj_mm_aaaa(separator = '/')
|
136
|
+
self.strftime(['%d','%m','%Y'].join(separator))
|
137
|
+
end
|
138
|
+
|
139
|
+
# @return [String] English date with separator
|
140
|
+
# @example
|
141
|
+
# Time.now.mm_dd_yyyy # => "12/28/2022"
|
142
|
+
#
|
143
|
+
# @param separator [String] Separator to use (default: '/')
|
144
|
+
def mm_dd_yyyy(separator = '/')
|
145
|
+
self.strftime(['%m','%d','%Y'].join(separator))
|
146
|
+
end
|
147
|
+
|
148
|
+
end #/class Time
|
149
|
+
|
150
|
+
class Integer
|
151
|
+
|
152
|
+
def ago
|
153
|
+
(Time.now - self).mm_dd_yyyy
|
154
|
+
end
|
155
|
+
|
156
|
+
end #/class Integer
|
157
|
+
|
158
|
+
def ilya(laps, options = nil)
|
159
|
+
options ||= {}
|
160
|
+
(Time.now - laps).jj_mm_aaaa(options[:separator] || '/')
|
161
|
+
end
|