lucarecord 0.2.19 → 0.2.24
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 +4 -4
- data/CHANGELOG.md +22 -0
- data/lib/luca_record/base.rb +2 -0
- data/lib/luca_record/dict.rb +36 -50
- data/lib/luca_record/io.rb +28 -3
- data/lib/luca_record/version.rb +1 -1
- data/lib/luca_support.rb +1 -11
- data/lib/luca_support/code.rb +61 -10
- data/lib/luca_support/config.rb +13 -11
- data/lib/luca_support/view.rb +6 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 88e839f7ee18513f3798a568e65bd067eb16b87209f895f1b13a0deb43433837
|
4
|
+
data.tar.gz: 9292a2d9a8e53cab0b5d4aabcea77f5a9a45f8fb504e048ac081a22e86b2ae28
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b6d39c80ffea32406bd009cbd1c3393a8c02c52128ac6365f395ff68af2f1b8964aa1bdd2b7f951dab00aad00a3659bff2883dd839e3179bd013b8e1026185d5
|
7
|
+
data.tar.gz: 8577ad3aee104dc8341b20ec04aae3f18a0145ca826165696f726cce4a2f25661c1205fb79ce2e43d5d361f7c89a449607f8854349c7ea0c6a6a987da1c3aaee
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
## LucaRecord 0.2.24
|
2
|
+
|
3
|
+
* Digit delimiter for `delimit_num` can be customized through `thousands_separator` and `decimal_separator` in config.yml.
|
4
|
+
* Const `CONFIG` and `PJDIR` is defined at `LucaRecord::Base`.
|
5
|
+
* add `LucaSupport::Code.keys_stringify()`
|
6
|
+
|
7
|
+
## LucaRecord 0.2.23
|
8
|
+
|
9
|
+
* Enhance Dictionary, supporting extensible options.
|
10
|
+
|
11
|
+
## LucaRecord 0.2.22
|
12
|
+
|
13
|
+
* add `LucaSupport::View.nushell()`, render nushell table directly.
|
14
|
+
|
15
|
+
## LucaRecord 0.2.21
|
16
|
+
|
17
|
+
* Enhance `LucaSupport::Code.delimit_num()`. Handle with BigDecimal, decimal length & delmiter customization.
|
18
|
+
|
19
|
+
## LucaRecord 0.2.20
|
20
|
+
|
21
|
+
* UUID completion framework on prefix match
|
22
|
+
|
1
23
|
## LucaRecord 0.2.19
|
2
24
|
|
3
25
|
* `LucaSupport::Code.decode_id()`
|
data/lib/luca_record/base.rb
CHANGED
data/lib/luca_record/dict.rb
CHANGED
@@ -6,7 +6,6 @@ require 'yaml'
|
|
6
6
|
require 'pathname'
|
7
7
|
require 'luca_support'
|
8
8
|
|
9
|
-
#
|
10
9
|
# Low level API
|
11
10
|
#
|
12
11
|
module LucaRecord
|
@@ -14,64 +13,40 @@ module LucaRecord
|
|
14
13
|
include LucaSupport::Code
|
15
14
|
|
16
15
|
def initialize(file = @filename)
|
17
|
-
#@path = file
|
18
16
|
@path = self.class.dict_path(file)
|
19
17
|
set_driver
|
20
18
|
end
|
21
19
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
20
|
+
# Search word with n-gram.
|
21
|
+
# If dictionary has Hash or Array, it returns [label, options].
|
22
|
+
#
|
23
|
+
def search(word, default_word = nil, main_key: 'label', options: nil)
|
24
|
+
res, score = max_score_code(word.gsub(/[[:space:]]/, ''))
|
25
|
+
return default_word if score < 0.4
|
26
|
+
|
27
|
+
case res
|
28
|
+
when Hash
|
29
|
+
hash2multiassign(res, main_key, options: options)
|
30
|
+
when Array
|
31
|
+
res.map { |item| hash2multiassign(item, main_key, options: options) }
|
26
32
|
else
|
27
|
-
|
33
|
+
res
|
28
34
|
end
|
29
35
|
end
|
30
36
|
|
37
|
+
# Separate main item from other options.
|
38
|
+
# If options specified as Array of string, it works as safe list filter.
|
31
39
|
#
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
# for double entry data
|
40
|
-
# * debit_value
|
41
|
-
# :credit_label
|
42
|
-
# for double entry data
|
43
|
-
# * credit_value
|
44
|
-
# :note
|
45
|
-
# can be the same column as another label
|
46
|
-
#
|
47
|
-
# :encoding
|
48
|
-
# file encoding
|
49
|
-
#
|
50
|
-
def csv_config
|
51
|
-
{}.tap do |config|
|
52
|
-
if @config.dig('label')
|
53
|
-
config[:label] = @config['label'].to_i
|
54
|
-
if @config.dig('counter_label')
|
55
|
-
config[:counter_label] = @config['counter_label']
|
56
|
-
config[:type] = 'single'
|
57
|
-
end
|
58
|
-
elsif @config.dig('debit_label')
|
59
|
-
config[:debit_label] = @config['debit_label'].to_i
|
60
|
-
if @config.dig('credit_label')
|
61
|
-
config[:credit_label] = @config['credit_label'].to_i
|
62
|
-
config[:type] = 'double'
|
63
|
-
end
|
40
|
+
def hash2multiassign(obj, main_key = 'label', options: nil)
|
41
|
+
options = {}.tap do |opt|
|
42
|
+
obj.map do |k, v|
|
43
|
+
next if k == main_key
|
44
|
+
next if !options.nil? && !options.include?(k)
|
45
|
+
|
46
|
+
opt[k.to_sym] = v
|
64
47
|
end
|
65
|
-
config[:type] ||= 'invalid'
|
66
|
-
config[:debit_value] = @config['debit_value'].to_i if @config.dig('debit_value')
|
67
|
-
config[:credit_value] = @config['credit_value'].to_i if @config.dig('credit_value')
|
68
|
-
config[:note] = @config['note'] if @config.dig('note')
|
69
|
-
config[:encoding] = @config['encoding'] if @config.dig('encoding')
|
70
|
-
|
71
|
-
config[:year] = @config['year'] if @config.dig('year')
|
72
|
-
config[:month] = @config['month'] if @config.dig('month')
|
73
|
-
config[:day] = @config['day'] if @config.dig('day')
|
74
48
|
end
|
49
|
+
[obj[main_key], options.compact]
|
75
50
|
end
|
76
51
|
|
77
52
|
#
|
@@ -120,6 +95,17 @@ module LucaRecord
|
|
120
95
|
end
|
121
96
|
end
|
122
97
|
|
98
|
+
def self.validate(filename, target_key = :label)
|
99
|
+
errors = load(filename).map { |k, v| v[target_key].nil? ? k : nil }.compact
|
100
|
+
if errors.empty?
|
101
|
+
puts 'No error detected.'
|
102
|
+
nil
|
103
|
+
else
|
104
|
+
"Key #{errors.join(', ')} has nil #{target_key}."
|
105
|
+
errors.count
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
123
109
|
private
|
124
110
|
|
125
111
|
def set_driver
|
@@ -129,7 +115,7 @@ module LucaRecord
|
|
129
115
|
end
|
130
116
|
|
131
117
|
def self.dict_path(filename)
|
132
|
-
Pathname(LucaSupport::
|
118
|
+
Pathname(LucaSupport::PJDIR) / 'dict' / filename
|
133
119
|
end
|
134
120
|
|
135
121
|
def self.reverse(dict)
|
@@ -138,7 +124,7 @@ module LucaRecord
|
|
138
124
|
|
139
125
|
def max_score_code(str)
|
140
126
|
res = @definitions.map do |k, v|
|
141
|
-
[v,
|
127
|
+
[v, match_score(str, k, 3)]
|
142
128
|
end
|
143
129
|
res.max { |x, y| x[1] <=> y[1] }
|
144
130
|
end
|
data/lib/luca_record/io.rb
CHANGED
@@ -62,7 +62,7 @@ module LucaRecord # :nodoc:
|
|
62
62
|
def term(start_year, start_month, end_year, end_month, code = nil, basedir = @dirname)
|
63
63
|
return enum_for(:term, start_year, start_month, end_year, end_month, code, basedir) unless block_given?
|
64
64
|
|
65
|
-
LucaSupport::Code.encode_term(start_year, start_month, end_year, end_month).each do |subdir|
|
65
|
+
LucaSupport::Code.encode_term(start_year, start_month, end_year, end_month).each do |subdir|
|
66
66
|
open_records(basedir, subdir, nil, code) do |f, path|
|
67
67
|
if @record_type == 'raw'
|
68
68
|
yield f, path
|
@@ -119,6 +119,31 @@ module LucaRecord # :nodoc:
|
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
122
|
+
# If multiple ID matched, return short ID and human readable label.
|
123
|
+
#
|
124
|
+
def id_completion(phrase, label: 'name', basedir: @dirname)
|
125
|
+
list = prefix_search(phrase, basedir: basedir)
|
126
|
+
case list.length
|
127
|
+
when 1
|
128
|
+
list
|
129
|
+
when 0
|
130
|
+
raise 'No match on specified phrase'
|
131
|
+
else
|
132
|
+
(3..list[0].length).each do |l|
|
133
|
+
if list.map { |id| id[0, l] }.uniq.length == list.length
|
134
|
+
return list.map { |id| { id: id[0, l], label: find(id).dig(label) } }
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def prefix_search(phrase, basedir: @dirname)
|
141
|
+
glob_str = phrase.length <= 3 ? "#{phrase}*/*" : "#{id2path(phrase)}*"
|
142
|
+
Dir.chdir(abs_path(basedir)) do
|
143
|
+
Dir.glob(glob_str).to_a.map! { |path| path.gsub!('/', '') }
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
122
147
|
def prepare_dir!(basedir, date_obj)
|
123
148
|
dir_name = (Pathname(basedir) + LucaSupport::Code.encode_dirname(date_obj)).to_s
|
124
149
|
FileUtils.mkdir_p(dir_name) unless Dir.exist?(dir_name)
|
@@ -218,7 +243,7 @@ module LucaRecord # :nodoc:
|
|
218
243
|
end
|
219
244
|
|
220
245
|
# test if having required dirs/files under exec path
|
221
|
-
def valid_project?(path = LucaSupport::
|
246
|
+
def valid_project?(path = LucaSupport::PJDIR)
|
222
247
|
project_dir = Pathname(path)
|
223
248
|
FileTest.file?((project_dir + 'config.yml').to_s) and FileTest.directory?( (project_dir + 'data').to_s)
|
224
249
|
end
|
@@ -337,7 +362,7 @@ module LucaRecord # :nodoc:
|
|
337
362
|
|
338
363
|
# TODO: replace with data_dir method
|
339
364
|
def abs_path(base_dir)
|
340
|
-
Pathname(LucaSupport::
|
365
|
+
Pathname(LucaSupport::PJDIR) / 'data' / base_dir
|
341
366
|
end
|
342
367
|
|
343
368
|
# true when file doesn't have record on code
|
data/lib/luca_record/version.rb
CHANGED
data/lib/luca_support.rb
CHANGED
@@ -2,18 +2,8 @@
|
|
2
2
|
|
3
3
|
module LucaSupport
|
4
4
|
autoload :Code, 'luca_support/code'
|
5
|
+
autoload :CONFIG, 'luca_support/config'
|
5
6
|
autoload :Config, 'luca_support/config'
|
6
7
|
autoload :Mail, 'luca_support/mail'
|
7
8
|
autoload :View, 'luca_support/view'
|
8
|
-
|
9
|
-
def self.match_score(a, b, n = 2)
|
10
|
-
v_a = to_ngram(a, n)
|
11
|
-
v_b = to_ngram(b, n)
|
12
|
-
|
13
|
-
v_a.map { |item| v_b.include?(item) ? 1 : 0 }.sum / v_a.length.to_f
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.to_ngram(str, n = 2)
|
17
|
-
str.each_char.each_cons(n).map(&:join)
|
18
|
-
end
|
19
9
|
end
|
data/lib/luca_support/code.rb
CHANGED
@@ -48,8 +48,30 @@ module LucaSupport
|
|
48
48
|
'0123456789ABCDEFGHIJKLMNOPQRSTUV'.index(char)
|
49
49
|
end
|
50
50
|
|
51
|
-
|
52
|
-
|
51
|
+
# Format number in 3-digit-group.
|
52
|
+
# Long decimal is just ommitted with floor().
|
53
|
+
#
|
54
|
+
def delimit_num(num, decimal: nil, delimiter: nil)
|
55
|
+
return nil if num.nil?
|
56
|
+
|
57
|
+
decimal ||= LucaSupport::CONFIG['decimal_num']
|
58
|
+
delimiter ||= LucaSupport::CONFIG['thousands_separator']
|
59
|
+
case num
|
60
|
+
when BigDecimal
|
61
|
+
if decimal == 0
|
62
|
+
num.floor.to_s.reverse.gsub!(/(\d{3})(?=\d)/, '\1 ').reverse!
|
63
|
+
.gsub!(/\s/, delimiter)
|
64
|
+
else
|
65
|
+
fragments = num.floor(decimal).to_s('F').split('.')
|
66
|
+
fragments[0].reverse!.gsub!(/(\d{3})(?=\d)/, '\1 ').reverse!
|
67
|
+
.gsub!(/\s/, delimiter)
|
68
|
+
fragments[1].gsub!(/(\d{3})(?=\d)/, '\1 ')
|
69
|
+
fragments.join(LucaSupport::CONFIG['decimal_separator'])
|
70
|
+
end
|
71
|
+
else
|
72
|
+
num.to_s.reverse.gsub!(/(\d{3})(?=\d)/, '\1 ').reverse!
|
73
|
+
.gsub!(/\s/, delimiter)
|
74
|
+
end
|
53
75
|
end
|
54
76
|
|
55
77
|
# encode directory name from year and month.
|
@@ -82,7 +104,7 @@ module LucaSupport
|
|
82
104
|
(start_year..end_year).to_a.map do |y|
|
83
105
|
g1 = y == start_year ? encode_month(start_month) : encode_month(1)
|
84
106
|
g2 = y == end_year ? encode_month(end_month) : encode_month(12)
|
85
|
-
"#{y}[#{g1}-#{g2}]"
|
107
|
+
g1 == g2 ? "#{y}#{g1}" : "#{y}[#{g1}-#{g2}]"
|
86
108
|
end
|
87
109
|
end
|
88
110
|
|
@@ -95,6 +117,31 @@ module LucaSupport
|
|
95
117
|
Digest::SHA1.hexdigest(SecureRandom.uuid)
|
96
118
|
end
|
97
119
|
|
120
|
+
# Convert Hash keys to string recursively.
|
121
|
+
# Required for YAML compatibility.
|
122
|
+
#
|
123
|
+
def keys_stringify(dat)
|
124
|
+
case dat
|
125
|
+
when Array
|
126
|
+
dat.map { |d| keys_stringify(d) }
|
127
|
+
when Hash
|
128
|
+
dat.map { |k, v| [k.to_s, keys_stringify(v)] }.to_h
|
129
|
+
else
|
130
|
+
dat
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def match_score(a, b, n = 2)
|
135
|
+
v_a = to_ngram(a, n)
|
136
|
+
v_b = to_ngram(b, n)
|
137
|
+
|
138
|
+
v_a.map { |item| v_b.include?(item) ? 1 : 0 }.sum / v_a.length.to_f
|
139
|
+
end
|
140
|
+
|
141
|
+
def to_ngram(str, n = 2)
|
142
|
+
str.each_char.each_cons(n).map(&:join)
|
143
|
+
end
|
144
|
+
|
98
145
|
def decimalize(obj)
|
99
146
|
case obj.class.name
|
100
147
|
when 'Array'
|
@@ -112,15 +159,19 @@ module LucaSupport
|
|
112
159
|
end
|
113
160
|
end
|
114
161
|
|
115
|
-
def readable(obj, len = LucaSupport::
|
116
|
-
case obj
|
117
|
-
when
|
162
|
+
def readable(obj, len = LucaSupport::CONFIG['decimal_num'])
|
163
|
+
case obj
|
164
|
+
when Array
|
118
165
|
obj.map { |i| readable(i) }
|
119
|
-
when
|
166
|
+
when Hash
|
120
167
|
obj.inject({}) { |h, (k, v)| h[k] = readable(v); h }
|
121
|
-
when
|
122
|
-
|
123
|
-
|
168
|
+
when BigDecimal
|
169
|
+
if len == 0
|
170
|
+
obj.round # Integer is precise
|
171
|
+
else
|
172
|
+
parts = obj.round(len).to_s('F').split('.')
|
173
|
+
"#{parts[0]}.#{parts[1][0, len]}"
|
174
|
+
end
|
124
175
|
else
|
125
176
|
obj
|
126
177
|
end
|
data/lib/luca_support/config.rb
CHANGED
@@ -3,18 +3,20 @@
|
|
3
3
|
require 'pathname'
|
4
4
|
require 'yaml'
|
5
5
|
|
6
|
-
#
|
7
6
|
# startup config
|
8
7
|
#
|
9
8
|
module LucaSupport
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
9
|
+
PJDIR = ENV['LUCA_TEST_DIR'] || Dir.pwd.freeze
|
10
|
+
CONFIG = begin
|
11
|
+
{
|
12
|
+
'decimal_separator' => '.',
|
13
|
+
'thousands_separator' => ','
|
14
|
+
}.merge(YAML.load_file(Pathname(PJDIR) / 'config.yml'))
|
15
|
+
rescue Errno::ENOENT
|
16
|
+
{
|
17
|
+
'decimal_separator' => '.',
|
18
|
+
'thousands_separator' => ','
|
19
|
+
}
|
20
|
+
end
|
21
|
+
CONFIG['decimal_num'] ||= CONFIG['country'] == 'jp' ? 0 : 2
|
20
22
|
end
|
data/lib/luca_support/view.rb
CHANGED
@@ -36,11 +36,16 @@ module LucaSupport
|
|
36
36
|
|
37
37
|
def search_template(file, dir = 'templates')
|
38
38
|
# TODO: load config
|
39
|
-
[
|
39
|
+
[LucaSupport::PJDIR, lib_path].each do |base|
|
40
40
|
path = (Pathname(base) / dir / file)
|
41
41
|
return path.to_path if path.file?
|
42
42
|
end
|
43
43
|
nil
|
44
44
|
end
|
45
|
+
|
46
|
+
def nushell(yml)
|
47
|
+
require 'open3'
|
48
|
+
Open3.pipeline_w(%(nu -c 'cat - | from yaml')) { |stdin| stdin.puts yml }
|
49
|
+
end
|
45
50
|
end
|
46
51
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lucarecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.24
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chuma Takahiro
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-11-
|
11
|
+
date: 2020-11-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mail
|