rafini 2.0.0 → 3.1.221212

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b81e63f8b4651660cd62560692546173a7ae971ce0a0a76b6bc772c573f2d181
4
- data.tar.gz: 37146803ff33ddcf74da2cd90bb5ddd4a2b1da0fe2be0c55b1b5b984a14029ca
3
+ metadata.gz: 8daaac2f079c639abaa892b634f6400f1b0e75ffead282c4551dc8fb7fe45fda
4
+ data.tar.gz: a383dca85331bc2cf43e443fc909a9612044ea2c0f73a33a7bc9c568f2db0370
5
5
  SHA512:
6
- metadata.gz: ecb3a3935d9892365a7568e304994605657b0ff0ed9c4e5adab0eee83c4d42d9584b0b6c6cdcd2d602f2817371d3a632f51396bf62b9ca134153c7eae926f22a
7
- data.tar.gz: a1795c03155fbaa64fd90b4a5a8ab4ad033fa5489bb925168a765ab4be71b742cca5bc61d282c88c9c4af75c2b9c24feb8c2bd87f5d01189a5b32e34fd2a8c89
6
+ metadata.gz: b169dce8d8edd85971c8e62c433b3485a863fa8327e84430e253d660dbc0ca5a89c0106830ecedbfdb52103fd12bfb0193b07504dbc50b2812ced3a12096595c
7
+ data.tar.gz: c62702b40d3f826f4a63828e3122dada54376c8cd23f358b17a1c6650e6fc79bec2914f706c55d61c2ba0a087e88c2a848cdaec7410bda5b238d44b33f6b4f9d
data/README.md CHANGED
@@ -1,101 +1,139 @@
1
- # rafini 2.0.0
1
+ # Rafini
2
+
3
+ * [VERSION 3.1.221212](https://github.com/carlosjhr64/rafini/releases)
4
+ * [github](https://github.com/carlosjhr64/rafini)
5
+ * [rubygems](https://rubygems.org/gems/rafini)
2
6
 
3
7
  ## DESCRIPTION:
4
8
 
5
9
  Just a collection of useful refinements.
6
10
 
11
+ ## INSTALL:
12
+ ```shell
13
+ $ gem install rafini
14
+ ```
7
15
  ## SYNOPSIS:
8
-
16
+ ```ruby
17
+ require 'rafini'
18
+ ```
19
+ ### include Rafini::Empty
20
+ ```ruby
21
+ include Rafini::Empty
22
+
23
+ s0 #=> ""
24
+ a0 #=> []
25
+ h0 #=> {}
26
+ [s0,a0,h0].all?(&:frozen?) #=> true
27
+ ```
9
28
  ### using Rafini::Array
29
+ ```ruby
30
+ using Rafini::Array
10
31
 
11
- # joins
12
- ['a','b','c','d','e','f'].joins('-','-',' '){':'} #=> "a-b-c d:e:f"
13
-
14
- # per
15
- h={}
16
- ['a','b','c'].per(['A','B','C']){|l,u| h[l]=u}
17
- h #=> {'a'=>'A','b'=>'B','c'=>'C'}
18
-
19
- # which
20
- ['dog','cat','bunny'].which{|a|a=~/c/} #=> "cat"
21
-
22
- # is
23
- [:a,:b,:c].is(true) #=> {a: true, b: true, c: true}
32
+ # classify(like Set#classify)
33
+ [1, 2.0, 'Three', 4.0].classify #=> {Integer=>[1], Float=>[2.0, 4.0], String=>["Three"]}
24
34
 
25
- ### using Rafini::Exception
26
-
27
- # $!.puts
28
- begin
29
- raise 'Ugly Message'
30
- rescue RuntimeError
31
- $!.puts 'Nice Message'
32
- end
33
-
34
- # Rafini.bang!
35
- value = Rafini.bang!('Nice Message') do
36
- raise 'Ugly Message'
37
- end
38
- value #=> return value of block or error object
39
-
40
- # Rafini.thread_bang!
41
- Rafini.thread_bang!('Nice Message') do
42
- # this is in a thread
43
- raise 'Ugly Message'
44
- end
35
+ # is
36
+ [:a,:b,:c].is(true) #=> {:a=>true, :b=>true, :c=>true}
45
37
 
38
+ # joins
39
+ ['Y','M','D','h','m','s'].joins('-','-',' '){':'}
40
+ #=> "Y-M-D h:m:s"
41
+ [1,9,2,8,3,7,4,6,5,5].joins{|a,b|a>b ? '>': a<b ? '<': '='}
42
+ #=> "1<9>2<8>3<7>4<6>5=5"
43
+ ```
46
44
  ### using Rafini::Hash
45
+ ```ruby
46
+ using Rafini::Hash
47
47
 
48
- # to_struc
49
- struct = {a:'A',b:'C',c:'C'}.to_struct
50
- struct.a #=> 'A'
51
-
52
- # modify
53
- {a:'A',b:'B'}.modify({b:'X',c:'C'},{c:'Y',d:'D'}) #=> {a:'A',b:'X',c:'Y',d:'D'}
48
+ # to_struc
49
+ struct = {a:'A',b:'C',c:'C'}.to_struct{ def ok = "OK" }
50
+ struct #=> #<struct a="A", b="C", c="C">
51
+ struct.a #=> "A"
52
+ struct.ok #=> "OK"
54
53
 
55
- # supplement
56
- {a:'A',b:'B'}.supplement({b:'X',c:'C'},{c:'Y',d:'D'}) #=> {a:'A',b:'B',c:'C',d:'D'}
57
-
58
- # amend
59
- {a:'A',b:'B'}.amend({b:'X',c:'C'},{c:'Y',d:'D'}) #=> {a:'A',b:'X'}
60
-
61
- # maps
62
- {a:'A',b:'B',c:'C',c:'D'}.maps(:c,:a,:b) #=> ['C','A','B']
54
+ # supplement
55
+ {a:'A',b:'B'}.supplement({b:'X',c:'C'},{c:'Y',d:'D'}) #=> {:a=>"A", :b=>"B", :c=>"C", :d=>"D"}
63
56
 
57
+ # amend
58
+ {a:'A',b:'B'}.amend({b:'X',c:'C'},{c:'Y',d:'D'}) #=> {:a=>"A", :b=>"X"}
59
+ ```
64
60
  ### using Rafini::Integer
61
+ ```ruby
62
+ using Rafini::Integer
65
63
 
66
- # odometer
67
- 123.odometer(10,10) #=> [3,2,1]
68
- 30.odometer(2,3,5) #=> [0,0,0,1]
69
-
70
- ### using Rafini::Odometers
71
-
72
- # sec2time
73
- 12501.sec2time.to_s #=> "3 hours and 28 minutes"
74
-
75
- # illion
76
- 3_512_325.illion.to_s #=> "3.51M"
77
-
64
+ # odometer
65
+ 123.odometer(10,10) #=> [3, 2, 1]
66
+ 30.odometer(2,3,5) #=> [0, 0, 0, 1]
67
+ ```
78
68
  ### using Rafini::String
69
+ ```ruby
70
+ using Rafini::String
79
71
 
80
- # camelize
81
- 'a_camel_kick'.camelize #=> "ACamelKick"
82
-
83
- # semantic
84
- '1.2.3'.semantic(0..1) #=> '1.2'
85
-
86
- ### Rafini::Empty
72
+ # camelize
73
+ 'a_camel_kick'.camelize #=> "ACamelKick"
87
74
 
88
- STRING, ARRAY, HASH = ''.frozen, [].frozen, {}.frozen
89
-
90
- ## INSTALL:
91
-
92
- $ gem install rafini
75
+ # semantic
76
+ '1.2.3'.semantic(0..1) #=> "1.2"
93
77
 
78
+ # shellescape(like Shellwords.escape)
79
+ 'Hello World!'.shellescape #=> "Hello\\ World\\!"
80
+ ```
81
+ ### using Rafini::Exception
82
+ ```ruby
83
+ using Rafini::Exception
84
+
85
+ # $!.puts
86
+ # Normally stderr.puts your "Nice" message.
87
+ # Additionally puts your "Ugly" message when $VERBOSE.
88
+ # Additionally puts backtrace when $DEBUG
89
+ # No output when $VERBOSE is nil.
90
+ begin
91
+ raise 'Ugly Message'
92
+ rescue RuntimeError
93
+ $!.puts 'Nice Message'
94
+ end
95
+
96
+ # Rafini.bang!
97
+ error = Rafini.bang!('Nice Message') do
98
+ raise 'Ugly Message'
99
+ # Outputs as $!.puts "Nice Message"
100
+ end
101
+ error.class #=> RuntimeError
102
+ error.to_s #=> "Ugly Message"
103
+
104
+ # Rafini.thread_bang!
105
+ thread = Rafini.thread_bang!('Nice Message') do
106
+ # this is in a thread
107
+ raise 'Ugly Message' # outputs as $!.puts 'Nice Message'
108
+ end
109
+ # The returned value joined from the thread
110
+ # will not re-raise the error(but gives the error).
111
+ thread.value.class #=> RuntimeError
112
+ ```
113
+ ### using Rafini::Odometers
114
+ ```ruby
115
+ using Rafini::Odometers
116
+
117
+ # sec2time
118
+ 12501.sec2time.to_s #=> "3 hours and 28 minutes"
119
+
120
+ # illion
121
+ 3_512_325.illion.to_s #=> "3.51M"
122
+ ```
123
+ ### using Rafini::Requires
124
+ ```ruby
125
+ using Rafini::Requires
126
+
127
+ requires'
128
+ ruby ~>3.0
129
+ rafini ~>3.1
130
+ json ~>2.0' #=> ["json"]
131
+ ```
94
132
  ## LICENSE:
95
133
 
96
134
  (The MIT License)
97
135
 
98
- Copyright (c) 2020 carlosjhr64
136
+ Copyright (c) 2022 CarlosJHR64
99
137
 
100
138
  Permission is hereby granted, free of charge, to any person obtaining
101
139
  a copy of this software and associated documentation files (the
data/lib/rafini/array.rb CHANGED
@@ -1,54 +1,55 @@
1
1
  module Rafini
2
2
  module Array
3
3
  refine ::Array do
4
- # string = array.joins(sep1,sep2,sep3,...){|i| sep[i]}
5
- #
6
- # Returns a string created by converting each element of the array to
7
- # a string, separated by the given separators.
8
- # If no separators are given or are used up,
9
- # it uses the value of the executed block, which is passed an iteration number.
10
- # Note that the iteration number starts at 1.
11
- # Lastly it uses an empty string.
12
- # ['a','b','c','d','e','f'].joins('-', '-', ' '){':'} #=> 'a-b-c d:e:f'
13
- # ['a','b','c'].joins{','} #=> 'a,b,c'
14
- # ['1','2','3'].joins('.') #=> '1.23'
15
- # ['a','b','c'].joins{|i|i} #=> 'a1b2c'
16
- def joins(*p, &block)
17
- str = ''
18
- if length > 0
19
- str << self[0]
20
- 1.upto(length-1) do |i|
21
- str << (p.empty? ? (block ? block.call(i).to_s : '') : p.shift.to_s)
22
- str << self[i]
23
- end
24
- end
25
- return str
26
- end
27
-
28
- # array1.per(array2){|obj1, obj2| ... }
29
- #
30
- # Gives the block each two elements of two array respectively.
31
- # If the second array is not given, it passes the block the index number instead.
32
- # h={} # say you have a hash h, then
33
- # ['a','b','c'].per(['A','B','C']){|l,n| h[l]=n} # h=={'a'=>'A','b'=>'B','c'=>'C'}
34
- # ['a','b','c'].per{|l,i| h[l]=i} # h=={'a'=>0,'b'=>1,'c'=>2}
35
- def per(b=nil)
36
- 0.upto(length-1){|i| yield self[i], (b)? b[i] : i}
4
+ # classify:
5
+ # Like Set#classify
6
+ def classify(hash: ::Hash.new{|h,k|h[k]=[]}, &block)
7
+ block ||= lambda{|v|v.class}
8
+ self.each{|v| hash[block[v]] << v}
9
+ return hash
37
10
  end
38
11
 
39
- # array.which{|a|...}
40
- #
41
- # Returns first object for which block is true.
42
- # ['dog','cat','bunny'].which{|a|a=~/c/} #=> "cat"
43
- alias which detect
44
-
12
+ # is:
45
13
  # [:a,:b,:c].is(true) #=> {:a=>true,:b=>true,:c=>true}
46
14
  #
47
15
  # Updates a hash with the keys given by the array to the given value.
48
16
  def is(value, hash={})
49
- self.each{|key| hash[key]=value}
17
+ each{|key| hash[key]=value}
50
18
  return hash
51
19
  end
20
+
21
+ # joins:
22
+ # type _AToS = ::Array[(_AToS|_ToS)]
23
+ # _AToS#joins(*_AToS seps)?{(_ToS,_ToS)->_ToS}
24
+ #
25
+ # Returns a string created by joining the elements of the (flatten) array,
26
+ # separated by the given (flatten) separators.
27
+ # If no separators are given or are used up,
28
+ # it uses the value of the executed block,
29
+ # which is passed the next neigboring iteration items.
30
+ # Else, it just joins the items.
31
+ # ['2021','Jan','09','07','29','05'].joins('-', '-', ' '){':'}
32
+ # #=> "2021-Jan-09 07:29:05"
33
+ # [:a,[1,2],:b].joins{','} #=> 'a,1,2,b'
34
+ # [3,1,4,1,5,9].joins('.') #=> '3.14159'
35
+ # [1,9,2,8,3,7,4,6,5,5].joins{|a,b|a>b ? '>': a<b ? '<': '='}
36
+ # #=> "1<9>2<8>3<7>4<6>5=5"
37
+ def joins(*seps, &block)
38
+ return '' if length < 1
39
+ items = flatten
40
+ previous = items.shift
41
+ string = ::String.new previous.to_s
42
+ return string if items.empty?
43
+ seps.flatten!
44
+ while item = items.shift
45
+ if sep = seps.shift&.to_s || block&.call(previous,item)&.to_s
46
+ string << sep
47
+ end
48
+ string << item.to_s
49
+ previous = item
50
+ end
51
+ return string
52
+ end
52
53
  end
53
54
  end
54
55
  end
data/lib/rafini/empty.rb CHANGED
@@ -1,10 +1,11 @@
1
1
  module Rafini
2
- # In a world where objects are allowed to represent infinities,
3
- # Rafini dares to introduce empty sets. But WHY!???
4
- # Ta-ta-TUM...
5
2
  module Empty
6
- ARRAY = [].freeze
7
- HASH = {}.freeze
3
+ ARRAY = [].freeze
4
+ HASH = {}.freeze
8
5
  STRING = ''.freeze
6
+
7
+ def a0 = ARRAY
8
+ def h0 = HASH
9
+ def s0 = STRING
9
10
  end
10
11
  end
@@ -29,7 +29,7 @@ using Rafini::Exception
29
29
  # value = Rafini.bang!('Ooops! Not perfect?') do
30
30
  # # Perfect code here...
31
31
  # end
32
- def Rafini.bang!(message=nil, bang=Exception, &block)
32
+ def Rafini.bang!(message=nil, bang=::Exception, &block)
33
33
  value = nil
34
34
  begin
35
35
  value = block.call
@@ -52,6 +52,6 @@ end
52
52
  # end
53
53
  # With the following below, I'll be able to say
54
54
  # Rafini.thread_bang!('blah blah...'){ ...stuff... }
55
- def Rafini.thread_bang!(header=nil, bang=Exception, &block)
55
+ def Rafini.thread_bang!(header=nil, bang=::Exception, &block)
56
56
  Thread.new{Rafini.bang!(header, bang, &block)}
57
57
  end
data/lib/rafini/hash.rb CHANGED
@@ -3,30 +3,17 @@ module Rafini
3
3
  refine ::Hash do
4
4
  # struct = hash.to_struct
5
5
  # Why not?
6
- def to_struct
7
- Struct.new(*self.keys).new(*self.values)
6
+ def to_struct(&blk)
7
+ Struct.new(*keys, &blk).new(*values)
8
8
  end
9
9
 
10
- # hash0.modify(hash1,...) #=> hash
11
- #
12
- # Updates hash with hashes.
13
- # Overwrites existing elements and adds elements.
14
- # {a:'A',b:'B'}.modify({b:'X',c:'C'},{c:'Y',d:'D'}) #=> {a:'A',b:'X',c:'Y',d:'D'}
15
- def modify(*hashes)
16
- hashes.each do |hash|
17
- hash.each do |key, value|
18
- self[key] = value
19
- end
20
- end
21
- self
22
- end
23
-
24
- # hash0.supplement(hash1,...) #=> hash
10
+ # hash0.supplement!(hash1,...) #=> hash
25
11
  #
26
12
  # Supplements hash with hashes.
27
13
  # Adds missing elements only.
28
- # {a:'A',b:'B'}.supplement({b:'X',c:'C'},{c:'Y',d:'D'}) #=> {a:'A',b:'B',c:'C',d:'D'}
29
- def supplement(*hashes)
14
+ # {a:'A',b:'B'}.supplement({b:'X',c:'C'},{c:'Y',d:'D'})
15
+ # #=> {a:'A',b:'B',c:'C',d:'D'}
16
+ def supplement!(*hashes)
30
17
  hashes.each do |hash|
31
18
  hash.each do |key, value|
32
19
  self[key] = value unless self.has_key?(key)
@@ -34,30 +21,24 @@ module Rafini
34
21
  end
35
22
  self
36
23
  end
24
+ def supplement(...)
25
+ self.dup.supplement!(...)
26
+ end
37
27
 
38
- # hash0.ammend(hash1,...)
28
+ # hash0.amend(hash1,...)
39
29
  #
40
30
  # Ammends hash with hashes.
41
- # Overwrites existing elements only.
42
- # {a:'A',b:'B'}.supplement({b:'X',c:'C'},{c:'Y',d:'D'}) #=> {a:'A',b:'X'}
43
- def amend(*hashes)
44
- self.keys.each do |key|
45
- hashes.each do |hash|
46
- if hash.has_key?(key)
47
- self[key] = hash[key]
48
- break
49
- end
50
- end
31
+ # Overwrites existing keys
32
+ # only with first key value found in amending hashes.
33
+ # {a:'A',b:'B'}.amend({b:'X',c:'C'},{c:'Y',d:'D'}) #=> {a:'A',b:'X'}
34
+ def amend!(*hashes)
35
+ self.each_key do |key|
36
+ value=hashes.find{_1.has_key? key}&.fetch(key) and self[key]=value
51
37
  end
52
38
  self
53
39
  end
54
-
55
- # hash.maps(key1,...)
56
- #
57
- # Maps parameters list with hash.
58
- # {a:'A",b:'B',c:'C'}.maps(:c,:a,:b) #=> ['C','A','B']
59
- def maps(*keys)
60
- keys.map{|_|self[_]}
40
+ def amend(...)
41
+ self.dup.amend!(...)
61
42
  end
62
43
  end
63
44
  end
@@ -5,21 +5,25 @@ module Rafini
5
5
  # Kinda hard to explain...
6
6
  # 123.odometer(10,10) #=> [3,2,1]
7
7
  # 30.odometer(2,3,5) #=> [0,0,0,1]
8
- # ((60*60*24)*3 + (60*60)*12 + 60*15 + 30).odometer(60,60,24) #=> [30, 15, 12, 3]
8
+ # ((60*60*24)*3 + (60*60)*12 + 60*15 + 30).odometer(60,60,24)
9
+ # #=> [30, 15, 12, 3]
9
10
  # Useful for making clocks, number scales, mayan long count... etc.
10
- def odometer(*p)
11
- n = self
12
- m = p.inject(1,:*)
13
- r = []
11
+ def odometer(*levels, factors: true)
12
+ raise RangeError, 'negative odometer' if self < 0
13
+ readings, remainder = [], self
14
14
 
15
- (p.length-1).downto(0) do |i|
16
- y = n/m; r.unshift y
17
- n = n%m
18
- f = p[i]; m = m/f
15
+ levels = levels.inject([1]){|m, f| m.push(m.last*f)} if factors
16
+ levels.shift
17
+
18
+ levels.reverse_each do |level|
19
+ # in case of a float, floor
20
+ reading = (remainder/level).floor
21
+ readings.unshift reading
22
+ remainder = remainder%level
19
23
  end
20
- r.unshift n
21
24
 
22
- return r
25
+ # in case of a float, round
26
+ readings.unshift remainder.round
23
27
  end
24
28
  end
25
29
  end
@@ -1,78 +1,101 @@
1
+ require 'rafini/integer'
2
+ require 'rafini/hash'
3
+
1
4
  module Rafini
2
5
  module Odometers
3
- SEC2TIME = {
4
- second: 60,
6
+ # Sidereal Year: https://en.wikipedia.org/wiki/Year
7
+ year = 365*24*60*60 + 6*60*60 + 9*60 + 9.76
8
+ SEC2TIME = { # levels
9
+ second: 1,
5
10
  minute: 60,
6
- hour: 24,
7
- day: 7,
8
- week: 4,
9
- month: 13,
10
- year: 10,
11
- decade: 10,
12
- centurie: 10,
13
- millennium: 10,
14
- age: 10,
15
- epoch: 10,
16
- era: 5,
17
- eon: 2,
18
- gigaannum: nil,
11
+ hour: 60*60,
12
+ day: 24*60*60,
13
+ week: 7*24*60*60,
14
+ month: year/12.0,
15
+ year: year,
16
+ decade: 10*year,
17
+ centurie: 100*year,
18
+ millennium: 1_000*year,
19
+ age: 10_000*year,
20
+ epoch: 100_000*year,
21
+ era: 1_000_000*year,
22
+ eon: 5_000_000*year,
23
+ gigaannum: 10_000_000*year,
19
24
  }
20
25
 
21
- SCALE = {
26
+ SCALE = { # levels
22
27
  base: {
23
- ones: 10,
24
- tens: 10,
25
- hundreds: 10,
26
- thousands: 1_000,
28
+ one: 1,
29
+ ten: 10,
30
+ hundred: 100,
31
+ thousand: 10**3,
32
+ million: 10**6,
27
33
  },
28
34
  short: {
29
- millions: 1_000,
30
- billions: 1_000,
31
- trillions: 1_000,
32
- quadrillions: nil,
35
+ billion: 10**9,
36
+ trillion: 10**12,
37
+ quadrillion: 10**15,
33
38
  },
34
39
  long: {
35
- millions: 1_000_000,
36
- billions: 1_000_000,
37
- trillions: 1_000_000,
38
- quadrillions: nil,
40
+ billion: 10**12,
41
+ trillion: 10**18,
42
+ quadrillion: 10**24,
39
43
  },
40
44
  }
41
45
 
42
- refine ::Integer do
43
- # Need Rafini::Integer for #odometer
44
- # Need Rafini::Hash for #to_struct
45
- # Need Rafini::Array for #per
46
- [Rafini::Integer, Rafini::Hash, Rafini::Array].each{|mod| using mod}
47
-
48
- def odoread(scale)
49
- values = scale.values
50
- keys = scale.keys;
51
- counts = self.odometer(*values[0..-2])
52
-
53
- string = "#{counts[0]} #{keys[0]}#{(counts[0]==1)? '' : 's'}"
54
- (keys.length-1).downto(1) do |i|
55
- if counts[i] > 0
56
- string = "#{counts[i]} #{keys[i]}#{(counts[i]>1)? 's' : ''}"
57
- string << " and #{counts[i-1]} #{keys[i-1]}#{(counts[i-1]>1)? 's' : ''}" if counts[i-1]>0
46
+ refine ::Hash do
47
+ def description
48
+ string = ''
49
+ reverse_each do |key, count|
50
+ s = (count==1)? '' : 's'
51
+ unless string.empty?
52
+ string << " and #{count} #{key}#{s}" if count > 0
58
53
  break
59
54
  end
55
+ next if count == 0
56
+ string << "#{count} #{key}#{s}"
60
57
  end
58
+ return string
59
+ end
60
+ end
61
61
 
62
- hash = {}
63
- keys.per(counts){|k,v| hash[k]=v}
64
- hash[:to_s]=string
62
+ refine ::Integer do
63
+ using Rafini::Integer # Need Rafini::Integer for #odometer
64
+ using Rafini::Hash # Need Rafini::Hash for #to_struct
65
65
 
66
- return hash.to_struct
66
+ def odoread(scale, **kw, &blk)
67
+ counts = odometer(*scale.values, **kw)
68
+ ::Hash[scale.keys.zip(counts)].to_struct(&blk)
67
69
  end
68
70
 
69
71
  # Integer#sec2time
70
72
  # Returns a struct with the different time scales for number of seconds.
71
- # Note that the month(4 weeks)/year(13 months) are not meant to be exact.
72
73
  # 10_000.sec2time.to_s #=> "2 hours and 46 minutes"
73
74
  # 10_000.sec2time.hour #=> 2
74
75
  def sec2time
75
- self.odoread(SEC2TIME)
76
+ odoread(SEC2TIME, factors:false) do
77
+ def to_s
78
+ string = nil
79
+ SEC2TIME.keys.reverse_each do |key|
80
+ count=self[key]
81
+ if string
82
+ if count > 0
83
+ string << " and #{count} #{key}"
84
+ string << 's' if count > 1
85
+ end
86
+ break
87
+ end
88
+ next if count==0
89
+ string = "#{count} #{key}"
90
+ string << 's' if count > 1
91
+ end
92
+ string = "0 #{SEC2TIME.first[0]}s" unless string
93
+ string
94
+ end
95
+ def to_i
96
+ SEC2TIME.to_a.map{|k,v|v*self[k]}.sum.round
97
+ end
98
+ end
76
99
  end
77
100
 
78
101
  # 1_230.illion.to_s #=> "1.23k"
@@ -91,37 +114,42 @@ module Rafini
91
114
  # m.quadrillions #=> 888
92
115
  # m.to_s #=> "888Q" It rounds up 888.7!
93
116
  def illion(type=:short)
94
- keys = SCALE[:base].keys + SCALE[type].keys
95
- values = SCALE[:base].values + SCALE[type].values
96
- counts = self.odometer(*values[0..-2])
97
-
98
- string = nil
99
- if self < 1_000
100
- string = self.to_s
101
- elsif self < 1_000_000
102
- d = (self<10_000)? 2 : (self<100_000)? 1 : 0
103
- m = (self/1000.0).round(d)
104
- string = "#{m}k"
105
- else
106
- (keys.length-1).downto(4) do |i|
107
- next unless counts[i]>0
108
- n = counts[i]
109
- if n < 1_000
110
- d = (n<10)? 2 : (n<100)? 1 : 0
111
- n = (n + counts[i-1]/values[i-1].to_f).round(d)
112
- else
113
- n = n.illion
117
+ scale = SCALE[:base].merge SCALE[type]
118
+ struct = odoread(scale, factors:false) do
119
+ def scale=(scale)
120
+ @scale=scale
121
+ end
122
+ def type=(type)
123
+ @type=type
124
+ end
125
+ def to_s
126
+ number = to_i
127
+ return number.to_s if number < 1_000
128
+ if number < 1_000_000
129
+ precision = (number<10_000)? 2 : (number<100_000)? 1 : 0
130
+ return "#{(number/1000.0).round(precision)}K"
114
131
  end
115
- string = "#{n}#{keys[i][0].upcase}"
116
- break
132
+ keys = @scale.keys.reverse_each
133
+ loop do
134
+ key = keys.next
135
+ n = self[key]
136
+ next if n == 0
137
+ if n < 1_000
138
+ precision = (n<10)? 2 : (n<100)? 1 : 0
139
+ scale = @scale[key].to_f
140
+ f = (number/scale).round(precision)
141
+ return "#{f}#{key[0].upcase}"
142
+ end
143
+ return "#{n.illion}#{key[0].upcase}"
144
+ end
145
+ end
146
+ def to_i
147
+ @scale.to_a.map{|k,v|v*self[k]}.sum
117
148
  end
118
149
  end
119
-
120
- hash = {}
121
- keys.per(counts){|k,v| hash[k]=v}
122
- hash[:to_s] = string
123
-
124
- return hash.to_struct
150
+ struct.type = type
151
+ struct.scale = scale
152
+ return struct
125
153
  end
126
154
  end
127
155
  end
@@ -0,0 +1,42 @@
1
+ module Rafini
2
+ module Requires
3
+ refine ::String do
4
+ # satisfies?:
5
+ # "1.2.3".satisfies?('~>1.1') #=> true
6
+ # "1.2.3".satisfies?('~>2.3') #=> false
7
+ def satisfies?(*reqs)
8
+ Gem::Requirement.new(*reqs).satisfied_by? Gem::Version.new(self)
9
+ end
10
+ end
11
+
12
+ refine ::Kernel do
13
+ def requires(*list)
14
+ loaded = []
15
+ list.each do |gems|
16
+ gems.lines.each do |gemname_reqs|
17
+ gemname, *reqs = gemname_reqs.split
18
+ next unless gemname
19
+ unless reqs.empty?
20
+ case gemname
21
+ when 'rafini'
22
+ unless VERSION.satisfies?(*reqs)
23
+ raise "helpema #{VERSION} not #{reqs.join(', ')}"
24
+ end
25
+ next
26
+ when 'ruby'
27
+ unless RUBY_VERSION.satisfies?(*reqs)
28
+ raise "ruby #{RUBY_VERSION} not #{reqs.join(', ')}"
29
+ end
30
+ next
31
+ else
32
+ gem gemname, *reqs
33
+ end
34
+ end
35
+ require gemname and loaded.push gemname
36
+ end
37
+ end
38
+ return loaded
39
+ end
40
+ end
41
+ end
42
+ end
data/lib/rafini/string.rb CHANGED
@@ -4,17 +4,24 @@ module Rafini
4
4
  # camelize:
5
5
  # 1) A camel kick, as in "I gotz camelized".
6
6
  # 2) "a_camel_kick" => "ACamelKick"
7
- def camelize(sep=/_/)
8
- self.split(sep).map{|word|word.capitalize }.join('')
7
+ def camelize(sep='_')
8
+ self.split(sep).map(&:capitalize).join
9
9
  end
10
10
 
11
11
  # semantic:
12
12
  # 'a.b.c'.semantic(1) #=> 'b'
13
13
  # 'a.b.c'.semantic(0..1) #=> 'a.b'
14
- # 'a.b.c'.semantic(0..2, '/') #=> 'b/c'
15
- # 'a/b/c'.semantic(0..2, '.', /\//) #=> 'a.b.c'
16
- def semantic(v,s='.',sx=/\./)
17
- [*self.split(sx)[v]].join(s)
14
+ # 'a.b.c'.semantic(0..2, join:'/') #=> 'b/c'
15
+ # 'a/b/c'.semantic(0..2, split:'/', join:'.') #=> 'a.b.c'
16
+ def semantic(v=(0..2), split:'.', join:'.')
17
+ [*self.split(split)[v]].join(join)
18
+ end
19
+
20
+ # shellescape:
21
+ # Same funtionality as Shellword's String#shellescape
22
+ def shellescape
23
+ # This is a contraction of Shellwords.escape function
24
+ self.gsub(/[^\w\-.,:+\/@\n]/,'\\\\\\&').gsub(/\n/,"'\n'")
18
25
  end
19
26
  end
20
27
  end
data/lib/rafini.rb CHANGED
@@ -1,12 +1,17 @@
1
1
  module Rafini
2
- VERSION = '2.0.0'
3
- require 'rafini/array'
4
- require 'rafini/exception'
5
- require 'rafini/hash'
6
- require 'rafini/integer'
7
- require 'rafini/string'
8
- require 'rafini/odometers'
9
- require 'rafini/empty'
2
+ VERSION = '3.1.221212'
3
+ # Constants
4
+ autoload :Empty, 'rafini/empty'
5
+ # Pure
6
+ autoload :Array, 'rafini/array'
7
+ autoload :Hash, 'rafini/hash'
8
+ autoload :Integer, 'rafini/integer'
9
+ autoload :String, 'rafini/string'
10
+ # Hybrid
11
+ autoload :Exception, 'rafini/exception'
12
+ # Mix
13
+ autoload :Odometers, 'rafini/odometers'
14
+ autoload :Requires, 'rafini/requires'
10
15
  end
11
16
  # Requires:
12
17
  #`ruby`
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rafini
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 3.1.221212
5
5
  platform: ruby
6
6
  authors:
7
- - carlosjhr64
7
+ - CarlosJHR64
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-15 00:00:00.000000000 Z
11
+ date: 2022-12-12 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: 'Just a collection of useful refinements.
14
14
 
@@ -26,6 +26,7 @@ files:
26
26
  - lib/rafini/hash.rb
27
27
  - lib/rafini/integer.rb
28
28
  - lib/rafini/odometers.rb
29
+ - lib/rafini/requires.rb
29
30
  - lib/rafini/string.rb
30
31
  homepage: https://github.com/carlosjhr64/rafini
31
32
  licenses:
@@ -46,8 +47,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
46
47
  - !ruby/object:Gem::Version
47
48
  version: '0'
48
49
  requirements:
49
- - 'ruby: ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-linux]'
50
- rubygems_version: 3.1.2
50
+ - 'ruby: ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [aarch64-linux]'
51
+ rubygems_version: 3.3.7
51
52
  signing_key:
52
53
  specification_version: 4
53
54
  summary: Just a collection of useful refinements.