Module: LucaSupport::Code

Included in:
LucaRecord::Dict, LucaRecord::IO
Defined in:
lib/luca_support/code.rb

Overview

implement Luca IDs convention

Class Method Summary collapse

Class Method Details

.decimalize(obj) ⇒ Object



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/luca_support/code.rb', line 147

def decimalize(obj)
  case obj.class.name
  when 'Array'
    obj.map { |i| decimalize(i) }
  when 'Hash'
    obj.inject({}) { |h, (k, v)| h[k] = decimalize(v); h }
  when 'Integer'
    BigDecimal(obj.to_s)
  when 'String'
    return obj if /^0[0-9]+$/.match(obj) # zero-prefixed code
    /^[0-9\.]+$/.match(obj) ? BigDecimal(obj) : obj
  when 'Float'
    raise 'already float'
  else
    obj
  end
end

.decode_date(char) ⇒ Object



48
49
50
# File 'lib/luca_support/code.rb', line 48

def decode_date(char)
  '0123456789ABCDEFGHIJKLMNOPQRSTUV'.index(char)
end

.decode_id(id_str) ⇒ Object

Parse historical id into Array of date & transaction id.



16
17
18
19
# File 'lib/luca_support/code.rb', line 16

def decode_id(id_str)
  m = %r(^(?<year>[0-9]+)(?<month>[A-L])/?(?<day>[0-9A-V])(?<txid>[0-9A-Z]{,3})).match(id_str)
  ["#{m[:year]}-#{decode_month(m[:month])}-#{decode_date(m[:day])}", decode_txid(m[:txid])]
end

.decode_month(char) ⇒ Object



98
99
100
# File 'lib/luca_support/code.rb', line 98

def decode_month(char)
  '0ABCDEFGHIJKL'.index(char)
end

.decode_term(char) ⇒ Object



112
113
114
115
# File 'lib/luca_support/code.rb', line 112

def decode_term(char)
  m = /^([0-9]{4})([A-La-l])/.match(char)
  [m[1].to_i, decode_month(m[2])]
end

.decode_txid(id) ⇒ Object



27
28
29
30
31
# File 'lib/luca_support/code.rb', line 27

def decode_txid(id)
  txmap = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  l = txmap.length
  txmap.index(id[0]) * (l**2) + txmap.index(id[1]) * l + txmap.index(id[2])
end

.delimit_num(num, decimal: nil, delimiter: nil) ⇒ Object

Format number in 3-digit-group. Long decimal is just ommitted with floor().



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/luca_support/code.rb', line 55

def delimit_num(num, decimal: nil, delimiter: nil)
  return nil if num.nil?

  decimal ||= CONST.config['decimal_num']
  delimiter ||= CONST.config['thousands_separator']
  case num
  when BigDecimal
    if decimal == 0
      num.floor.to_s.reverse!.gsub(/(\d{3})(?=\d)/, '\1 ').reverse!
        .gsub(/\s/, delimiter)
    else
      fragments = num.floor(decimal).to_s('F').split('.')
      fragments[0].reverse!.gsub!(/(\d{3})(?=\d)/, '\1 ')
      fragments[0].reverse!.gsub!(/\s/, delimiter)
      fragments[1].gsub!(/(\d{3})(?=\d)/, '\1 ')
      fragments.join(CONST.config['decimal_separator'])
    end
  else
    num.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\1 ').reverse!
      .gsub(/\s/, delimiter)
  end
end

.encode_date(date) ⇒ Object

Day of month to code conversion. Date, DateTime, String, Integer is valid input. If nil, returns empty String for consistency.



37
38
39
40
41
42
43
44
45
46
# File 'lib/luca_support/code.rb', line 37

def encode_date(date)
  return '' if date.nil?

  index = date.day if date.respond_to?(:day)
  index ||= date.to_i if date.respond_to?(:to_i)
  index ||= 0
  raise 'Invalid date specified' if index < 1 || index > 31

  '0123456789ABCDEFGHIJKLMNOPQRSTUV'[index]
end

.encode_dirname(date_obj) ⇒ Object

encode directory name from year and month.



80
81
82
# File 'lib/luca_support/code.rb', line 80

def encode_dirname(date_obj)
  date_obj.year.to_s + encode_month(date_obj)
end

.encode_month(date) ⇒ Object

Month to code conversion. Date, DateTime, String, Integer is valid input. If nil, returns empty String for consistency.



87
88
89
90
91
92
93
94
95
96
# File 'lib/luca_support/code.rb', line 87

def encode_month(date)
  return '' if date.nil?

  index = date.month if date.respond_to?(:month)
  index ||= date.to_i if date.respond_to?(:to_i)
  index ||= 0
  raise 'Invalid month specified' if index < 1 || index > 12

  '0ABCDEFGHIJKL'[index]
end

.encode_term(start_year, start_month, end_year, end_month) ⇒ Object

Generate globbing phrase like [“2020”] for range search.



104
105
106
107
108
109
110
# File 'lib/luca_support/code.rb', line 104

def encode_term(start_year, start_month, end_year, end_month)
  (start_year..end_year).to_a.map do |y|
    g1 = y == start_year ? encode_month(start_month) : encode_month(1)
    g2 = y == end_year ? encode_month(end_month) : encode_month(12)
    g1 == g2 ? "#{y}#{g1}" : "#{y}[#{g1}-#{g2}]"
  end
end

.encode_txid(num) ⇒ Object



21
22
23
24
25
# File 'lib/luca_support/code.rb', line 21

def encode_txid(num)
  txmap = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  l = txmap.length
  txmap[num / (l**2)] + txmap[(num % (l**2)) / l] + txmap[num % l]
end

.has_status?(dat, status) ⇒ Boolean

Returns:

  • (Boolean)


235
236
237
238
239
240
# File 'lib/luca_support/code.rb', line 235

def has_status?(dat, status)
  return false if dat['status'].nil?

  dat['status'].map { |h| h.key?(status) }
    .include?(true)
end

.issue_random_idObject



117
118
119
# File 'lib/luca_support/code.rb', line 117

def issue_random_id
  Digest::SHA1.hexdigest(SecureRandom.uuid)
end

.keys_stringify(dat) ⇒ Object

Convert Hash keys to string recursively. Required for YAML compatibility.



124
125
126
127
128
129
130
131
132
133
# File 'lib/luca_support/code.rb', line 124

def keys_stringify(dat)
  case dat
  when Array
    dat.map { |d| keys_stringify(d) }
  when Hash
    dat.map { |k, v| [k.to_s, keys_stringify(v)] }.to_h
  else
    dat
  end
end

.match_score(a, b, n = 2) ⇒ Object



135
136
137
138
139
140
141
# File 'lib/luca_support/code.rb', line 135

def match_score(a, b, n = 2)
  split_factor = [a.length, b.length, n].min
  v_a = to_ngram(a, split_factor)
  v_b = to_ngram(b, split_factor)

  v_a.map { |item| v_b.include?(item) ? 1 : 0 }.sum / v_a.length.to_f
end

.parse_current(dat, date = @date) ⇒ Object

convert effective/defunct data into current hash on @date. not parse nested children.



186
187
188
189
190
# File 'lib/luca_support/code.rb', line 186

def parse_current(dat, date = @date)
  {}.tap do |processed|
    dat.each { |k, _v| processed[k] = take_current(dat, k, date) }
  end
end

.readable(obj, len = ) ⇒ Object



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/luca_support/code.rb', line 165

def readable(obj, len = CONST.config['decimal_num'])
  case obj
  when Array
    obj.map { |i| readable(i) }
  when Hash
    obj.inject({}) { |h, (k, v)| h[k] = readable(v); h }
  when BigDecimal
    if len == 0
      obj.round # Integer is precise
    else
      parts = obj.round(len).to_s('F').split('.')
      "#{parts[0]}.#{parts[1][0, len]}"
    end
  else
    obj
  end
end

.take_current(dat, item, date = @date) ⇒ Object

return current value with effective/defunct on target @date For multiple attribues, return hash on other than ‘val’. Examples:

- effective: 2020-1-1
  val: 3000
=> 3000

- effective: 2020-1-1
  rank: 5
  point: 1000
=> { 'effective' => 2020-1-1, 'rank' => 5, 'point' => 1000 }

- defunct: 2020-1-1
  val: 3000
=> nil


208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/luca_support/code.rb', line 208

def take_current(dat, item, date = @date)
  target = dat&.dig(item)
  return target unless target.is_a?(Array)

  keys = target.map(&:keys).flatten
  return target if !keys.include?('effective') && !keys.include?('defunct')

  latest = target
             .reject { |a| a['defunct'] && Date.parse(a['defunct'].to_s) < date  }
             .filter { |a| a['effective'] && Date.parse(a['effective'].to_s) < date }
             .max { |a, b| Date.parse(a['effective'].to_s) <=> Date.parse(b['effective'].to_s) }

  latest&.dig('val') || latest
end

.take_history(dat, item) ⇒ Object

convert all effective/defunct data into Array not parse nested children.



226
227
228
229
230
231
232
233
# File 'lib/luca_support/code.rb', line 226

def take_history(dat, item)
  target = dat&.dig(item)
  return Array(target) unless target.is_a?(Array)

  target
    .sort { |a, b| Date.parse(a['effective'].to_s) <=> Date.parse(b['effective'].to_s) }
    .map { |a| a['val'] }
end

.to_ngram(str, n = 2) ⇒ Object



143
144
145
# File 'lib/luca_support/code.rb', line 143

def to_ngram(str, n = 2)
  str.each_char.each_cons(n).map(&:join)
end