libis-tools 1.0.0 → 1.0.1

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
  SHA1:
3
- metadata.gz: 6ac60f332c876e86ca3f751e4a99b68a10b60f03
4
- data.tar.gz: cc488738791b1535b7c79fdc9dbf7b7e75847dca
3
+ metadata.gz: 3471bd7089b2059df27dd50e932a5d069fd81caa
4
+ data.tar.gz: d361302b0cf5b1ebbbe489f6fb88b04a740955bb
5
5
  SHA512:
6
- metadata.gz: 3c74151553d0042175b87149cc9f19d15efbb23191e32dce0c37d3f40ef17eb9872005b9757aafec0cf159b905fbdd4bbc9609248d61a0874d5a3fa3f1a47a66
7
- data.tar.gz: ee214d77eae0e39be7a08b6bfdbb1daf4568814aa5be02bc35ee93095b090fd0abd04ee5bee4f40e96e9413dc3fa30769c08feb02a6db9b0ed8da7dac0ffe05c
6
+ metadata.gz: fdb901ba14397b93eec5157bb28b6ee0c39b0ecbb0e094b1b5a87a7e183338fb89ec2b0460b049035381179cc45f9c4661b1185c7f9a61b38085308546f69dea
7
+ data.tar.gz: fa413bbcdc1349838b9cc8f6156b4fd52a7466ed382b3f98b47abd4f87fa0925eb8b010c951fea4cbec59b4e857435254f9624d9c50549f760d85b3c0bc6af2f
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'libis/tools'
4
+
5
+ Libis::Tools::CommandLine.start(ARGV)
@@ -3,6 +3,8 @@ require_relative 'tools/version'
3
3
  module Libis
4
4
  module Tools
5
5
 
6
+ autoload :CommandLine, 'libis/tools/command_line'
7
+
6
8
  autoload :Checksum, 'libis/tools/checksum'
7
9
  autoload :Command, 'libis/tools/command'
8
10
  autoload :Config, 'libis/tools/config'
@@ -0,0 +1,189 @@
1
+ require 'tty-prompt'
2
+ require 'tty-config'
3
+ require 'pastel'
4
+
5
+ module Libis
6
+ module Tools
7
+ module Cli
8
+ module Helper
9
+
10
+ module ClassMethods
11
+
12
+ def exit_on_failure?
13
+ true
14
+ end
15
+
16
+ end
17
+
18
+ def self.included(base)
19
+ base.extend(ClassMethods)
20
+ end
21
+
22
+ attr_reader :prompt, :config, :pastel, :config_file_prefix
23
+
24
+ def initialize(*args)
25
+ @prompt = TTY::Prompt.new
26
+ @config = TTY::Config.new
27
+ @pastel = Pastel.new
28
+ @config.append_path Dir.home
29
+ @config_file_prefix = '.tools.'
30
+ prompt.warn "Default config file: #{config.filename}"
31
+ super
32
+ end
33
+
34
+ protected
35
+
36
+ private
37
+
38
+ def index_of(list, value)
39
+ i = list.index(value)
40
+ i += 1 if i
41
+ i || 1
42
+ end
43
+
44
+ def config_write(name = nil)
45
+ set_config_name(name || new_config)
46
+ unless get_config_name
47
+ prompt.error 'Could not write the configuration file: configuration not set'
48
+ return
49
+ end
50
+ config.write force: true
51
+ end
52
+
53
+ def config_read(name = nil)
54
+ config.filename = name ?
55
+ "#{config_file_prefix}#{name}" :
56
+ select_config_file(with_new: false)
57
+ unless get_config_name
58
+ prompt.error 'Could not read the configuration file: configuration not set'
59
+ return
60
+ end
61
+ config.read
62
+ rescue TTY::Config::ReadError
63
+ prompt.error('Could not read the configuration file.')
64
+ exit
65
+ end
66
+
67
+ def toggle_config(field)
68
+ config.set(field, value: !config.fetch(field))
69
+ end
70
+
71
+ def get_config_name
72
+ return $1 if get_config_file.match(config_file_regex)
73
+ nil
74
+ end
75
+
76
+ def get_config_file
77
+ config.filename
78
+ end
79
+
80
+ def config_file_regex(with_ext: false)
81
+ /^#{Regexp.quote(config_file_prefix)}(.+)#{Regexp.quote(config.extname) if with_ext}$/
82
+ end
83
+
84
+ def set_config_name(name)
85
+ config.filename = "#{config_file_prefix}#{name}" if name && !name.empty?
86
+ end
87
+
88
+ def set_config_file(name)
89
+ config.filename = name if name && !name.empty?
90
+ end
91
+
92
+ def select_config_file(*args)
93
+ "#{config_file_prefix}#{select_config_name *args}"
94
+ end
95
+
96
+ def select_config_name(with_new: true, force_select: false)
97
+ current_cfg = get_config_name
98
+ return current_cfg if !force_select && current_cfg
99
+
100
+ cfgs = []
101
+ cfgs << {
102
+ name: '-- new configuration --',
103
+ value: -> do
104
+ new_config
105
+ end
106
+ } if with_new
107
+ cfgs += Dir.glob(File.join(Dir.home, "#{config_file_prefix}*")).reduce([]) do |a, x|
108
+ a.push($1) if File.basename(x).match(config_file_regex(with_ext: true))
109
+ a
110
+ end
111
+
112
+ return nil if cfgs.empty?
113
+
114
+ prompt.select '[ Select config menu ]', cfgs, default: index_of(cfgs, current_cfg), filter: true
115
+ end
116
+
117
+ def new_config
118
+ while true
119
+ name = prompt.ask('Enter a name for the configuration:', modify: :trim)
120
+ return name unless File.exist?(File.join(Dir.home, "#{config_file_prefix}#{name}#{config.extname}")) &&
121
+ !prompt.yes?("Configuration '#{name}' already exists. Overwrite?")
122
+ end
123
+ end
124
+
125
+ def ask(question, field, bool: false, enum: nil, default: nil, mask: false, if_empty: false)
126
+ cmd, args, opts = :ask, [question], {}
127
+ default ||= config.fetch(field)
128
+ if enum
129
+ cmd = :select
130
+ args << enum
131
+ # Change default to its index in the enum
132
+ default = index_of(enum, default)
133
+ # Force the question if the supplied value is not valid
134
+ config.delete field unless !if_empty || enum.include?(config.fetch field)
135
+ end
136
+ cmd = :mask if mask
137
+ opts[:default] = config.fetch(field)
138
+ opts[:default] = default if default
139
+ cmd = (opts[:default] ? :yes? : :no?) if bool
140
+ config.set(field, value: prompt.send(cmd, *args, opts)) unless if_empty && config.fetch(field)
141
+ end
142
+
143
+ def tree_select(path, question: nil, file: false, page_size: 22, filter: true, cycle: false, create: false,
144
+ default_choices: nil)
145
+ path = Pathname.new(path) unless path.is_a? Pathname
146
+
147
+ return path unless path.exist?
148
+ path = path.realpath
149
+
150
+ dirs = path.children.select(&:directory?).sort
151
+ files = file ? path.children.select(&:file?).sort : []
152
+
153
+ choices = []
154
+ choices << {name: "Folder: #{path}", value: path, disabled: file ? '' : false}
155
+ choices += default_choices if default_choices
156
+ choices << {name: '-- new directory --', value: -> do
157
+ new_name = prompt.ask('new directory name:', modify: :trim, required: true)
158
+ new_path = path + new_name
159
+ FileUtils.mkdir(new_path.to_path)
160
+ new_path
161
+ end
162
+ } if create
163
+
164
+ choices << {name: "-- new file --", value: -> do
165
+ new_name = prompt.ask('new file name:', modify: :trim, required: true)
166
+ path + new_name
167
+ end
168
+ } if file && create
169
+
170
+ choices << {name: '[..]', value: path.parent}
171
+
172
+ dirs.each {|d| choices << {name: "[#{d.basename}]", value: d}}
173
+ files.each {|f| choices << {name: f.basename.to_path, value: f}}
174
+
175
+ question ||= "Select #{'file or ' if files}directory"
176
+ selection = prompt.select question, choices,
177
+ per_page: page_size, filter: filter, cycle: cycle, default: file ? 2 : 1
178
+
179
+ return selection unless selection.is_a? Pathname
180
+ return selection.to_path if selection == path || selection.file?
181
+
182
+ tree_select selection, question: question, file: file, page_size: page_size, filter: filter,
183
+ cycle: cycle, create: create, default_choices: default_choices
184
+ end
185
+
186
+ end
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,407 @@
1
+ require 'libis/tools/spreadsheet'
2
+ require 'awesome_print'
3
+
4
+ module Libis
5
+ module Tools
6
+ module Cli
7
+ module Reorg
8
+
9
+ # noinspection RubyExpressionInStringInspection
10
+ DEFAULT_CONFIG = {
11
+ base: '.',
12
+ filter: '^(.*)$',
13
+ expression: 'target/#{file_name}',
14
+ action: 'move',
15
+ overwrite: false,
16
+ interactive: false,
17
+ report: nil,
18
+ dummy: false,
19
+ config: nil,
20
+ unattended: false
21
+ }
22
+
23
+ # noinspection RubyStringKeysInHashInspection
24
+ VALID_ACTIONS = {
25
+ 'move' => 'moved',
26
+ 'copy' => 'copied',
27
+ 'link' => 'linked'
28
+ }
29
+
30
+ STRING_CONFIG = {
31
+ base: "Source Directory to organize",
32
+ filter: "File matching filter",
33
+ expression: "New file path expression",
34
+ action: "Action to perform",
35
+ overwrite: "Overwite target files if newer",
36
+ interactive: "Ask for action on changed files",
37
+ report: "Report file",
38
+ dummy: "Perform phantom actions (not affecting files)"
39
+ }
40
+
41
+ REQ_HEADERS = {term: 'Term'}
42
+ OPT_HEADERS = {pid: 'Pid', filename: 'File'}
43
+
44
+ def self.included(klass)
45
+ klass.class_exec do
46
+ desc 'reorg [options]', 'Reorganize files'
47
+ long_desc <<-DESC
48
+
49
+ 'reorg [options]' will reorganize files based on the name of the files.
50
+
51
+ The base directory will be scanned for files that match the FILTER regular expression. For each matching
52
+ file, an action will be performed. The outcome of the action is determined by the expression that is given.
53
+
54
+ The expression will be evaluated as a Ruby string expression and supports string interpolation in the form
55
+ '\#{<thing>}', where <thing> can be any of:
56
+
57
+ . $x : refers to the x-th group in the FILTER. Groups are numbered by the order of the opening '('
58
+
59
+ . file_name : the original file name
60
+
61
+ The action that will be performed on the action depens on the configured ACTION. The valid ACTIONs are;
62
+ 'move', copy' and 'link'. Please note that in the latter case only the files will be soft-linked and any
63
+ directory in the target path will be created. The tool will therefore never create soft-links to directories.
64
+ The soft-links are created with an absolute reference path. This allows you to later move and rename the
65
+ soft-links later as you seem fit without affecting the source files. You could for instance run this tool on
66
+ the soft-links with the 'move' action to do so.
67
+
68
+ By default, if the target file already exists, the file ACTION will not be performed. The '--overwrite'
69
+ option will cause the tool to compare the file dates and checksums of source and target files in that case.
70
+ Only if the checksums are different and the source file has a more recent modification date, the target file
71
+ will be overwritten. If you want to be asked for overwrite confirmation for each such file, you can add the
72
+ '--interactive' option.
73
+
74
+ The tool can generate a report on all the file actions that have been performed. To do so, specify a file
75
+ name for the '--report' option. The format of the report will be determined by the file extension you supply:
76
+
77
+ - *.csv : comma-separated file
78
+
79
+ - *.tsv : tab-separated file
80
+
81
+ - *.yml : YAML file
82
+
83
+ - *.xml : XML file
84
+
85
+ By adding the --dummy option, you can test your settings without performing the real actions on the file.
86
+ The tool will still report on its progress as if it would perform the actions.
87
+
88
+ All the options can be saved into a configuration file to be reused later. You can specify which
89
+ configuration file you want to use with the '--config' option. If you specify a configuration file, the tool
90
+ will first load the options from the configuration file and then process the command-line options. The
91
+ command-line options therefore have priority over the options in the configuration file.
92
+
93
+ By default the tool allows you to review the activated options and gives you the opportunity to modify them
94
+ before continuing of bailing out. If you are confident the settings are fine, you can skip this with the
95
+ '--unatttended' option. Handle with care!
96
+
97
+ DESC
98
+
99
+ method_option :base, aliases: '-b', desc: 'Directory that needs to be reorganized',
100
+ default: DEFAULT_CONFIG[:base]
101
+ method_option :filter, aliases: '-f', desc: 'Regex for file name matching',
102
+ default: DEFAULT_CONFIG[:filter]
103
+ method_option :expression, aliases: '-e', desc: 'Ruby string expression for the new file path',
104
+ default: DEFAULT_CONFIG[:expression]
105
+
106
+ method_option :action, aliases: '-a', desc: 'Operation to perform on files found',
107
+ enum: VALID_ACTIONS.keys, default: DEFAULT_CONFIG[:action]
108
+ method_option :overwrite, aliases: '-o', desc: 'Overwrite target if changed',
109
+ type: :boolean, default: DEFAULT_CONFIG[:overwrite]
110
+ method_option :interactive, aliases: '-i', desc: 'Ask for action when changed files are found',
111
+ type: :boolean, default: DEFAULT_CONFIG[:interactive]
112
+
113
+ method_option :report, aliases: '-r', banner: 'FILE', desc: 'Generate report in FILE',
114
+ default: DEFAULT_CONFIG[:report]
115
+
116
+ method_option :dummy, desc: 'Do not perform file actions, only report them',
117
+ type: :boolean, default: DEFAULT_CONFIG[:dummy]
118
+
119
+ method_option :config, aliases: '-c', desc: 'Configuration name to use',
120
+ type: :string, default: DEFAULT_CONFIG[:config]
121
+
122
+ method_option :unattended, aliases: '-u', desc: 'Run without asking for input',
123
+ type: :boolean, default: DEFAULT_CONFIG[:unattended]
124
+
125
+ end
126
+
127
+ end
128
+
129
+ def reorg
130
+ @config_file_prefix = '.reorg.'
131
+
132
+ # return config_write
133
+
134
+ DEFAULT_CONFIG.each {|key, value| config.set(key, value: value) if value}
135
+ config_read(options[:config]) if options[:config]
136
+ DEFAULT_CONFIG.each {|key, _| config.set(key, value: options[:key]) if options.has_key?(key)}
137
+ run_menu
138
+ end
139
+
140
+ protected
141
+
142
+ def run_menu
143
+
144
+ begin
145
+ choices = []
146
+
147
+ choices << {name: "Configuration editor",
148
+ value: -> {config_menu; 1}
149
+ }
150
+
151
+ choices << {name: "Run", value: -> {do_reorg; nil}}
152
+ choices << {name: "Exit", value: nil}
153
+
154
+ selection = prompt.select "[ LIBIS Tool - ReOrg ]",
155
+ choices, cycle: true, default: 1
156
+
157
+ end until selection.nil?
158
+
159
+ end
160
+
161
+ def print_field(field)
162
+ value = config.fetch(field)
163
+ value = 'Yes' if value.is_a?(TrueClass)
164
+ value = 'No' if value.is_a?(FalseClass)
165
+ "#{STRING_CONFIG[field]} : #{pastel.green(value)}"
166
+ end
167
+
168
+ def config_menu
169
+
170
+ selection = 1
171
+
172
+ begin
173
+ choices = []
174
+ choices << {name: print_field(:base),
175
+ value: -> do
176
+ config.set :base,
177
+ value: tree_select(config.fetch(:base) || '.', question: 'Select source directory:')
178
+ 1
179
+ end
180
+ }
181
+ choices << {name: print_field(:filter),
182
+ value: -> {ask 'File filter regex:', :filter; 2}
183
+ }
184
+ choices << {name: print_field(:expression),
185
+ value: -> {ask 'New path expression:', :expression; 3}
186
+ }
187
+ choices << {name: print_field(:action),
188
+ value: -> {ask 'Action:', :action, enum: VALID_ACTIONS.keys; 4}
189
+ }
190
+ choices << {name: print_field(:overwrite),
191
+ value: -> {toggle_config(:overwrite); prompt.say print_field(:overwrite); 5}
192
+ }
193
+ choices << {name: print_field(:interactive),
194
+ value: -> {toggle_config(:interactive); prompt.say print_field(:interactive); 6}
195
+ }
196
+ choices << {name: print_field(:report),
197
+ value: -> do
198
+ report = config.fetch(:report)
199
+ default = '.'
200
+ default = File.dirname(report) if report && File.file?(report)
201
+ report = tree_select(default, question: 'Select source directory',
202
+ file: true, create: true,
203
+ default_choices: [{name: "-- no report --", value: nil}])
204
+ if report
205
+ config.set(:report, value: report)
206
+ else
207
+ config.delete(:report)
208
+ end
209
+ 7
210
+ end
211
+ }
212
+ choices << {name: print_field(:dummy),
213
+ value: -> {toggle_config(:dummy); prompt.say print_field(:dummy); 8}
214
+ }
215
+ choices << {name: "-- save configuration '#{get_config_name}' --",
216
+ value: -> {config_write get_config_name; 9}
217
+ } if get_config_name
218
+ choices << {name: "-- save to new configuration --",
219
+ value: -> {config_write new_config; 10}
220
+ }
221
+ choices << {name: "-- read configuration --",
222
+ value: -> {config_read; 11}
223
+ }
224
+ choices << {name: "-- return to main menu --", value: nil}
225
+
226
+ selection = prompt.select "[ Configuration menu ]",
227
+ choices, per_page: 20, cycle: true, default: selection
228
+
229
+ end until selection.nil?
230
+
231
+ end
232
+
233
+ def do_reorg
234
+ prompt.ok 'This can take a while. Please sit back and relax, grab a cup of coffee, have a quick nap or read a good book ...'
235
+
236
+ # keeps track of folders created
237
+ require 'set'
238
+ target_dir_list = Set.new
239
+
240
+ open_report
241
+
242
+ require 'fileutils'
243
+ count = {move: 0, duplicate: 0, update: 0, reject: 0, skipped_dir: 0, unmatched_file: 0}
244
+
245
+ base_dir = config.fetch(:base)
246
+ parse_regex = Regexp.new(config.fetch(:filter))
247
+ path_expression = "#{config.fetch(:expression)}"
248
+ dummy_operation = config.fetch(:dummy)
249
+ interactive = config.fetch(:interactive)
250
+ overwrite = config.fetch(:overwrite)
251
+ file_operation = config.fetch(:action)
252
+ Dir.new(base_dir).entries.each do |file_name|
253
+ next if file_name =~ /^\.\.?$/
254
+ entry = File.join(File.absolute_path(base_dir), file_name)
255
+ unless File.file?(entry)
256
+ puts "Skipping directory #{entry}." unless @report
257
+ write_report(entry, '', '', 'Directory - skipped.')
258
+ count[:skipped_dir] += 1
259
+ next
260
+ end
261
+ unless file_name =~ parse_regex
262
+ puts "Skipping file #{file_name}. File name does not match expression." unless @report
263
+ write_report(entry, '', '', 'Mismatch - skipped.')
264
+ count[:unmatched_file] += 1
265
+ next
266
+ end
267
+ target = eval('"' + path_expression + '"')
268
+ target_file = File.basename(target)
269
+ target_dir = File.dirname(target)
270
+ target_dir = File.join(base_dir, target_dir) unless target_dir[0] == '/'
271
+ unless target_dir_list.include?(target_dir)
272
+ puts "-> Create directory '#{target_dir}'" unless @report
273
+ FileUtils.mkpath(target_dir) unless dummy_operation
274
+ target_dir_list << target_dir
275
+ end
276
+ target_path = File.join(target_dir, target_file)
277
+ remark = nil
278
+ action = false
279
+ if File.exist?(target_path)
280
+ if compare_entry(entry, target_path)
281
+ remark = 'Duplicate - skipped.'
282
+ count[:duplicate] += 1
283
+ $stderr.puts "Duplicate file entry: #{entry}." unless @report
284
+ else
285
+ # puts "source: #{File.mtime(entry)} #{'%11s' % Filesize.new(File.size(entry)).pretty} #{entry}"
286
+ # puts "target: #{File.mtime(target_path)} #{'%11s' % Filesize.new(File.size(target_path)).pretty} #{target_path}"
287
+ if interactive ? prompt.send((overwrite ? :yes : :no), 'Overwrite target?') : overwrite
288
+ remark = 'Duplicate - updated'
289
+ action = true
290
+ count[:update] += 1
291
+ else
292
+ remark = 'Duplicate - rejected.'
293
+ $stderr.puts "ERROR: #{entry} exists with different content." unless @report
294
+ count[:reject] += 1
295
+ end
296
+ end
297
+ else
298
+ action = true
299
+ count[:move] += 1
300
+ end
301
+ if action
302
+ puts "-> #{file_operation} '#{file_name}' to '#{target}'" unless @report
303
+ case file_operation
304
+ when 'move'
305
+ FileUtils.move(entry, File.join(target_dir, target_file), force: true)
306
+ when 'copy'
307
+ FileUtils.copy(entry, File.join(target_dir, target_file))
308
+ when 'link'
309
+ FileUtils.symlink(entry, File.join(target_dir, target_file), force: true)
310
+ else
311
+ # Shouldn't happen
312
+ raise RuntimeError, "Bad file operation: '#{file_operation}'"
313
+ end unless dummy_operation
314
+ end
315
+ write_report(entry, target_dir, target_file, remark)
316
+ end
317
+
318
+ prompt.ok "#{'%8d' % count[:skipped_dir]} dir(s) found and skipped."
319
+ prompt.ok "#{'%8d' % count[:unmatched_file]} file(s) found that did not match and skipped."
320
+ prompt.ok "#{'%8d' % count[:move]} file(s) #{VALID_ACTIONS[file_operation]}."
321
+ prompt.ok "#{'%8d' % count[:duplicate]} duplicate(s) found and skipped."
322
+ prompt.ok "#{'%8d' % count[:update]} changed file(s) found and updated."
323
+ prompt.ok "#{'%8d' % count[:reject]} changed file(s) found and rejected."
324
+
325
+ close_report
326
+
327
+ prompt.ok 'Done!'
328
+
329
+ end
330
+
331
+
332
+ def open_report
333
+ if (report_file = config.fetch(:report))
334
+ # noinspection RubyStringKeysInHashInspection
335
+ @report_type = {'.csv' => :csv, '.tsv' => :tsv, '.xml' => :xml, '.yml' => :yml}[File.extname(report_file)]
336
+ unless @report_type
337
+ prompt.error "Unknown file type: #{File.extname(report_file)}"
338
+ exit
339
+ end
340
+ @report = File.open(report_file, 'w+')
341
+ end
342
+ end
343
+
344
+ def for_tsv(string)
345
+ ; string =~ /\t\n/ ? "\"#{string.gsub('"', '""')}\"" : string;
346
+ end
347
+
348
+ def for_csv(string)
349
+ ; string =~ /,\n/ ? "\"#{string.gsub('"', '""')}\"" : string;
350
+ end
351
+
352
+ def for_xml(string, type = :attr)
353
+ ; string.encode(xml: type);
354
+ end
355
+
356
+ def for_yml(string)
357
+ ; string.inspect.to_yaml;
358
+ end
359
+
360
+ def write_report(old_name, new_folder, new_name, remark = nil)
361
+ return unless @report
362
+ case @report_type
363
+ when :tsv
364
+ @report.puts "old_name\tnew_folder\tnew_name\tremark" if @report.size == 0
365
+ @report.puts "#{for_tsv(old_name)}\t#{for_tsv(new_folder)}" +
366
+ "\t#{for_tsv(new_name)}\t#{for_tsv(remark)}"
367
+ when :csv
368
+ @report.puts 'old_name,new_folder,new_name' if @report.size == 0
369
+ @report.puts "#{for_csv(old_name)},#{for_csv(new_folder)}" +
370
+ ",#{for_csv(new_name)},#{for_csv(remark)}"
371
+ when :xml
372
+ @report.puts '<?xml version="1.0" encoding="UTF-8"?>' if @report.size == 0
373
+ @report.puts '<report>' if @report.size == 1
374
+ @report.puts ' <file>'
375
+ @report.puts " <old_name>#{for_xml(old_name, :text)}</old_name>"
376
+ @report.puts " <new_folder>#{for_xml(new_folder, :text)}</new_folder>"
377
+ @report.puts " <new_name>#{for_xml(new_name, :text)}</new_name>"
378
+ @report.puts " <remark>#{for_xml(remark, :text)}</remark>" if remark
379
+ @report.puts ' </file>'
380
+ when :yml
381
+ @report.puts '# Reorganisation report' if @report.size == 0
382
+ @report.puts "- old_name: #{for_yml(old_name)}" +
383
+ "\n new_folder: #{for_yml(new_folder)}" +
384
+ "\n new_name: #{for_yml(new_name)}" +
385
+ (remark ? "\n remark: #{for_yml(remark)}" : '')
386
+ else
387
+ #nothing
388
+ end
389
+ end
390
+
391
+ def close_report
392
+ return unless @report
393
+ if @report_type == :xml
394
+ @report.puts '</report>'
395
+ end
396
+ @report.close
397
+ end
398
+
399
+ def compare_entry(src, tgt)
400
+ hasher = Libis::Tools::Checksum.new(:SHA256)
401
+ hasher.digest(src) == hasher.digest(tgt)
402
+ end
403
+
404
+ end
405
+ end
406
+ end
407
+ end
@@ -0,0 +1,23 @@
1
+ require 'thor'
2
+ require 'tty-prompt'
3
+ require 'tty-config'
4
+
5
+ require 'libis/tools/cli/cli_helper'
6
+ require 'libis/tools/cli/reorg'
7
+
8
+ module Libis
9
+ module Tools
10
+
11
+ class CommandLine < Thor
12
+
13
+ include Cli::Helper
14
+ include Cli::Reorg
15
+
16
+ def reorg
17
+ super
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+ end
@@ -1,5 +1,5 @@
1
1
  module Libis
2
2
  module Tools
3
- VERSION = '1.0.0'
3
+ VERSION = '1.0.1'
4
4
  end
5
5
  end
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.platform = Gem::Platform::JAVA if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
23
23
 
24
24
  spec.files = `git ls-files -z`.split("\x0")
25
+ spec.bindir = 'bin'
25
26
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
26
27
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
27
28
 
@@ -47,4 +48,8 @@ Gem::Specification.new do |spec|
47
48
  spec.add_runtime_dependency 'yard', '~> 0.9.11'
48
49
  spec.add_runtime_dependency 'roo', '~> 2.5'
49
50
  spec.add_runtime_dependency 'roo-xls', '~> 1.0'
51
+ spec.add_runtime_dependency 'tty-prompt'
52
+ spec.add_runtime_dependency 'tty-config'
53
+ spec.add_runtime_dependency 'thor'
54
+
50
55
  end
@@ -34,11 +34,16 @@ describe 'METS File' do
34
34
  context 'with IE AMD' do
35
35
 
36
36
  let(:dc_record) {
37
- record = Libis::Tools::Metadata::DublinCoreRecord.new
38
- record.title = 'Title'
39
- record.author = 'Author'
40
- record.subject = 'Subject'
41
- record
37
+ Libis::Tools::XmlDocument.parse <<-STR
38
+ <?xml version="1.0" encoding="UTF-8"?>
39
+ <record xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
40
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
41
+ xmlns:dcterms="http://purl.org/dc/terms/">
42
+ <dc:title>Title</dc:title>
43
+ <dc:author>Author</dc:author>
44
+ <dc:subject>Subject</dc:subject>
45
+ </record>
46
+ STR
42
47
  }
43
48
 
44
49
  let(:marc_record) {
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: libis-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kris Dekeyser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-08-20 00:00:00.000000000 Z
11
+ date: 2018-10-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -262,10 +262,53 @@ dependencies:
262
262
  - - "~>"
263
263
  - !ruby/object:Gem::Version
264
264
  version: '1.0'
265
+ - !ruby/object:Gem::Dependency
266
+ name: tty-prompt
267
+ requirement: !ruby/object:Gem::Requirement
268
+ requirements:
269
+ - - ">="
270
+ - !ruby/object:Gem::Version
271
+ version: '0'
272
+ type: :runtime
273
+ prerelease: false
274
+ version_requirements: !ruby/object:Gem::Requirement
275
+ requirements:
276
+ - - ">="
277
+ - !ruby/object:Gem::Version
278
+ version: '0'
279
+ - !ruby/object:Gem::Dependency
280
+ name: tty-config
281
+ requirement: !ruby/object:Gem::Requirement
282
+ requirements:
283
+ - - ">="
284
+ - !ruby/object:Gem::Version
285
+ version: '0'
286
+ type: :runtime
287
+ prerelease: false
288
+ version_requirements: !ruby/object:Gem::Requirement
289
+ requirements:
290
+ - - ">="
291
+ - !ruby/object:Gem::Version
292
+ version: '0'
293
+ - !ruby/object:Gem::Dependency
294
+ name: thor
295
+ requirement: !ruby/object:Gem::Requirement
296
+ requirements:
297
+ - - ">="
298
+ - !ruby/object:Gem::Version
299
+ version: '0'
300
+ type: :runtime
301
+ prerelease: false
302
+ version_requirements: !ruby/object:Gem::Requirement
303
+ requirements:
304
+ - - ">="
305
+ - !ruby/object:Gem::Version
306
+ version: '0'
265
307
  description: Some tool classes for other LIBIS gems.
266
308
  email:
267
309
  - kris.dekeyser@libis.be
268
- executables: []
310
+ executables:
311
+ - libis_tool
269
312
  extensions: []
270
313
  extra_rdoc_files: []
271
314
  files:
@@ -276,11 +319,15 @@ files:
276
319
  - Gemfile
277
320
  - README.md
278
321
  - Rakefile
322
+ - bin/libis_tool
279
323
  - lib/libis-tools.rb
280
324
  - lib/libis/tools.rb
281
325
  - lib/libis/tools/assert.rb
282
326
  - lib/libis/tools/checksum.rb
327
+ - lib/libis/tools/cli/cli_helper.rb
328
+ - lib/libis/tools/cli/reorg.rb
283
329
  - lib/libis/tools/command.rb
330
+ - lib/libis/tools/command_line.rb
284
331
  - lib/libis/tools/config.rb
285
332
  - lib/libis/tools/config_file.rb
286
333
  - lib/libis/tools/csv.rb