libis-tools 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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