util 0.2.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a411369612d59eb1c14f1a31b5d0a28bf8b5b4527851706e314ea096eee3aab2
4
- data.tar.gz: 65f6f5e7a8bebc5418ed98ed2844010525946398841875e9769ea300ac6f12dd
3
+ metadata.gz: ed3de69b80836c4c8ed1ebb36a12f64a0eb9fdfe93580d37e9c92adc5fa1f592
4
+ data.tar.gz: 2539c7681963982bf06c4c8d9dfa5a1b462b06aac849ed60d91fdd3ae5a3e260
5
5
  SHA512:
6
- metadata.gz: 417cc14f81425eae6d01052d6b0cb855e786ceb92610bc219a575413883810084fcf19492d8e2d23963aa04527ca36b0ec6fcf215f94262010fd4ef090e0575d
7
- data.tar.gz: 84443a3ab823ded668be55f4768ee18209d2d9449329db2e4aeabf1fa36fd61245819d0155183951d985cb6a92c35ee805b4fb253c571367e07312d3517a5fc0
6
+ metadata.gz: b9ab28083a354018b031d2f9cb3aa9b113cc8bb47d1a008afe077c90d330b8b2a642fcdd484d00a875029e4df4da72f16c4b8be6ea04480d97a5cc03c7e4c9c6
7
+ data.tar.gz: 12f674bd048dafdb573f8fbe24a4845b5c58b1f4251885f9f0ef2a794aa29f7fde7667f8a8a2f7604164e89b5a1781dfd7557045eca962745a7613811d1b797d
data/README.md CHANGED
@@ -7,20 +7,20 @@ Cette gemme regroupe un certain nombre de petits utilitaires visant à simplifie
7
7
 
8
8
  Sauf mention contraire, toutes les classes sont contenues dans le module principal `Util`. Se reporter à la documentation (en anglais) pour une explication plus détaillée du fonctionnement de chaque classe.
9
9
 
10
- ### → `Arg` (classe indépendante)
10
+ ### → `Args` et `Opts`
11
11
 
12
- **Attention ! Cette classe va très certainement changer dans une version postérieure.**
13
-
14
- Cette classe sert à vérifier le type des arguments passés à une fonction et, au besoin, à remplacer un argument invalide par une valeur par défaut.
12
+ Cette classe sert à vérifier le type des arguments passés à une fonction et, au besoin, à remplacer un argument invalide par une valeur par défaut. La variante `Opts` est utilisée pour les dictionnaires d’options.
15
13
 
16
14
  ```ruby
17
15
  def creer_dict_entier cle, valeur
18
- cle = Arg.check cle, Symbol, :def # Nil ne répond pas à #to_sym
19
- valeur = Arg.check valeur, Integer, nil, true
20
- # Si nil est passé comme valeur, il doit rester tel quel et non
21
- # être transformé en 0 par #to_i
16
+ cle, valeur = Util::Args.check cle, Symbol, :def, valeur, Integer, nil
22
17
  { cle => valeur }
23
18
  end
19
+
20
+ def initialize opts={}
21
+ @encodage, @corps, @hauteur_ligne = Util::Opts.check opts, \
22
+ :enc, String, 'UTF-8', :corps, Integer, 12, :hauteur, Float, 1.0
23
+ end
24
24
  ```
25
25
 
26
26
  ### → `ConsoleLogger`
@@ -40,6 +40,21 @@ rescue Exception => e
40
40
  end
41
41
  ```
42
42
 
43
+ ### → `Downloader`
44
+
45
+ Une classe permettant de télécharger un fichier de manière sécurisée.
46
+
47
+ ```ruby
48
+ require 'oga' # Doit être requis par l’utilisateur, `Util` ne le fera pas
49
+ url = 'https://www.perdu.com/'
50
+ html = Util::Downloader.new(url).set_dest(Oga)
51
+ html.download # => Util::Result.ok
52
+ html.data.at_css('h1').text # 'Perdu sur l\'Internet ?'
53
+
54
+ url = 'https://gitlab.com/uploads/-/system/user/avatar/5582173/avatar.png'
55
+ Util::Downloader.new(url).set_name('guillel.png').set_force.download
56
+ ```
57
+
43
58
  ### → `I18n`
44
59
 
45
60
  Une classe permettant de faire de l’internationalisation basique. Ce n’est *en rien* un système complet prenant en charge le sens de lecture des langues ou les différentes manières de gérer le pluriel. Un code d’identification et une langue donnent une chaîne de caractères, rien de plus. Si vous avez besoin de quelque chose de plus complexe, il vaudra mieux vous tourner vers la gemme [`i18n`](https://rubygems.org/gems/i18n) de Rails, voire vers `gettext`.
@@ -66,6 +81,42 @@ puts codes::P3.exist? :prv # true
66
81
  puts codes::P3.valid? :prv # false
67
82
  ```
68
83
 
84
+ ### → `Result`
85
+
86
+ Une classe pour encapsuler un résultat et éviter les exceptions, similaire au `Result` de Rust ou OCaml.
87
+
88
+ ```ruby
89
+ class Num
90
+ attr_reader :contenu
91
+ def initialize num
92
+ @contenu = num.to_f
93
+ end
94
+
95
+ def self.ajouter premier, second
96
+ Util::Result.ok Num.new(premier.contenu + second.contenu)
97
+ end
98
+
99
+ def ajouter num
100
+ Util::Result.ok Num.new(@contenu + num.to_f)
101
+ end
102
+
103
+ def diviser denom
104
+ return Util::Result.err :div0, self if denom == 0
105
+ Util::Result.ok Num.new(@contenu / denom.to_f)
106
+ end
107
+ end
108
+
109
+ res = Num.new(42).ajouter(79) \
110
+ .bindm(:ajouter, 11) \
111
+ .bindm(:diviser, 12) \
112
+ .bindcm(:ajouter, Num.new(13)).value # 24
113
+
114
+ res = Num.new(42).ajouter(79) \
115
+ .bindm(:ajouter, 11) \
116
+ .bindm(:diviser, 0) \
117
+ .bindcm(:ajouter, Num.new(13)).error # :div0
118
+ ```
119
+
69
120
  ### → `Testing`
70
121
 
71
122
  Une classe pour simplifier l’écriture de tests unitaires. Là encore, on reste sur du basique. Pour quelque chose de plus complexe, on se tournera vers des environnements de test dédiés.
@@ -115,20 +166,20 @@ This gem groups together a number of small utilities that aim to make writing co
115
166
 
116
167
  Unless specified otherwise, all classes are contained inside the `Util` main module. See the documentation for a more detailed explanation of how each class works.
117
168
 
118
- ### → `Arg` (independent class)
169
+ ### → `Args` and `Opts`
119
170
 
120
- **Warning! This class will most certainly change in a later version.**
121
-
122
- This class is used to typecheck the arguments provided to a function and, if needed, replace an invalid argument by a default value.
171
+ This class is used to typecheck the arguments provided to a function and, if needed, replace an invalid argument by a default value. The `Opts` variant is used for option hashes.
123
172
 
124
173
  ```ruby
125
174
  def create_integer_hash key, value
126
- key = Arg.check key, Symbol, :def # Nil does not respond to #to_sym
127
- value = Arg.check value, Integer, nil, true
128
- # If nil is provided as value, it shall remain as such and not
129
- # be converted to 0 by #to_i
175
+ key, value = Util::Args.check key, Symbol, :def, value, Integer, nil
130
176
  { key => value }
131
177
  end
178
+
179
+ def initialize opts={}
180
+ @encoding, @font_size, @line_height = Util::Opts.check opts, \
181
+ :enc, String, 'UTF-8', :size, Integer, 12, :height, Float, 1.0
182
+ end
132
183
  ```
133
184
 
134
185
  ### → `ConsoleLogger`
@@ -148,6 +199,21 @@ rescue Exception => e
148
199
  end
149
200
  ```
150
201
 
202
+ ### → `Downloader`
203
+
204
+ A class to safely download a file.
205
+
206
+ ```ruby
207
+ require 'oga' # Must be required by the user, `Util` will not do it
208
+ url = 'https://www.perdu.com/'
209
+ html = Util::Downloader.new(url).set_dest(Oga)
210
+ html.download # => Util::Result.ok
211
+ html.data.at_css('h1').text # 'Perdu sur l\'Internet ?'
212
+
213
+ url = 'https://gitlab.com/uploads/-/system/user/avatar/5582173/avatar.png'
214
+ Util::Downloader.new(url).set_name('guillel.png').set_force.download
215
+ ```
216
+
151
217
  ### → `I18n`
152
218
 
153
219
  A class to do some basic internationalization. It is *in no way* a full-fledged system that would take into account reading directions or different ways of pluralizing. An token and a language give a string, nothing more. If you need something more complex, you should rather use Rails [`i18n`](https://rubygems.org/gems/i18n) gem, or even `gettext`.
@@ -174,6 +240,42 @@ puts codes::P3.exist? :prv # true
174
240
  puts codes::P3.valid? :prv # false
175
241
  ```
176
242
 
243
+ ### → `Result`
244
+
245
+ A class to wrap a result and avoid exceptions, similar to Rust’s or OCaml’s `Result`.
246
+
247
+ ```ruby
248
+ class Num
249
+ attr_reader :content
250
+ def initialize num
251
+ @content = num.to_f
252
+ end
253
+
254
+ def self.add first, second
255
+ Util::Result.ok Num.new(premier.first + second.content)
256
+ end
257
+
258
+ def add num
259
+ Util::Result.ok Num.new(@content + num.to_f)
260
+ end
261
+
262
+ def divide denom
263
+ return Util::Result.err :div0, self if denom == 0
264
+ Util::Result.ok Num.new(@content / denom.to_f)
265
+ end
266
+ end
267
+
268
+ res = Num.new(42).add(79) \
269
+ .bindm(:add, 11) \
270
+ .bindm(:divide, 12) \
271
+ .bindcm(:add, Num.new(13)).value # 24
272
+
273
+ res = Num.new(42).add(79) \
274
+ .bindm(:add, 11) \
275
+ .bindm(:divide, 0) \
276
+ .bindcm(:add, Num.new(13)).error # :div0
277
+ ```
278
+
177
279
  ### → `Testing`
178
280
 
179
281
  A class to make writing unit tests easier. There again, it remains basic. For something more complex, better go with a dedicated testing framework.
@@ -223,20 +325,20 @@ Aquesto gèmo recampo quauqui pichòtis utilita servènt à escriéure dóu code
223
325
 
224
326
  Sènso mencioun countràri, tòuti li classo soun countengudo dins lou moudule principau `Util`. Vèire la doucumentacioun pèr d’esclargimen sus coume marcho cado classo.
225
327
 
226
- ### → `Arg` (classo independento)
328
+ ### → `Args` e `Opts`
227
329
 
228
- **Mèfi ! Aquesto classo va à cop segur chanja dins uno versioun venento.**
229
-
230
- Aquesto classo sèr à s’assegura dóu tipe dis argumen passa à-n-uno founcioun e, au besoun, à ramplaça un argumen invalide amé ’no valour pèr defaut.
330
+ Aquesto classo sèr à s’assegura dóu tipe dis argumen passa à-n-uno founcioun e, au besoun, à ramplaça un argumen invalide amé ’no valour pèr defaut. La varianto `Opts` s’uso pèr li diciounàri d’oupcioun.
231
331
 
232
332
  ```ruby
233
333
  def crea_dic_entie clau, valour
234
- clau = Arg.check clau, Symbol, :def # Nil respond pas à #to_sym
235
- valour = Arg.check valour, Integer, nil, true
236
- # Se nil es baia coume valour, dèu demoura coume acò e pas èstre
237
- # vira en 0 pèr #to_i
334
+ clau, valour = Util::Args.check clau, Symbol, :def, valour, Integer, nil
238
335
  { clau => valour }
239
336
  end
337
+
338
+ def initialize oupc={}
339
+ @encoudage, @cors, @aussado_ligno = Util::Opts.check oupc, \
340
+ :enc, String, 'UTF-8', :cors, Integer, 12, :aussado, Float, 1.0
341
+ end
240
342
  ```
241
343
 
242
344
  ### → `ConsoleLogger`
@@ -256,6 +358,21 @@ rescue Exception => e
256
358
  end
257
359
  ```
258
360
 
361
+ ### → `Downloader`
362
+
363
+ Uno classo pèr telecarga un fiquié seguramen.
364
+
365
+ ```ruby
366
+ require 'oga' # Dèu èstre requist pèr l’usagié, `Util` lou fara pas
367
+ url = 'https://www.perdu.com/'
368
+ html = Util::Downloader.new(url).set_dest(Oga)
369
+ html.download # => Util::Result.ok
370
+ html.data.at_css('h1').text # 'Perdu sur l\'Internet ?'
371
+
372
+ url = 'https://gitlab.com/uploads/-/system/user/avatar/5582173/avatar.png'
373
+ Util::Downloader.new(url).set_name('guillel.png').set_force.download
374
+ ```
375
+
259
376
  ### → `I18n`
260
377
 
261
378
  Uno classo pèr faire de l’internaciounalisacioun elementàri. Es *pas ges* de sistèmo entié que tèn comte dóu sèns de leituro di lengo vo di biais diferènt de mena lou plurau. Un code d’identificacioun e uno lengo baion uno cadeno d’emprèsso, ges de mai. Se vous fai mestié d’agué quicon de mai coumplèisse, vaudra miés chausi la gèmo [`i18n`](https://rubygems.org/gems/i18n) de Rails, emai bessai `gettext`.
@@ -282,6 +399,42 @@ puts codes::P3.exist? :prv # true
282
399
  puts codes::P3.valid? :prv # false
283
400
  ```
284
401
 
402
+ ### → `Result`
403
+
404
+ Uno classo pèr embala un resultat e s’engarda dis eicecioun, coume lou `Result` de Rust o OCaml.
405
+
406
+ ```ruby
407
+ class Num
408
+ attr_reader :countengu
409
+ def initialize num
410
+ @countengu = num.to_f
411
+ end
412
+
413
+ def self.apoundre premie, segound
414
+ Util::Result.ok Num.new(premie.countengu + segound.countengu)
415
+ end
416
+
417
+ def apoundre num
418
+ Util::Result.ok Num.new(@countengu + num.to_f)
419
+ end
420
+
421
+ def parti denoum
422
+ return Util::Result.err :div0, self if denoum == 0
423
+ Util::Result.ok Num.new(@countengu / denoum.to_f)
424
+ end
425
+ end
426
+
427
+ res = Num.new(42).apoundre(79) \
428
+ .bindm(:apoundre, 11) \
429
+ .bindm(:parti, 12) \
430
+ .bindcm(:apoundre, Num.new(13)).value # 24
431
+
432
+ res = Num.new(42).apoundre(79) \
433
+ .bindm(:apoundre, 11) \
434
+ .bindm(:parti, 0) \
435
+ .bindcm(:apoundre, Num.new(13)).error # :div0
436
+ ```
437
+
285
438
  ### → `Testing`
286
439
 
287
440
  Uno classo pèr assimpli l’escrituro d’assai unitàri. Aqui tambèn, demouro elementàri. Pèr quicon de mai coumplèisse, fau chausi un vertadier envirounamen d’assai.
@@ -1,7 +1,11 @@
1
+ require 'util/args'
1
2
  require 'util/communia'
2
3
  require 'util/console_logger'
4
+ require 'util/downloader'
3
5
  require 'util/i18n'
4
6
  require 'util/lists'
7
+ require 'util/result'
8
+ require 'util/test'
5
9
  require 'util/yaml'
6
10
 
7
11
  # A collection of simple utilities to reduce boilerplate.
@@ -0,0 +1,138 @@
1
+ module Util
2
+ # Functions to typecheck the arguments of a function.
3
+ class Args
4
+ private_class_method :new
5
+
6
+ # If no alternative value is provided to {check}, these will be used.
7
+ DEFAULT_VALUES = {
8
+ Array => [],
9
+ 'Boolean' => false,
10
+ Class => NilClass,
11
+ Complex => 0.to_c,
12
+ Encoding => Encoding::UTF_8,
13
+ FalseClass => true,
14
+ Float => 0.0,
15
+ Hash => {},
16
+ Integer => 0,
17
+ Module => Kernel,
18
+ NilClass => nil,
19
+ Object => Object.new,
20
+ Queue => Queue.new,
21
+ Random => Random::DEFAULT,
22
+ Range => (0..),
23
+ Rational => 0.to_r,
24
+ Regexp => /.*/,
25
+ SizedQueue => SizedQueue.new(1),
26
+ String => '',
27
+ Symbol => :nil,
28
+ Time => Time.now,
29
+ TrueClass => false,
30
+ }
31
+
32
+ # @no_doc
33
+ FUNCTIONS = {
34
+ Array => :to_a,
35
+ Complex => :to_c,
36
+ Enumerator => :to_enum,
37
+ Float => :to_f,
38
+ Hash => :to_h,
39
+ Integer => :to_i,
40
+ Rational => :to_r,
41
+ String => :to_s,
42
+ Symbol => :to_sym,
43
+ }
44
+
45
+ # Verify whether a given argument or arguments belong to a class or can
46
+ # be converted into that class. Any number of sequences of the three
47
+ # arguments below can be passed.
48
+ # @param [Object] value argument to check
49
+ # @param [Class, String] klass class to which it must belong; Boolean is
50
+ # a synonym for TrueClass; all standard classes who have #to_X will try
51
+ # to convert the value if it responds to the given conversion method
52
+ # (if not provided, +NilClass+ is used)
53
+ # @param [Object] alt value to use if the argument does not belong to the
54
+ # wanted class (if not provided, will default to +DEFAULT_VALUES[klass]+)
55
+ # @return [Object, Array<Object>] an array of the checked and converted
56
+ # values, or just the value if the array has only one element
57
+ # @example
58
+ # def create_integer_hash key, value
59
+ # key, value = Util::Args.check key, Symbol, :def, value, Integer, nil
60
+ # { key => value }
61
+ # end
62
+ #
63
+ # hash1 = create_integer_hash 'hello', 13.4 # { :hello => 13 }
64
+ # hash2 = create_integer_hash nil, nil # { :def => nil }
65
+ def self.check *args
66
+ check_internal nil, *args
67
+ end
68
+
69
+ # Typecheck the content of an options hash, while ignoring undefined
70
+ # options. Calls Args.check on the values associated with a given key,
71
+ # according to the rest of the informations given.
72
+ # @param [Hash] opts hash whose content to check
73
+ # @param [Array] args see {check} for the rest of the arguments
74
+ # @return (see check)
75
+ # @note It is not necessary to check whether +opts+ is a +Hash+, the
76
+ # method will do it.
77
+ # @example
78
+ # def initialize opts={}
79
+ # @encoding, @font_size, @line_height = Util::Args.check_opts opts, \
80
+ # :enc, String, 'UTF-8', :size, Integer, 12, :height, Float, 1.0
81
+ # end
82
+ def self.check_opts opts, *args
83
+ check_internal opts, *args
84
+ end
85
+
86
+ private
87
+
88
+ # Common parts to both {check} and {check_opts}.
89
+ def self.check_internal opts, *args
90
+ return nil if args.length == 0
91
+ opts = check opts, Hash, {} unless opts.nil?
92
+ count, modulo = args.length.divmod 3
93
+
94
+ if modulo == 1 then
95
+ args << NilClass
96
+ end
97
+
98
+ if modulo > 0 then
99
+ count += 1
100
+ args << DEFAULT_VALUES[args.last]
101
+ end
102
+
103
+ res = []
104
+ (0...count).each do |i|
105
+ value, klass, alt = args[i*3], args[i*3+1], args[i*3+2]
106
+ value = opts[value] unless opts.nil?
107
+ res << alt and next if value.nil?
108
+
109
+ if FUNCTIONS.has_key? klass then
110
+ f = FUNCTIONS[klass]
111
+ res << (value.respond_to?(f) ? value.send(f) : alt)
112
+ next
113
+ end
114
+
115
+ case klass.to_s
116
+ when 'Boolean' then res << (value ? true : alt) and next
117
+ when 'FalseClass' then res << (value ? alt : false) and next
118
+ when 'TrueClass' then res << (value ? true : alt) and next
119
+ end
120
+
121
+ res << alt and next unless klass.is_a?(Class) and value.is_a?(klass)
122
+ res << value
123
+ end
124
+
125
+ (res.length < 2) ? res.first : res
126
+ end
127
+ end
128
+
129
+ # Alias for {Args.check_opts}
130
+ class Opts
131
+ private_class_method :new
132
+
133
+ # Alias for {Args.check_opts}
134
+ def self.check opts, *args
135
+ Args.check_opts opts, *args
136
+ end
137
+ end
138
+ end
@@ -15,7 +15,7 @@ module Util
15
15
  # # Message written in red
16
16
  # end
17
17
  class ConsoleLogger
18
- require 'util/arg'
18
+ require 'util/args'
19
19
 
20
20
  # ANSI code to reset all formatting
21
21
  RESET = "\x1b[0m"
@@ -44,14 +44,14 @@ module Util
44
44
  # :dbl_underline, :faint, :italic, :overline, :reverse,
45
45
  # :underline, Array<idem>] :decor text decorations
46
46
  def self.escape_code opts={}
47
- opts = Arg.check opts, Hash, {}
47
+ opts = Util::Args.check opts, Hash, {}
48
48
  return RESET if opts.empty?
49
49
  code = ''
50
50
 
51
51
  if opts.has_key? :color then
52
- color = Arg.check opts[:color], Symbol, :white
52
+ color = Util::Args.check opts[:color], Symbol, :white
53
53
  color = :white unless COLORS.has_key? color
54
- bright = Arg.check opts[:bright], 'Boolean', false
54
+ bright = Util::Args.check opts[:bright], 'Boolean', false
55
55
 
56
56
  cur = COLOR_TYPES[:fg] + COLORS[color]
57
57
  cur += COLOR_TYPES[:bright] if bright
@@ -59,9 +59,9 @@ module Util
59
59
  end
60
60
 
61
61
  if opts.has_key? :bgcolor then
62
- color = Arg.check opts[:bgcolor], Symbol, :black
62
+ color = Util::Args.check opts[:bgcolor], Symbol, :black
63
63
  color = :black unless COLORS.has_key? color
64
- bright = Arg.check opts[:bgbright], 'Boolean', false
64
+ bright = Util::Args.check opts[:bgbright], 'Boolean', false
65
65
 
66
66
  cur = COLOR_TYPES[:bg] + COLORS[color]
67
67
  cur += COLOR_TYPES[:bright] if bright
@@ -70,7 +70,7 @@ module Util
70
70
  end
71
71
 
72
72
  if opts.has_key? :decor then
73
- decors = Arg.check opts[:decor], Array, [opts[:decor]]
73
+ decors = Util::Args.check opts[:decor], Array, [opts[:decor]]
74
74
  cur = ''
75
75
  decors.each do |d|
76
76
  cur += ';' + DECORS[d].to_s if DECORS.has_key? d
@@ -96,7 +96,7 @@ module Util
96
96
  # @option config [Hash { :stderr => Boolean, :code => String }]
97
97
  # w configuration for warning (defaults to yellow text)
98
98
  def initialize config={}
99
- config = Arg.check config, Hash, {}
99
+ config = Util::Args.check config, Hash, {}
100
100
  @config = {
101
101
  :e => { :io => $stdout, :code => CL.escape_code(color: :red) },
102
102
  :i => { :io => $stdout, :code => CL.escape_code(color: :cyan) },
@@ -107,7 +107,7 @@ module Util
107
107
 
108
108
  config.each_pair do |k, v|
109
109
  next unless @config.has_key? k
110
- v = Arg.check v, Hash, {}
110
+ v = Util::Args.check v, Hash, {}
111
111
  @config[k][:io] = (v[:stderr] == true) ? $stderr : $stdout
112
112
  @config[k][:code] = CL.escape_code v
113
113
  end
@@ -167,8 +167,8 @@ module Util
167
167
  # @param payload (see #error)
168
168
  # @return nil
169
169
  def printf type, msg, payload
170
- msg = Arg.check msg, String, ''
171
- payload = Arg.check payload, Hash, {}
170
+ msg = Util::Args.check msg, String, ''
171
+ payload = Util::Args.check payload, Hash, {}
172
172
  payload.each_pair do |k, v|
173
173
  msg = msg.gsub "%#{k}%", v.to_s
174
174
  end