renamr 1.0.2

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ebae244a34e6b7498de4aa841b1d09d2dde748e553f6100553551fcc8a88c6e4
4
+ data.tar.gz: 1fa08e2c6001b46f9675f07c142de7a226efe1fbbba18554c71ed4ba8f9b0138
5
+ SHA512:
6
+ metadata.gz: 4e94c4ed1320cd5652cbe4c6ce85852225ce9a6dca6daeb945d0ae499d191a585fff21f65344bd02b7733522baa23a690e72cfa2162ec14bfad16bb1c661a139
7
+ data.tar.gz: 570c8406c091a60895232d26f1c19cbf5add10e891250e672f02b1074c350e004e4ba646e3801662c8855bb09222d58f743d35c4f78640c2fab6f385d2127b3d
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) 2020, David Rabkin
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,53 @@
1
+ # Renamr
2
+ File and directory names organiser
3
+
4
+ * [About](#about)
5
+ * [Installation](#installation)
6
+ * [Usage](#usage)
7
+ * [License](#license)
8
+
9
+ ## About
10
+ Hi, I'm [David Rabkin](https://www.rabkin.co.il). I created this tool to
11
+ orginize multiple files.
12
+
13
+ ## Installation
14
+ The tool is designed to work on macOS, GNU/Linux, Windows, Unix-like OS. It is
15
+ packaged as a Gem and require Ruby version 2.4 or later. See «[Installing
16
+ Ruby](https://www.ruby-lang.org/en/documentation/installation/)» if you don't
17
+ have the proper version on your platform.
18
+
19
+ Use this command to install:
20
+
21
+ gem install renamr
22
+
23
+ ### Updating
24
+ Use this command to update the package:
25
+
26
+ gem update renamr
27
+
28
+ ### Requirements
29
+ There are no requirements.
30
+
31
+ ## Usage
32
+ renamr [options]
33
+ -a, --act Real renaming.
34
+ -r, --rec Passes recursively.
35
+ -l, --lim Limits name length.
36
+ -m, --mod Prepends modification time.
37
+ -d, --dir dir Directory to rename.
38
+ -s, --src src A string to substitute.
39
+ -t, --dst dst A string to replace to.
40
+ -p, --pre pre A string to prepend to.
41
+ -w, --wid wid Width of the table.
42
+ -c, --cut pos,len Removes symbols from pos.
43
+
44
+ ### Example
45
+
46
+ renamr -d <source>
47
+
48
+ It renames all files in `source` by default pattern: 26 English letters,
49
+ 10 numbers, minus for spaces and other symbols.
50
+
51
+ ## License
52
+ Transcode is copyright [David Rabkin](http://www.rabkin.co.il/) and
53
+ available under a [2-Claus BSD license](https://github.com/rdavid/renamr/blob/master/LICENSE).
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
5
+ # Copyright 2018-present David Rabkin
6
+ # This script renames files in given directory by specific rules.
7
+
8
+ require 'pidfile'
9
+ require_relative '../lib/renamr'
10
+
11
+ PidFile.new
12
+ Renamr::Renamer.new.do
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require_relative 'renamr/renamer'
7
+ require_relative 'renamr/version'
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ module Renamr
7
+ # An interface for actions implementation.
8
+ class Action
9
+ def do(src)
10
+ raise "Undefined method Action.do is called with #{src}."
11
+ end
12
+
13
+ def set(src) end
14
+
15
+ def p2m(src)
16
+ src.tr('.', '-')
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require_relative 'action'
7
+
8
+ module Renamr
9
+ # Checks if the resulted string has only ASCII symbols.
10
+ class ASCIIValidatorAction < Action
11
+ def do(src)
12
+ ascii = src.chars.select(&:ascii_only?).join
13
+ raise "String #{src} has non-ASCII symbols." if src != ascii
14
+
15
+ src
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require 'i18n'
7
+ require_relative 'action'
8
+
9
+ module Renamr
10
+ # Automatic localization.
11
+ class AutoLocalizationAction < Action
12
+ def initialize
13
+ I18n.config.available_locales = :en
14
+ end
15
+
16
+ def do(src)
17
+ I18n.transliterate(src).tr('?', '')
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require 'set'
7
+ require_relative 'action'
8
+
9
+ module Renamr
10
+ # All special symbols besides some (., &, $) are replaced by minus.
11
+ class CharAction < Action
12
+ SYM = ' (){},~–\'![]_#@=„“”"`—+‘’;·‡«»%…'.chars.to_set.freeze
13
+
14
+ def do(src)
15
+ src.chars.map { |s| SYM.include?(s) ? '-' : s }.join
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require 'optparse'
7
+
8
+ module Renamr
9
+ # Handles input parameters.
10
+ class Configurator
11
+ DIC = [
12
+ ['-a', '--act', 'Real renaming.', :act],
13
+ ['-r', '--rec', 'Passes recursively.', :rec],
14
+ ['-l', '--lim', 'Limits name length.', :lim],
15
+ ['-m', '--mod', 'Prepends modification time.', :mod],
16
+ ['-d', '--dir dir', 'Directory to rename.', :dir],
17
+ ['-s', '--src src', 'A string to substitute.', :src],
18
+ ['-t', '--dst dst', 'A string to replace to.', :dst],
19
+ ['-p', '--pre pre', 'A string to prepend to.', :pre],
20
+ ['-w', '--wid wid', 'Width of the table.', :wid]
21
+ ].freeze
22
+
23
+ def initialize
24
+ @options = {}
25
+ OptionParser.new do |o|
26
+ o.banner = "Usage: #{File.basename($PROGRAM_NAME)} [options]."
27
+ DIC.each { |f, p, d, k| o.on(f, p, d) { |i| @options[k] = i } }
28
+ o.on('-c', '--cut pos,len', Array, 'Removes symbols from pos.') do |l|
29
+ @options[:pos] = l[0]
30
+ @options[:len] = l[1]
31
+ end
32
+ end.parse!
33
+ validate
34
+ end
35
+
36
+ def validate
37
+ if dir.nil?
38
+ @options[:dir] = Dir.pwd
39
+ else
40
+ raise "No such directory: #{dir}." unless File.directory?(dir)
41
+
42
+ @options[:dir] = File.expand_path(dir)
43
+ end
44
+ raise "Width of the table should exeeds 14 symbols: #{wid}." if wid < 15
45
+ end
46
+
47
+ def act?
48
+ @options[:act]
49
+ end
50
+
51
+ def rec?
52
+ @options[:rec]
53
+ end
54
+
55
+ def lim?
56
+ @options[:lim]
57
+ end
58
+
59
+ def mod?
60
+ @options[:mod]
61
+ end
62
+
63
+ def dir
64
+ @options[:dir]
65
+ end
66
+
67
+ def src
68
+ @options[:src]
69
+ end
70
+
71
+ def dst
72
+ @options[:dst]
73
+ end
74
+
75
+ def pre
76
+ @options[:pre]
77
+ end
78
+
79
+ def pos
80
+ @options[:pos]
81
+ end
82
+
83
+ def len
84
+ @options[:len]
85
+ end
86
+
87
+ def wid
88
+ if @options[:wid].nil?
89
+ # Reads current terminal width.
90
+ wid = `tput cols`
91
+ wid.to_s.empty? ? 79 : wid.to_i
92
+ else
93
+ @options[:wid].to_i
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require_relative 'action'
7
+
8
+ module Renamr
9
+ # All names should be downcased.
10
+ class DowncaseAction < Action
11
+ def do(src)
12
+ src.downcase
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require_relative 'action'
7
+
8
+ module Renamr
9
+ # Adds number from 0 to 9 in case of file existence.
10
+ class ExistenceAction < Action
11
+ ITERATION = 10
12
+
13
+ def initialize(dir, lim)
14
+ raise 'dir cannot be nil.' if dir.nil?
15
+ raise 'lim cannot be nil.' if lim.nil?
16
+
17
+ @dir = dir
18
+ @lim = lim
19
+ end
20
+
21
+ def do(src) # rubocop:disable MethodLength, CyclomaticComplexity, AbcSize
22
+ raise 'ExistenceAction needs original file name.' if @src.nil?
23
+ return src if src == @src
24
+ return src unless File.exist?(File.join(@dir, src))
25
+
26
+ if src.length == @lim
27
+ ext = File.extname(src)
28
+ src = src[0..@lim - ext.length - ITERATION.to_s.length + 1]
29
+ src << ext
30
+ end
31
+ nme = File.basename(src, '.*')
32
+ nme = '' if nme.length == 1
33
+ ext = File.extname(src)
34
+ (0..ITERATION).each do |i|
35
+ n = File.join(@dir, nme + i.to_s + ext)
36
+ return n unless File.exist?(n)
37
+ end
38
+ raise "Unable to compose a new name: #{src}."
39
+ end
40
+
41
+ def set(src)
42
+ @src = src
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require_relative 'ascii_validator'
7
+ require_relative 'auto_localization'
8
+ require_relative 'char'
9
+ require_relative 'downcase'
10
+ require_relative 'existence'
11
+ require_relative 'manual_localization'
12
+ require_relative 'omit'
13
+ require_relative 'point'
14
+ require_relative 'prepend'
15
+ require_relative 'prepend_date'
16
+ require_relative 'remove'
17
+ require_relative 'rutoen'
18
+ require_relative 'substitute'
19
+ require_relative 'trim'
20
+ require_relative 'truncate'
21
+
22
+ module Renamr
23
+ # Produces actions for certain directories.
24
+ class ActionsFactory
25
+ LIMIT = 143 # Synology eCryptfs limitation.
26
+
27
+ def initialize(cfg)
28
+ @cfg = cfg
29
+ end
30
+
31
+ def produce(dir) # rubocop:disable MethodLength, AbcSize
32
+ if @cfg.lim?
33
+ [
34
+ OmitAction.new(LIMIT),
35
+ TruncateAction.new(LIMIT)
36
+ ]
37
+ else
38
+ [
39
+ PointAction.new(dir), # Should be the first.
40
+ @cfg.pos.nil? ? nil : RemoveAction.new(@cfg.pos, @cfg.len),
41
+ @cfg.src.nil? ? nil : SubstituteAction.new(@cfg.src, @cfg.dst),
42
+ ManualLocalizationAction.new,
43
+ DowncaseAction.new,
44
+ CharAction.new,
45
+ RuToEnAction.new,
46
+ AutoLocalizationAction.new,
47
+ @cfg.mod? ? PrependDateAction.new(dir) : nil,
48
+ @cfg.pre.nil? ? nil : PrependAction.new(@cfg.pre),
49
+ ASCIIValidatorAction.new,
50
+ TrimAction.new,
51
+ TruncateAction.new(LIMIT),
52
+ ExistenceAction.new(dir, LIMIT)
53
+ ].compact
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require_relative 'action'
7
+
8
+ module Renamr
9
+ # Manual localization.
10
+ class ManualLocalizationAction < Action
11
+ SRC = 'ÀÁÂÃÄÅàáâãäåĀāĂ㥹ÇçĆćĈĉĊċČčÐðĎďĐđ'\
12
+ 'ÈÉÊËèéêëĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħÌÍÎÏ'\
13
+ 'ìíîïĨĩĪīĬĭĮįİıĴĵĶķĸĹĺĻļĽľĿŀŁłÑñŃńŅ'\
14
+ 'ņŇňʼnŊŋÒÓÔÕÖØòóôõöøŌōŎŏŐőŔŕŖŗŘřŚśŜŝ'\
15
+ 'ŞşŠšſŢţŤťŦŧÙÚÛÜùúûüŨũŪūŬŭŮůŰűŲųŴŵÝ'\
16
+ 'ýÿŶŷŸŹźŻżŽž'
17
+ DST = 'AAAAAAaaaaaaAaAaAaCcCcCcCcCcDdDdDd'\
18
+ 'EEEEeeeeEeEeEeEeEeGgGgGgGgHhHhIIII'\
19
+ 'iiiiIiIiIiIiIiJjKkkLlLlLlLlLlNnNnN'\
20
+ 'nNnnNnOOOOOOooooooOoOoOoRrRrRrSsSs'\
21
+ 'SsSssTtTtTtUUUUuuuuUuUuUuUuUuUuWwY'\
22
+ 'yyYyYZzZzZz'
23
+
24
+ def do(src)
25
+ src.tr(SRC, DST)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require_relative 'action'
7
+
8
+ module Renamr
9
+ # Omits file names shorter than limit.
10
+ class OmitAction < Action
11
+ def initialize(lim)
12
+ raise 'lim cannot be nil.' if lim.nil?
13
+
14
+ @lim = lim
15
+ end
16
+
17
+ def do(src)
18
+ src.length < @lim ? nil : src
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require_relative 'action'
7
+
8
+ module Renamr
9
+ # All points besides extention are replaced by minus.
10
+ class PointAction < Action
11
+ def initialize(dir)
12
+ raise 'dir cannot be nil.' if dir.nil?
13
+
14
+ @dir = dir
15
+ end
16
+
17
+ def do(src)
18
+ if File.file?(File.join(@dir, src))
19
+ p2m(File.basename(src, '.*')) << File.extname(src)
20
+ else
21
+ p2m(src)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require_relative 'action'
7
+
8
+ module Renamr
9
+ # Prepends user patter.
10
+ class PrependAction < Action
11
+ def initialize(pat)
12
+ raise 'pat cannot be nil.' if pat.nil?
13
+
14
+ @pat = pat
15
+ end
16
+
17
+ def do(src)
18
+ src.prepend(@pat)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require_relative 'action'
7
+
8
+ module Renamr
9
+ # Prepends file modification datestamp.
10
+ class PrependDateAction < Action
11
+ def initialize(dir)
12
+ @dir = dir
13
+ end
14
+
15
+ def do(src)
16
+ src.prepend(File.mtime(File.join(@dir, @src)).strftime('%Y%m%d-'))
17
+ end
18
+
19
+ def set(src)
20
+ @src = src
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require_relative 'action'
7
+
8
+ module Renamr
9
+ # Removes symbols between left and right positions.
10
+ class RemoveAction < Action
11
+ def initialize(pos, len)
12
+ raise 'len cannot bi nil.' if len.nil?
13
+ raise 'pos cannot be nil.' if pos.nil?
14
+ raise 'pos has to be positive.' unless pos.to_i.positive?
15
+
16
+ @pos = pos.to_i
17
+ @len = len.to_i
18
+ end
19
+
20
+ def do(src)
21
+ return src[@len..-1] if @pos == 1
22
+
23
+ src[0..@pos - 1] + src[@pos + @len..-1]
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require 'fileutils'
7
+ require_relative 'configurator'
8
+ require_relative 'factory'
9
+ require_relative 'reporter'
10
+
11
+ module Renamr
12
+ # Renames file by certain rules.
13
+ class Renamer
14
+ def initialize
15
+ @cfg = Configurator.new
16
+ @fac = ActionsFactory.new(@cfg)
17
+ end
18
+
19
+ def move(dir, dat) # rubocop:disable MethodLength, AbcSize
20
+ rep = Reporter.new(File.expand_path(dir))
21
+ dat.each do |src, dst|
22
+ if src == dst
23
+ rep.add(File.basename(src), '')
24
+ next
25
+ end
26
+ begin
27
+ FileUtils.mv(src, dst) if @cfg.act?
28
+ rep.add(File.basename(src), File.basename(dst))
29
+ rescue StandardError => e
30
+ rep.add(File.basename(src), e)
31
+ puts e.backtrace.join("\n\t")
32
+ .sub("\n\t", ": #{e}#{e.class ? " (#{e.class})" : ''}\n\t")
33
+ end
34
+ end
35
+ rep.do
36
+ end
37
+
38
+ def do_dir(dir) # rubocop:disable MethodLength, CyclomaticComplexity, AbcSize
39
+ raise "No such directory: #{dir}." unless File.directory?(dir)
40
+
41
+ dat = []
42
+ act = @fac.produce(dir)
43
+ (Dir.entries(dir) - ['.', '..']).sort.each do |nme|
44
+ src = File.join(dir, nme)
45
+ do_dir(src) if @cfg.rec? && File.directory?(src)
46
+ act.each { |a| a.set(nme) }
47
+ act.each { |a| break if (nme = a.do(nme)).nil? }
48
+ dat << [src, File.join(dir, nme)] unless nme.nil?
49
+ end
50
+ move(dir, dat) if dat.any?
51
+ end
52
+
53
+ def do
54
+ Reporter.init(@cfg.act?, @cfg.wid)
55
+ do_dir(@cfg.dir)
56
+ Reporter.final
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require 'terminal-table'
7
+ require_relative 'action'
8
+ require_relative 'utils'
9
+
10
+ module Renamr
11
+ # Formats and prints output data.
12
+ class Reporter
13
+ @@tim = Timer.new
14
+ @@sta = { moved: 0, unaltered: 0, failed: 0 }
15
+
16
+ def self.init(act, wid)
17
+ @@act = act
18
+ @@tbl = wid
19
+ @@ttl = wid - 4
20
+ @@str = (wid - 7) / 2
21
+ end
22
+
23
+ def initialize(dir)
24
+ @dir = dir
25
+ @row = []
26
+ end
27
+
28
+ def add(lhs, rhs)
29
+ if rhs.is_a?(StandardError)
30
+ tag = :failed
31
+ rhs = "#{rhs.message} (#{rhs.class})"
32
+ elsif rhs == ''
33
+ tag = :unaltered
34
+ else
35
+ tag = :moved
36
+ end
37
+ @@sta[tag] += 1
38
+ @row << [Utils.trim(lhs, @@str), Utils.trim(rhs, @@str)]
39
+ end
40
+
41
+ def do
42
+ puts Terminal::Table.new(
43
+ title: Utils.trim(@dir, @@ttl),
44
+ headings: [
45
+ { value: 'src', alignment: :center },
46
+ { value: 'dst', alignment: :center }
47
+ ],
48
+ rows: @row,
49
+ style: { width: @@tbl }
50
+ )
51
+ end
52
+
53
+ def self.stat_out
54
+ out = ''
55
+ @@sta.each do |k, v|
56
+ out += ' ' + v.to_s + ' ' + k.to_s + ',' if v.positive?
57
+ end
58
+ out.chop
59
+ end
60
+
61
+ def self.final
62
+ msg = "#{@@act ? 'Real' : 'Test'}:#{stat_out} in #{@@tim.read}."
63
+ msg = Utils.trim(msg, @@ttl)
64
+ puts "| #{msg}#{' ' * (@@ttl - msg.length)} |\n+-#{'-' * @@ttl}-+"
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require_relative 'action'
7
+
8
+ module Renamr
9
+ # Transliterates Russian to English.
10
+ class RuToEnAction < Action
11
+ MSC = {
12
+ 'ё' => 'jo',
13
+ 'ж' => 'zh',
14
+ 'ц' => 'tz',
15
+ 'ч' => 'ch',
16
+ 'ш' => 'sh',
17
+ 'щ' => 'szh',
18
+ 'ю' => 'ju',
19
+ 'я' => 'ya',
20
+ '$' => '-usd-',
21
+ '№' => '-num-',
22
+ '&' => '-and-'
23
+ }.freeze
24
+ SRC = 'абвгдезийклмнопрстуфхъыьэ'.chars.freeze
25
+ DST = 'abvgdeziyklmnoprstufh y e'.chars.freeze
26
+ DIC = SRC.zip(DST).to_h.merge(MSC).freeze
27
+
28
+ def do(src)
29
+ src.chars.map { |c| DIC[c].nil? ? c : DIC[c] }.collect(&:strip).join
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require_relative 'action'
7
+
8
+ module Renamr
9
+ # Substitutes a string with a string.
10
+ class SubstituteAction < Action
11
+ def initialize(src, dst)
12
+ raise 'src cannot be nil.' if src.nil?
13
+
14
+ # The action works after PointAction. All points are replaces with minus.
15
+ @src = p2m(src)
16
+ @dst = dst.nil? ? '-' : p2m(dst)
17
+ end
18
+
19
+ def do(src)
20
+ src.gsub(@src, @dst)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require_relative 'action'
7
+
8
+ module Renamr
9
+ # Replaces multiple minuses to single. Trims minuses.
10
+ class TrimAction < Action
11
+ def do(src)
12
+ src.gsub!(/-+/, '-')
13
+ src.gsub!('-.', '.')
14
+ src.gsub!('.-', '.')
15
+ src.gsub!(/^-|-$/, '') unless src == '-'
16
+ src
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require_relative 'action'
7
+
8
+ module Renamr
9
+ # Limits file length.
10
+ class TruncateAction < Action
11
+ def initialize(lim)
12
+ raise 'lim cannot be nil.' if lim.nil?
13
+
14
+ @lim = lim
15
+ end
16
+
17
+ def do(src)
18
+ return src unless src.length > @lim
19
+
20
+ ext = File.extname(src)
21
+ len = ext.length
22
+ dst = len >= @lim ? ext[0..@lim - 1] : src[0..@lim - 1 - len] << ext
23
+ dst.gsub!(/-$/, '')
24
+ dst.gsub!('-.', '.')
25
+ dst
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ # All methods are static.
7
+ class Utils
8
+ class << self
9
+ SEP = '~'
10
+ def trim(src, lim)
11
+ return src if src.length <= lim
12
+
13
+ beg = fin = (lim - SEP.length) / 2
14
+ beg -= 1 if lim.odd?
15
+ src[0..beg] + SEP + src[-fin..-1]
16
+ end
17
+ end
18
+ end
19
+
20
+ # Returns string with humanized time interval.
21
+ class Timer
22
+ DIC = [
23
+ [60, :seconds, :second],
24
+ [60, :minutes, :minute],
25
+ [24, :hours, :hour],
26
+ [1000, :days, :day]
27
+ ].freeze
28
+
29
+ def initialize
30
+ @sta = Time.now
31
+ end
32
+
33
+ def read
34
+ humanize(Time.now - @sta)
35
+ end
36
+
37
+ def humanize(sec)
38
+ return 'less than a second' if sec < 1
39
+
40
+ DIC.map do |cnt, nms, nm1|
41
+ next if sec <= 0
42
+
43
+ sec, n = sec.divmod(cnt)
44
+ "#{n.to_i} #{n.to_i != 1 ? nms : nm1}"
45
+ end.compact.reverse.join(' ')
46
+ end
47
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ module Renamr
7
+ VERSION = '1.0.2'
8
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/lib')
4
+
5
+ require 'renamr'
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = 'renamr'
9
+ s.version = Renamr::VERSION
10
+ s.required_ruby_version = '>= 2.4'
11
+ s.summary = 'File and directory names organiser.'
12
+ s.description = <<-HERE
13
+ Renamr organises multiple files and directories.
14
+ HERE
15
+ s.license = 'BSD-2-Clause'
16
+ s.author = 'David Rabkin'
17
+ s.email = 'pub@rabkin.co.il'
18
+ s.homepage = 'https://github.com/rdavid/renamr'
19
+ s.files = Dir['{bin,lib}/**/*'] + Dir['[A-Z]*'] + ['renamr.gemspec']
20
+ s.executables = ['renamr']
21
+ s.extra_rdoc_files = ['LICENSE', 'README.md']
22
+ s.require_paths = ['lib']
23
+ s.add_runtime_dependency 'fileutils', '1.4.1'
24
+ s.add_runtime_dependency 'i18n', '1.8.3'
25
+ s.add_runtime_dependency 'pidfile', '0.3.0'
26
+ s.add_runtime_dependency 'terminal-table', '1.8.0'
27
+ s.add_development_dependency 'rubocop', '0.85.1'
28
+ end
metadata ADDED
@@ -0,0 +1,142 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: renamr
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.2
5
+ platform: ruby
6
+ authors:
7
+ - David Rabkin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-06-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: fileutils
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.4.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 1.4.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: i18n
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 1.8.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 1.8.3
41
+ - !ruby/object:Gem::Dependency
42
+ name: pidfile
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 0.3.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 0.3.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: terminal-table
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.8.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 1.8.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '='
74
+ - !ruby/object:Gem::Version
75
+ version: 0.85.1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '='
81
+ - !ruby/object:Gem::Version
82
+ version: 0.85.1
83
+ description: " Renamr organises multiple files and directories.\n"
84
+ email: pub@rabkin.co.il
85
+ executables:
86
+ - renamr
87
+ extensions: []
88
+ extra_rdoc_files:
89
+ - LICENSE
90
+ - README.md
91
+ files:
92
+ - LICENSE
93
+ - README.md
94
+ - bin/renamr
95
+ - lib/renamr.rb
96
+ - lib/renamr/action.rb
97
+ - lib/renamr/ascii_validator.rb
98
+ - lib/renamr/auto_localization.rb
99
+ - lib/renamr/char.rb
100
+ - lib/renamr/configurator.rb
101
+ - lib/renamr/downcase.rb
102
+ - lib/renamr/existence.rb
103
+ - lib/renamr/factory.rb
104
+ - lib/renamr/manual_localization.rb
105
+ - lib/renamr/omit.rb
106
+ - lib/renamr/point.rb
107
+ - lib/renamr/prepend.rb
108
+ - lib/renamr/prepend_date.rb
109
+ - lib/renamr/remove.rb
110
+ - lib/renamr/renamer.rb
111
+ - lib/renamr/reporter.rb
112
+ - lib/renamr/rutoen.rb
113
+ - lib/renamr/substitute.rb
114
+ - lib/renamr/trim.rb
115
+ - lib/renamr/truncate.rb
116
+ - lib/renamr/utils.rb
117
+ - lib/renamr/version.rb
118
+ - renamr.gemspec
119
+ homepage: https://github.com/rdavid/renamr
120
+ licenses:
121
+ - BSD-2-Clause
122
+ metadata: {}
123
+ post_install_message:
124
+ rdoc_options: []
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '2.4'
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ requirements: []
138
+ rubygems_version: 3.1.4
139
+ signing_key:
140
+ specification_version: 4
141
+ summary: File and directory names organiser.
142
+ test_files: []