maprename 0.1.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 +11 -0
- data/.rspec +3 -0
- data/.travis.yml +6 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +49 -0
- data/LICENSE.txt +21 -0
- data/README.md +126 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/maprename +5 -0
- data/lib/maprename/app.rb +22 -0
- data/lib/maprename/cli.rb +45 -0
- data/lib/maprename/content_parser.rb +38 -0
- data/lib/maprename/file_name_parser.rb +32 -0
- data/lib/maprename/mapping_parser.rb +40 -0
- data/lib/maprename/renamer.rb +58 -0
- data/lib/maprename/version.rb +3 -0
- data/lib/maprename.rb +10 -0
- data/maprename.gemspec +27 -0
- data/maprename.yml +47 -0
- metadata +81 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b113974592630108fa3d0063ab010d20857e85fd80781fbf6c00de7ef7fb1d76
|
4
|
+
data.tar.gz: 83f7fc257838d469896ed9d3fa568ffae88a087635840705c207e97cab5b86bc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: '08c13702e2e2d94a766ad23c08fe1eb9e416fca04cb89709c92942380d9ff37f7a565c6a27acf59d874ced1e0b90680ba9684be184ef695f8ba40bade24aa712'
|
7
|
+
data.tar.gz: 18d43c7b2f84951f384a16346633da9185807be18697edc7634a00152c00dd47051f6353549df40db916459fb482ecb784c493a266add774831e6333a6f2146a
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at liuxiang@ktjr.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [https://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: https://contributor-covenant.org
|
74
|
+
[version]: https://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
maprename (0.1.0)
|
5
|
+
activesupport
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activesupport (6.0.0)
|
11
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
12
|
+
i18n (>= 0.7, < 2)
|
13
|
+
minitest (~> 5.1)
|
14
|
+
tzinfo (~> 1.1)
|
15
|
+
zeitwerk (~> 2.1, >= 2.1.8)
|
16
|
+
concurrent-ruby (1.1.5)
|
17
|
+
diff-lcs (1.3)
|
18
|
+
i18n (1.6.0)
|
19
|
+
concurrent-ruby (~> 1.0)
|
20
|
+
minitest (5.12.2)
|
21
|
+
rake (12.3.3)
|
22
|
+
rspec (3.8.0)
|
23
|
+
rspec-core (~> 3.8.0)
|
24
|
+
rspec-expectations (~> 3.8.0)
|
25
|
+
rspec-mocks (~> 3.8.0)
|
26
|
+
rspec-core (3.8.2)
|
27
|
+
rspec-support (~> 3.8.0)
|
28
|
+
rspec-expectations (3.8.4)
|
29
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
30
|
+
rspec-support (~> 3.8.0)
|
31
|
+
rspec-mocks (3.8.1)
|
32
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
33
|
+
rspec-support (~> 3.8.0)
|
34
|
+
rspec-support (3.8.2)
|
35
|
+
thread_safe (0.3.6)
|
36
|
+
tzinfo (1.2.5)
|
37
|
+
thread_safe (~> 0.1)
|
38
|
+
zeitwerk (2.1.10)
|
39
|
+
|
40
|
+
PLATFORMS
|
41
|
+
ruby
|
42
|
+
|
43
|
+
DEPENDENCIES
|
44
|
+
maprename!
|
45
|
+
rake (~> 12.0)
|
46
|
+
rspec (~> 3.0)
|
47
|
+
|
48
|
+
BUNDLED WITH
|
49
|
+
2.1.2
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2020 Liu Xiang
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
## Installation
|
2
|
+
|
3
|
+
```
|
4
|
+
$ sudo gem install maprename
|
5
|
+
```
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
```
|
10
|
+
Usage: maprename [options]
|
11
|
+
-c, --config CONFIG_FILE Specify config file, defaults to `maprename.yml' in current directory
|
12
|
+
-d, --dry dry run: only print generated file copy commands,
|
13
|
+
not execute the generated commands, use this option
|
14
|
+
for debugging or validating config file
|
15
|
+
-h, --help Prints this help
|
16
|
+
```
|
17
|
+
|
18
|
+
## Config file example
|
19
|
+
|
20
|
+
```yaml
|
21
|
+
input:
|
22
|
+
directory: ./tmp/in/
|
23
|
+
pattern: "(.*).txt"
|
24
|
+
source: "$1.pdf"
|
25
|
+
name_parse:
|
26
|
+
method: split
|
27
|
+
pattern: "[-_]"
|
28
|
+
fields:
|
29
|
+
- name: customer_name
|
30
|
+
value: "$1"
|
31
|
+
- name: raw_trans_no
|
32
|
+
value: "$2"
|
33
|
+
name_parse:
|
34
|
+
method: scan
|
35
|
+
pattern: L(.*)
|
36
|
+
fields:
|
37
|
+
- name: trans_no
|
38
|
+
value: "$1"
|
39
|
+
content_parse:
|
40
|
+
encoding: UTF-8
|
41
|
+
fields:
|
42
|
+
- name: address
|
43
|
+
pattern: "您的地址 ([^::]+)"
|
44
|
+
value: "$1"
|
45
|
+
- name: trade_date
|
46
|
+
pattern: "交易日期:([0-9-]+)"
|
47
|
+
value: "$1"
|
48
|
+
|
49
|
+
mapping:
|
50
|
+
file: ./tmp/in/mapping.csv
|
51
|
+
encoding: UTF-8
|
52
|
+
column_separator: ","
|
53
|
+
first_line_as_column_defination: false
|
54
|
+
columns:
|
55
|
+
- name: trans_id
|
56
|
+
index: 1
|
57
|
+
- name: account_no
|
58
|
+
index: 2
|
59
|
+
select:
|
60
|
+
- keyword_column: trans_id
|
61
|
+
keyword_value: trans_no
|
62
|
+
select_column: account_no
|
63
|
+
name: acct_no
|
64
|
+
|
65
|
+
output:
|
66
|
+
directory: ./tmp/out/
|
67
|
+
filename: "#{trade_date}/#{customer_name}-#{acct_no}-#{address}.pdf"
|
68
|
+
```
|
69
|
+
|
70
|
+
## Config file specification
|
71
|
+
|
72
|
+
+ `input`: 和输入文件相关的配置
|
73
|
+
+ `directory`: 指定输入文件所在的目录
|
74
|
+
+ `pattern`: 用于在匹配过滤输入文件的正则表达式, 其中的正则表达式分组可以用于 `source` 字段的值的生成
|
75
|
+
+ `source`: 指定将被拷贝的文件
|
76
|
+
+ `name_parse`: 关于文件名解析的规则配置
|
77
|
+
+ `method`: 文件名拆分方式,`split`: 使用指定的分割符分割,`scan`:使用正则表达式进行正则分组匹配
|
78
|
+
+ `pattern`: 对于 `split` 此字段为分割符,支持正则表达式, 对于 `scan` 此字段为用于分组匹配的正则表达式
|
79
|
+
+ `fields`: 定义要从文件名拆分出的字段
|
80
|
+
+ `name`: 将字段值存储到以 `name` 值为名称的变量中,在 `output.filename` 中可以使用 `#{变量名}` 的方法获取此变量的值
|
81
|
+
+ `value`: 字段值的生成规则,对于 `split` 方式 `$N` 指分割出的数组的第 N 个元素(索引从 1 开始), 对于 `scan` 方法, `$N` 为第 N 个正则分组匹配的值
|
82
|
+
+ `name_parse` 是一个递归嵌套的结构,可以对拆分出的外层字段,进一步配置 `name_parse` 使得进一步对字段进行拆分
|
83
|
+
+ `content_parse`: 若需要从文件内容中抽取值,使用此字段对内容抽取规则进行配置
|
84
|
+
+ `encoding`: 文件内容的字符编码,默认值为 `UTF-8`, 若文件内容为乱码可以尝试配置此字段的值,常用的值有: `UTF-8`, `GBK`, `UTF-16LE`, `UTF-16BE`
|
85
|
+
+ `fields`: 定义要从文件内容中抽取的值
|
86
|
+
+ `name`: 将抽取到的值存储到以 `name` 值为名称的变量中,在 `output.filename` 中可以使用 `#{变量名}` 的方法获取此变量的值
|
87
|
+
+ `pattern`: 用于对文件内容进行匹配的正则表达式,其中的正则表达式分组可以用于下面 `value` 字段的值的生成
|
88
|
+
+ `value`: 值的生成规则,`$N` 为第 N 个正则分组匹配的值
|
89
|
+
+ `mapping`: 映射文件相关的配置, 仅支持 CSV 格式
|
90
|
+
+ `file`: 映射文件路径
|
91
|
+
+ `encoding`: 映射文件内容的字符编码,默认值为 `UTF-8`, 若文件内容为乱码可以尝试配置此字段的值,常用的值有: `UTF-8`, `GBK`, `UTF-16LE`, `UTF-16BE`
|
92
|
+
+ `column_separator`: CSV 的字段分割符, 默认为水平制表符 `\t`
|
93
|
+
+ `first_line_as_column_defination`: 是否使用 CSV 文件的第一行作为字段的名称, 若设置此字段为 `true`, 将忽略下面 `columns` 配置
|
94
|
+
+ `columns`: 关于映射文件字段的规则定义
|
95
|
+
+ `name`: 给字段定义一个名称
|
96
|
+
+ `index`: 字段的列号(索引从 1 开始)
|
97
|
+
+ `select`: 定义映射规则
|
98
|
+
+ `keyword_column`: 使用映射文件中的哪个字段进行匹配比较
|
99
|
+
+ `keyword_value`: 使用哪个变量的值来和 `keyword_column` 所指定的映射文件中的值进行匹配比较
|
100
|
+
+ `select_column`: 匹配到映射的行之后,用哪个字段的值作为要存储在 `name` 所指定的变量中的值
|
101
|
+
+ `name`: 要存储到变量的名称, 在 `output.filename` 中可以使用 `#{变量名}` 的方法获取此变量的值
|
102
|
+
+ `output`: 和输出文件相关的配置
|
103
|
+
+ `directory`: 输出的目录
|
104
|
+
+ `filename`: 输出文件名的生成规则,其中可以包含字符串字面量和 `#{变量名}` 形式的变量取值
|
105
|
+
|
106
|
+
## About YAML
|
107
|
+
|
108
|
+
YAML 是一种常用的配置文件格式,它以不同层次的缩进来表示配置项之间的从属关系和结构,缩紧所使用的空白字符个数必须是规范的:同一级配置项的缩紧必须相同,第 N 层和第 N+1 层之间的缩进差别必须等于第 N+1 层和 第 N+2 层之间的缩紧差别,一个缩进递进中,推荐使用两个空格作为缩进
|
109
|
+
|
110
|
+
以减号开头的配置项表示它的上一级配置项的值是数组类型,每个减号表示数组中的一个元素
|
111
|
+
|
112
|
+
See also: Official YAML specification: https://yaml.org/spec/1.2/spec.html
|
113
|
+
|
114
|
+
## Involve with Regular Expression
|
115
|
+
|
116
|
+
正则表达式30分钟入门教程: https://deerchao.cn/tutorials/regex/regex.htm
|
117
|
+
|
118
|
+
文中,在学习过程中使用一个 Windows 上的正则表达式测试器来帮助理解,Mac 上可以使用这个 Web 版的正则表达式测试器: https://deerchao.cn/tools/wegester/
|
119
|
+
|
120
|
+
## License
|
121
|
+
|
122
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
123
|
+
|
124
|
+
## Code of Conduct
|
125
|
+
|
126
|
+
Everyone interacting in the Maprename project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/lululau/maprename/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "maprename"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/exe/maprename
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'active_support/all'
|
3
|
+
require "maprename/renamer"
|
4
|
+
|
5
|
+
module Maprename
|
6
|
+
class App
|
7
|
+
def initialize(config_file)
|
8
|
+
@config = YAML.load(IO.read(config_file)).with_indifferent_access
|
9
|
+
end
|
10
|
+
|
11
|
+
def run!(dry = false)
|
12
|
+
input_files.each do |file|
|
13
|
+
file = File.join(@config[:input][:directory], file)
|
14
|
+
Maprename::Renamer.new(file, @config).rename!(dry)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def input_files
|
19
|
+
Dir.children(@config[:input][:directory]).grep(Regexp.new(@config[:input][:pattern]))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'maprename/app'
|
3
|
+
|
4
|
+
module Maprename
|
5
|
+
class Cli
|
6
|
+
def initialize
|
7
|
+
@options = {}
|
8
|
+
parse_options!
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse_options!
|
12
|
+
@raw_options = OptionParser.new do |opts|
|
13
|
+
opts.banner = "Usage: maprename [options]"
|
14
|
+
|
15
|
+
opts.on("-c", "--config CONFIG_FILE", "Specify config file, default to maprename.yml in current directory, see specification: https://github.com/lululau/maprename/blob/master/README.md") do |config|
|
16
|
+
@options[:config] = config
|
17
|
+
end
|
18
|
+
|
19
|
+
opts.on("-d", "--dry", "dry run") do
|
20
|
+
@options[:dry] = true
|
21
|
+
end
|
22
|
+
|
23
|
+
opts.on("-h", "--help", "Prints this help") do
|
24
|
+
puts opts
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
@raw_options.parse!
|
30
|
+
end
|
31
|
+
|
32
|
+
def config_file
|
33
|
+
@options[:config] || "maprename.yml"
|
34
|
+
end
|
35
|
+
|
36
|
+
def run!
|
37
|
+
config = config_file
|
38
|
+
unless File.exists?(config)
|
39
|
+
puts @raw_options
|
40
|
+
exit 1
|
41
|
+
end
|
42
|
+
Maprename::App.new(config_file).run!(@options[:dry])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Maprename
|
2
|
+
class ContentParser
|
3
|
+
|
4
|
+
attr_accessor :config
|
5
|
+
|
6
|
+
def initialize(config, file)
|
7
|
+
@config = config
|
8
|
+
@file = file
|
9
|
+
@content = read_file_content(file)
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse!(context)
|
13
|
+
config[:fields].each do |field_definition|
|
14
|
+
fields = @content.match(Regexp.new(field_definition[:pattern])).to_a[1..-1]
|
15
|
+
eval_var_definition(fields, field_definition, context)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def eval_var_definition(fields, field_definition, context)
|
20
|
+
value = field_definition[:value].gsub(/\$(\d+)/, fields[$1.to_i - 1])
|
21
|
+
context.instance_eval "self.#{field_definition[:name]} = #{value.inspect}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def read_file_content(file)
|
25
|
+
encoding = config[:encoding] || 'UTF-8'
|
26
|
+
content = IO.read(file, encoding: encoding)
|
27
|
+
if config[:pre_processor]
|
28
|
+
IO.popen(config[:pre_processor]) do |pipe|
|
29
|
+
pipe.write(content)
|
30
|
+
pipe.close_write
|
31
|
+
pipe.read
|
32
|
+
end
|
33
|
+
else
|
34
|
+
content
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Maprename
|
2
|
+
class FileNameParser
|
3
|
+
def initialize(config)
|
4
|
+
@config = config
|
5
|
+
end
|
6
|
+
|
7
|
+
def parse!(subject, context)
|
8
|
+
fields = if split_method?
|
9
|
+
subject.split(Regexp.new(@config[:pattern]))
|
10
|
+
else
|
11
|
+
subject.match(Regexp.new(@config[:pattern])).to_a[1..-1]
|
12
|
+
end
|
13
|
+
|
14
|
+
@config[:fields].each do |field_definition|
|
15
|
+
eval_var_definition(fields, field_definition, context)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def eval_var_definition(fields, field_definition, context)
|
20
|
+
value = field_definition[:value].gsub(/\$(\d+)/) { fields[$1.to_i - 1] }
|
21
|
+
context.instance_eval "self.#{field_definition[:name]} = #{value.inspect}"
|
22
|
+
|
23
|
+
if parse_config = field_definition[:name_parse]
|
24
|
+
Maprename::FileNameParser.new(parse_config).parse!(context.instance_eval(field_definition[:name]), context)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def split_method?
|
29
|
+
@config[:method] == 'split'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Maprename
|
2
|
+
class MappingParser
|
3
|
+
|
4
|
+
attr_accessor :config
|
5
|
+
|
6
|
+
def initialize(config)
|
7
|
+
@config = config
|
8
|
+
@content = build_content
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse!(context)
|
12
|
+
@config[:select].each do |field_definition|
|
13
|
+
keyword_value = context.instance_eval(field_definition[:keyword_value])
|
14
|
+
row = @content.find { |row| row[field_definition[:keyword_column]] == keyword_value }
|
15
|
+
context.instance_eval("self.%s = %s" % [field_definition[:name], row[field_definition[:select_column]].inspect])
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def build_content
|
20
|
+
encoding = config[:encoding] || 'UTF-8'
|
21
|
+
separator = config[:column_separator] || "\t"
|
22
|
+
content = IO.readlines(config[:file], encoding: encoding).map(&:chomp)
|
23
|
+
if config[:first_line_as_column_defination]
|
24
|
+
config[:columns] = content.first.split(separator).each_with_index.map do |(c, i)|
|
25
|
+
{
|
26
|
+
name: c,
|
27
|
+
index: i+1
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
content.map do |raw_row|
|
33
|
+
fields = raw_row.split(separator)
|
34
|
+
config[:columns].each_with_object({}) do |column, result|
|
35
|
+
result[column[:name]] = fields[column[:index] - 1]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'fileutils'
|
3
|
+
require "maprename/mapping_parser"
|
4
|
+
require "maprename/content_parser"
|
5
|
+
require "maprename/file_name_parser"
|
6
|
+
|
7
|
+
module Maprename
|
8
|
+
class Renamer
|
9
|
+
def initialize(file, config)
|
10
|
+
@context = OpenStruct.new
|
11
|
+
@file = file
|
12
|
+
@config = config
|
13
|
+
|
14
|
+
parse_config!
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse_config!
|
18
|
+
@config[:input][:name_parse].try do |parse_config|
|
19
|
+
Maprename::FileNameParser.new(parse_config).parse!(input_basename, @context)
|
20
|
+
end
|
21
|
+
|
22
|
+
@config[:input][:content_parse].try do |parse_config|
|
23
|
+
Maprename::ContentParser.new(parse_config, @file).parse!(@context)
|
24
|
+
end
|
25
|
+
|
26
|
+
@config[:mapping].try do |mapping_config|
|
27
|
+
Maprename::MappingParser.new(mapping_config).parse!(@context)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def rename!(dry)
|
32
|
+
if dry
|
33
|
+
puts "mkdir -p %s" % File.dirname(destination)
|
34
|
+
puts "cp %s %s" % [source, destination]
|
35
|
+
else
|
36
|
+
FileUtils.mkdir_p(File.dirname(destination))
|
37
|
+
FileUtils.copy_file(source, destination)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def input_dirname
|
42
|
+
File.dirname(@file)
|
43
|
+
end
|
44
|
+
|
45
|
+
def input_basename
|
46
|
+
File.basename(@file)
|
47
|
+
end
|
48
|
+
|
49
|
+
def source
|
50
|
+
md = input_basename.match(Regexp.new(@config[:input][:pattern])).to_a
|
51
|
+
File.join(input_dirname, @config[:input][:source].gsub(/\$(\d+)/) { md[$1.to_i] })
|
52
|
+
end
|
53
|
+
|
54
|
+
def destination
|
55
|
+
File.join(@config[:output][:directory], @context.instance_eval('"%s"' % @config[:output][:filename]))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/maprename.rb
ADDED
data/maprename.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative 'lib/maprename/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "maprename"
|
5
|
+
spec.version = Maprename::VERSION
|
6
|
+
spec.authors = ["Liu Xiang"]
|
7
|
+
spec.email = ["liuxiang@ktjr.com"]
|
8
|
+
|
9
|
+
spec.summary = %q{Simple file rename utils}
|
10
|
+
spec.description = %q{Simple file rename utils}
|
11
|
+
spec.homepage = "https://github.com/lululau/maprename"
|
12
|
+
spec.license = "MIT"
|
13
|
+
|
14
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
15
|
+
|
16
|
+
|
17
|
+
# Specify which files should be added to the gem when it is released.
|
18
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
19
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
20
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
21
|
+
end
|
22
|
+
spec.bindir = "exe"
|
23
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
|
+
spec.require_paths = ["lib"]
|
25
|
+
|
26
|
+
spec.add_dependency 'activesupport'
|
27
|
+
end
|
data/maprename.yml
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
input:
|
2
|
+
directory: ./tmp/in/
|
3
|
+
pattern: "(.*).txt"
|
4
|
+
source: "$1.pdf"
|
5
|
+
name_parse:
|
6
|
+
method: split
|
7
|
+
pattern: "[-_]"
|
8
|
+
fields:
|
9
|
+
- name: customer_name
|
10
|
+
value: "$1"
|
11
|
+
- name: raw_trans_no
|
12
|
+
value: "$2"
|
13
|
+
name_parse:
|
14
|
+
method: scan
|
15
|
+
pattern: L(.*)
|
16
|
+
fields:
|
17
|
+
- name: trans_no
|
18
|
+
value: "$1"
|
19
|
+
content_parse:
|
20
|
+
encoding: UTF-8
|
21
|
+
fields:
|
22
|
+
- name: address
|
23
|
+
pattern: "您的地址 ([^::]+)"
|
24
|
+
value: "$1"
|
25
|
+
- name: trade_date
|
26
|
+
pattern: "交易日期:([0-9-]+)"
|
27
|
+
value: "$1"
|
28
|
+
|
29
|
+
mapping:
|
30
|
+
file: ./tmp/in/mapping.csv
|
31
|
+
encoding: UTF-8
|
32
|
+
column_separator: ","
|
33
|
+
first_line_as_column_defination: false
|
34
|
+
columns:
|
35
|
+
- name: trans_id
|
36
|
+
index: 1
|
37
|
+
- name: account_no
|
38
|
+
index: 2
|
39
|
+
select:
|
40
|
+
- keyword_column: trans_id
|
41
|
+
keyword_value: trans_no
|
42
|
+
select_column: account_no
|
43
|
+
name: acct_no
|
44
|
+
|
45
|
+
output:
|
46
|
+
directory: ./tmp/out/
|
47
|
+
filename: "#{trade_date}/#{customer_name}-#{acct_no}-#{address}.pdf"
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: maprename
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Liu Xiang
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-03-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: Simple file rename utils
|
28
|
+
email:
|
29
|
+
- liuxiang@ktjr.com
|
30
|
+
executables:
|
31
|
+
- maprename
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- ".gitignore"
|
36
|
+
- ".rspec"
|
37
|
+
- ".travis.yml"
|
38
|
+
- CODE_OF_CONDUCT.md
|
39
|
+
- Gemfile
|
40
|
+
- Gemfile.lock
|
41
|
+
- LICENSE.txt
|
42
|
+
- README.md
|
43
|
+
- Rakefile
|
44
|
+
- bin/console
|
45
|
+
- bin/setup
|
46
|
+
- exe/maprename
|
47
|
+
- lib/maprename.rb
|
48
|
+
- lib/maprename/app.rb
|
49
|
+
- lib/maprename/cli.rb
|
50
|
+
- lib/maprename/content_parser.rb
|
51
|
+
- lib/maprename/file_name_parser.rb
|
52
|
+
- lib/maprename/mapping_parser.rb
|
53
|
+
- lib/maprename/renamer.rb
|
54
|
+
- lib/maprename/version.rb
|
55
|
+
- maprename.gemspec
|
56
|
+
- maprename.yml
|
57
|
+
homepage: https://github.com/lululau/maprename
|
58
|
+
licenses:
|
59
|
+
- MIT
|
60
|
+
metadata: {}
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 2.3.0
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
requirements: []
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 2.7.8
|
78
|
+
signing_key:
|
79
|
+
specification_version: 4
|
80
|
+
summary: Simple file rename utils
|
81
|
+
test_files: []
|