pdfh 0.2.1 → 3.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 +4 -4
- data/.rspec +1 -1
- data/.rubocop.yml +2 -2
- data/.rubocop_todo.yml +24 -16
- data/.tool-versions +1 -0
- data/CHANGELOG.md +20 -15
- data/Gemfile +5 -4
- data/Gemfile.lock +80 -42
- data/README.md +21 -7
- data/bin/console +1 -1
- data/bin/run +1 -1
- data/exe/pdfh +1 -1
- data/lib/pdfh/main.rb +93 -0
- data/lib/pdfh/{document.rb → models/document.rb} +34 -17
- data/lib/pdfh/{document_type.rb → models/document_type.rb} +18 -5
- data/lib/pdfh/{settings.rb → models/settings.rb} +23 -15
- data/lib/pdfh/settings_template.rb +11 -11
- data/lib/pdfh/utils/console.rb +101 -0
- data/lib/pdfh/utils/opt_parser.rb +56 -0
- data/lib/pdfh/utils/options.rb +38 -0
- data/lib/pdfh/utils/pdf_file_handler.rb +121 -0
- data/lib/pdfh/utils/settings_builder.rb +57 -0
- data/lib/pdfh/version.rb +1 -1
- data/lib/pdfh.rb +29 -118
- data/pdfh.gemspec +2 -2
- metadata +16 -13
- data/.ruby-version +0 -1
- data/lib/pdfh/document_processor.rb +0 -164
- data/lib/pdfh/opt_parser.rb +0 -41
- data/lib/pdfh/pdf_handler.rb +0 -55
- /data/lib/pdfh/{document_period.rb → models/document_period.rb} +0 -0
- /data/lib/pdfh/{month.rb → utils/month.rb} +0 -0
@@ -1,26 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "yaml"
|
4
|
-
|
5
3
|
module Pdfh
|
6
4
|
# Handles the config yaml data mapping, and associates a file name with a doc type
|
7
5
|
class Settings
|
8
|
-
attr_reader :lookup_dirs, :base_path
|
6
|
+
attr_reader :lookup_dirs, :base_path
|
9
7
|
|
10
|
-
# @param
|
8
|
+
# @param config_data [Hash]
|
11
9
|
# @return [self]
|
12
|
-
def initialize(
|
13
|
-
|
14
|
-
|
10
|
+
def initialize(config_data)
|
11
|
+
process_lookup_dirs(config_data[:lookup_dirs])
|
12
|
+
process_destination_base(config_data[:destination_base_path])
|
13
|
+
|
14
|
+
Pdfh.debug "Configured Look up directories:"
|
15
|
+
lookup_dirs.each.with_index(1) { |dir, idx| Pdfh.debug " #{idx}. #{dir}" }
|
16
|
+
Pdfh.debug
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
+
load_doc_types(config_data[:document_types])
|
19
|
+
end
|
18
20
|
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
# @return [Array<DocumentType>]
|
22
|
+
def document_types
|
23
|
+
@document_types.values
|
24
|
+
end
|
22
25
|
|
23
|
-
|
26
|
+
# @return [DocumentType]
|
27
|
+
def document_type(id)
|
28
|
+
@document_types[id]
|
24
29
|
end
|
25
30
|
|
26
31
|
private
|
@@ -30,7 +35,7 @@ module Pdfh
|
|
30
35
|
@lookup_dirs = lookup_dirs_list.filter_map do |dir|
|
31
36
|
expanded = File.expand_path(dir)
|
32
37
|
unless File.directory?(expanded)
|
33
|
-
Pdfh.
|
38
|
+
Pdfh.debug " ** Error, Directory #{dir} does not exists."
|
34
39
|
next
|
35
40
|
end
|
36
41
|
expanded
|
@@ -47,7 +52,10 @@ module Pdfh
|
|
47
52
|
|
48
53
|
# @return [Array<DocumentType>]
|
49
54
|
def load_doc_types(doc_types)
|
50
|
-
doc_types.
|
55
|
+
@document_types = doc_types.each_with_object({}) do |data, result|
|
56
|
+
doc_type = DocumentType.new(data)
|
57
|
+
result.store(doc_type.gid, doc_type)
|
58
|
+
end
|
51
59
|
end
|
52
60
|
end
|
53
61
|
end
|
@@ -2,20 +2,20 @@
|
|
2
2
|
|
3
3
|
module Pdfh
|
4
4
|
# rubocop:disable Layout/HashAlignment
|
5
|
+
DOCUMENT_TYPE_TEMPLATE = {
|
6
|
+
"name" => "Example Name",
|
7
|
+
"re_file" => ".*file_name\.pdf",
|
8
|
+
"re_date" => "(\d{2})\/(?<m>\w+)\/(?<y>\d{4})",
|
9
|
+
"pwd" => "BASE64_STRING",
|
10
|
+
"store_path" => "{YEAR}/sub folder",
|
11
|
+
"name_template" => "{period} {original}",
|
12
|
+
"sub_types" => []
|
13
|
+
}.freeze
|
14
|
+
|
5
15
|
SETTINGS_TEMPLATE = {
|
6
16
|
"lookup_dirs" => ["~/Downloads"].freeze,
|
7
17
|
"destination_base_path" => "~/Documents",
|
8
|
-
"document_types" => [
|
9
|
-
{
|
10
|
-
"name" => "Example Name",
|
11
|
-
"re_file" => ".*file_name\.pdf",
|
12
|
-
"re_date" => "(\d{2})\/(?<m>\w+)\/(?<y>\d{4})",
|
13
|
-
"pwd" => "BASE64_STRING",
|
14
|
-
"store_path" => "{YEAR}/sub folder",
|
15
|
-
"name_template" => "{period} {original}",
|
16
|
-
"sub_types" => []
|
17
|
-
}.freeze
|
18
|
-
].freeze
|
18
|
+
"document_types" => [DOCUMENT_TYPE_TEMPLATE].freeze
|
19
19
|
}.freeze
|
20
20
|
# rubocop:enable Layout/HashAlignment
|
21
21
|
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pdfh
|
4
|
+
# All console output formats
|
5
|
+
class Console
|
6
|
+
# @return [self]
|
7
|
+
def initialize(verbose)
|
8
|
+
@verbose = verbose
|
9
|
+
end
|
10
|
+
|
11
|
+
# @return [void]
|
12
|
+
def debug(message = nil)
|
13
|
+
msg = message.to_s
|
14
|
+
msg = msg.colorize(:cyan) unless msg.colorized?
|
15
|
+
output(msg) if verbose?
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [void]
|
19
|
+
def info(message)
|
20
|
+
output(message)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Prints visual separator in shell for easier reading for humans
|
24
|
+
# @example output
|
25
|
+
# ——— Title ——————————————— ... ——————
|
26
|
+
# @param title [String]
|
27
|
+
# @return [void]
|
28
|
+
def headline(title)
|
29
|
+
_, cols = console_size
|
30
|
+
line_length = cols - (title.size + 5)
|
31
|
+
left = ("—" * 3).to_s.red
|
32
|
+
right = ("—" * line_length).to_s.red
|
33
|
+
output "\n#{left} #{title.colorize(color: :blue, mode: :bold)} #{right}"
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param message [String]
|
37
|
+
# @param exit_app [Boolean] exit application if true (default)
|
38
|
+
# @return [void]
|
39
|
+
def error_print(message, exit_app: true)
|
40
|
+
output "Error, #{message}".colorize(:red)
|
41
|
+
exit 1 if exit_app
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param message [String]
|
45
|
+
# @return [void]
|
46
|
+
def warn_print(message)
|
47
|
+
output "Warning, #{message}".colorize(:yellow)
|
48
|
+
end
|
49
|
+
|
50
|
+
# @example usage
|
51
|
+
# ident_print("Name", "iax")
|
52
|
+
# # => Name: "iax"
|
53
|
+
# @return [void]
|
54
|
+
def ident_print(field, value, color: :green, width: 3)
|
55
|
+
field_str = field.to_s.rjust(width)
|
56
|
+
value_str = value.colorize(color)
|
57
|
+
output "#{" " * 4}#{field_str}: #{value_str}"
|
58
|
+
end
|
59
|
+
|
60
|
+
# Show options used to run the current sync job
|
61
|
+
# @param options [Hash]
|
62
|
+
# @return [void]
|
63
|
+
def print_options(options) # rubocop:disable Metrics/CyclomaticComplexity
|
64
|
+
max_size = options.keys.map(&:size).max + 3
|
65
|
+
options.each do |key, value|
|
66
|
+
left = key.inspect.rjust(max_size).cyan
|
67
|
+
right = case value
|
68
|
+
when NilClass then value.inspect.colorize(color: :black, mode: :bold)
|
69
|
+
when TrueClass then value.inspect.colorize(color: :green, mode: :bold)
|
70
|
+
when FalseClass then value.inspect.colorize(color: :red, mode: :bold)
|
71
|
+
when Symbol then value.inspect.yellow
|
72
|
+
when String then value.inspect.light_magenta
|
73
|
+
else
|
74
|
+
value.inspect.red
|
75
|
+
end
|
76
|
+
debug "#{left} => #{right}"
|
77
|
+
end
|
78
|
+
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
# @return [boolean]
|
85
|
+
def verbose?
|
86
|
+
@verbose
|
87
|
+
end
|
88
|
+
|
89
|
+
# @return [void]
|
90
|
+
def output(msg)
|
91
|
+
puts(msg)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns rows, cols
|
95
|
+
# TODO: review https://gist.github.com/nixpulvis/6025433
|
96
|
+
# @return [Array<Integer, Integer>]
|
97
|
+
def console_size
|
98
|
+
`stty size`.split.map(&:to_i)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "optparse"
|
4
|
+
|
5
|
+
module Pdfh
|
6
|
+
# Handles Argument options
|
7
|
+
class OptParser
|
8
|
+
OPT_PARSER = OptionParser.new do |opts|
|
9
|
+
opts.default_argv
|
10
|
+
# Process ARGV
|
11
|
+
opts.banner = "Usage: #{opts.program_name} [options] [file1 ...]"
|
12
|
+
opts.separator ""
|
13
|
+
opts.separator "Specific options:"
|
14
|
+
|
15
|
+
opts.on("-tID", "--type=ID", "Document type id (requires a trailing file list)")
|
16
|
+
opts.on_tail("-T", "--list-types", "List document types in configuration") do
|
17
|
+
settings = SettingsBuilder.build
|
18
|
+
ident = 4
|
19
|
+
max_width = settings.document_types.map { |t| t.gid.size }.max
|
20
|
+
puts "#{" " * ident}#{"ID".ljust(max_width)} Type Name"
|
21
|
+
puts "#{" " * ident}#{"—" * max_width} #{"—" * 23}"
|
22
|
+
settings.document_types.each do |type|
|
23
|
+
puts "#{" " * ident}#{type.gid.ljust(max_width).yellow} #{type.name.inspect}"
|
24
|
+
end
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
opts.on_tail("-V", "--version", "Show version") do
|
28
|
+
puts "#{opts.program_name} v#{Pdfh::VERSION}"
|
29
|
+
exit
|
30
|
+
end
|
31
|
+
opts.on_tail("-h", "--help", "help (this dialog)") do
|
32
|
+
puts opts
|
33
|
+
exit
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on("-v", "--verbose", "Show more output. Useful for debug")
|
37
|
+
opts.on("-d", "--dry", "Dry run, does not write new pdf")
|
38
|
+
end
|
39
|
+
|
40
|
+
class << self
|
41
|
+
# @return [Hash]
|
42
|
+
def parse_argv
|
43
|
+
Pdfh.instance_variable_set(:@console, Console.new(false))
|
44
|
+
|
45
|
+
options = { dry: false, verbose: false }
|
46
|
+
OPT_PARSER.parse!(into: options)
|
47
|
+
options[:files] = ARGV if ARGV.any?
|
48
|
+
options.transform_keys { |key| key.to_s.tr("-", "_").to_sym }
|
49
|
+
rescue OptionParser::InvalidOption => e
|
50
|
+
error_print e.message, exit_app: false
|
51
|
+
puts OPT_PARSER.help
|
52
|
+
exit 1
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pdfh
|
4
|
+
# Argument Options object container
|
5
|
+
class Options
|
6
|
+
attr_reader :type, :files
|
7
|
+
|
8
|
+
# @param arg_options [Hash]
|
9
|
+
# @return [self]
|
10
|
+
def initialize(arg_options)
|
11
|
+
@verbose = arg_options[:verbose]
|
12
|
+
@dry = arg_options[:dry]
|
13
|
+
@type = arg_options[:type]
|
14
|
+
@files = arg_options[:files]
|
15
|
+
@mode = type ? :file : :directory
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [Boolean]
|
19
|
+
def verbose?
|
20
|
+
@verbose
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Boolean]
|
24
|
+
def dry?
|
25
|
+
@dry
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Boolean]
|
29
|
+
def file_mode?
|
30
|
+
@mode == :file
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [Boolean]
|
34
|
+
def files?
|
35
|
+
!!@files&.any?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pdfh
|
4
|
+
# Handles the PDF file
|
5
|
+
class PdfFileHandler
|
6
|
+
attr_reader :file, :type, :document
|
7
|
+
|
8
|
+
# @param [String] file
|
9
|
+
# @param [DocumentType, nil] type
|
10
|
+
# @return [self]
|
11
|
+
def initialize(file, type)
|
12
|
+
@file = file
|
13
|
+
@type = type
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [boolean]
|
17
|
+
def type?
|
18
|
+
!!type
|
19
|
+
end
|
20
|
+
|
21
|
+
# Generate document, and process actions
|
22
|
+
# @return [void]
|
23
|
+
def process_document(base_path)
|
24
|
+
Pdfh.info "Working on #{base_name.colorize(:light_green)}"
|
25
|
+
raise IOError, "File #{file} not found" unless File.exist?(file)
|
26
|
+
|
27
|
+
@document = Document.new(file, type, extract_text)
|
28
|
+
document.print_info
|
29
|
+
write_pdf(base_path)
|
30
|
+
|
31
|
+
nil
|
32
|
+
rescue StandardError => e
|
33
|
+
Pdfh.ident_print "Doc Error", e.message, color: :red, width: 12
|
34
|
+
end
|
35
|
+
|
36
|
+
# Create a backup of original document
|
37
|
+
# @return [void]
|
38
|
+
def make_document_backup(document)
|
39
|
+
Pdfh.debug "~~~~~~~~~~~~~~~~~~ Creating PDF backup"
|
40
|
+
Dir.chdir(document.home_dir) do
|
41
|
+
Pdfh.debug " Working on: #{document.home_dir.inspect} directory"
|
42
|
+
Pdfh.debug " mv #{document.file_name.inspect} -> #{document.backup_name.inspect}"
|
43
|
+
File.rename(document.file_name, document.backup_name) unless Pdfh.dry?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [void]
|
48
|
+
def copy_companion_files(destination, document)
|
49
|
+
Pdfh.debug "~~~~~~~~~~~~~~~~~~ Writing Companion files"
|
50
|
+
document.companion_files.each do |file|
|
51
|
+
Pdfh.debug " Working on #{file.inspect}..."
|
52
|
+
src_name = File.join(document.home_dir, file)
|
53
|
+
src_ext = File.extname(file)
|
54
|
+
dest_name = File.basename(document.new_name, ".pdf")
|
55
|
+
dest_full = File.join(destination, "#{dest_name}#{src_ext}")
|
56
|
+
Pdfh.debug " cp #{src_name} --> #{dest_full}"
|
57
|
+
FileUtils.cp(src_name, dest_full) unless Pdfh.dry?
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# @return [String]
|
62
|
+
def base_name
|
63
|
+
File.basename(file)
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
# @return [void]
|
69
|
+
def write_pdf(base_path)
|
70
|
+
full_path = File.join(base_path, document.store_path, document.new_name)
|
71
|
+
dir_path = File.join(base_path, document.store_path)
|
72
|
+
|
73
|
+
FileUtils.mkdir_p(dir_path)
|
74
|
+
|
75
|
+
write_new_pdf(dir_path, full_path)
|
76
|
+
make_document_backup(document)
|
77
|
+
copy_companion_files(dir_path, document)
|
78
|
+
rescue StandardError => e
|
79
|
+
Pdfh.ident_print "Doc Error", e.message, color: :red, width: IDENT
|
80
|
+
end
|
81
|
+
|
82
|
+
def qpdf_command(*args)
|
83
|
+
password_option = type&.password ? "--password=#{type&.password.inspect} " : ""
|
84
|
+
|
85
|
+
%(qpdf #{password_option}--decrypt #{args.join(" ")})
|
86
|
+
end
|
87
|
+
|
88
|
+
# Gets the text from the pdf in order to execute
|
89
|
+
# the regular expresion matches
|
90
|
+
# @return [String]
|
91
|
+
def extract_text
|
92
|
+
temp = Tempfile.new("pdfh")
|
93
|
+
Pdfh.debug "~~~~~~~~~~~~~~~~~~ Extract PDF text"
|
94
|
+
Pdfh.debug " --> #{temp.path} temporal file assigned."
|
95
|
+
|
96
|
+
cmd1 = qpdf_command("--stream-data=uncompress", file.inspect, temp.path)
|
97
|
+
Pdfh.debug " DeCrypt Command: #{cmd1}"
|
98
|
+
_result = `#{cmd1}`
|
99
|
+
|
100
|
+
cmd2 = %(pdftotext -enc UTF-8 #{temp.path} -)
|
101
|
+
Pdfh.debug " Extract Command: #{cmd2}"
|
102
|
+
text = `#{cmd2}`
|
103
|
+
Pdfh.debug " Text: #{text.inspect}"
|
104
|
+
text
|
105
|
+
end
|
106
|
+
|
107
|
+
# @return [void]
|
108
|
+
def write_new_pdf(dir_path, full_path)
|
109
|
+
Pdfh.debug "~~~~~~~~~~~~~~~~~~ Writing PDFs"
|
110
|
+
raise IOError, "Path #{dir_path} not found." unless Dir.exist?(dir_path)
|
111
|
+
|
112
|
+
cmd = qpdf_command(file.inspect, full_path.inspect)
|
113
|
+
Pdfh.debug " Write PDF Command: #{cmd}"
|
114
|
+
|
115
|
+
return if Pdfh.dry?
|
116
|
+
|
117
|
+
_result = `#{cmd}`
|
118
|
+
raise IOError, "New PDF file #{full_path.inspect} was not created." unless File.file?(full_path)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pdfh
|
4
|
+
# Loads or creates a default settings yaml file
|
5
|
+
class SettingsBuilder
|
6
|
+
CONFIG_FILE_LOCATIONS = [Dir.pwd, File.expand_path("~")].freeze
|
7
|
+
SUPPORTED_EXTENSIONS = %w[yml yaml].freeze
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# @return [Pdfh::Settings]
|
11
|
+
def build
|
12
|
+
config_file = search_config_file
|
13
|
+
file_hash = YAML.load_file(config_file, symbolize_names: true)
|
14
|
+
Pdfh.debug "Loaded configuration file: #{config_file}"
|
15
|
+
|
16
|
+
Settings.new(file_hash)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# @return [String]
|
22
|
+
def config_file_name
|
23
|
+
File.basename($PROGRAM_NAME)
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [String (frozen)]
|
27
|
+
def default_settings_name
|
28
|
+
"#{config_file_name}.#{SUPPORTED_EXTENSIONS.first}"
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [String]
|
32
|
+
def create_settings_file
|
33
|
+
full_path = File.join(File.expand_path("~"), default_settings_name)
|
34
|
+
return if File.exist?(full_path) # double check
|
35
|
+
|
36
|
+
File.write(full_path, Pdfh::SETTINGS_TEMPLATE.to_yaml)
|
37
|
+
Pdfh.info "Default settings file was created: #{full_path.colorize(:green)}"
|
38
|
+
|
39
|
+
full_path
|
40
|
+
end
|
41
|
+
|
42
|
+
# Gets the first settings file found, or creates a new one
|
43
|
+
# @return [String]
|
44
|
+
def search_config_file
|
45
|
+
CONFIG_FILE_LOCATIONS.each do |dir|
|
46
|
+
SUPPORTED_EXTENSIONS.each do |ext|
|
47
|
+
path = File.join(dir, "#{config_file_name}.#{ext}")
|
48
|
+
return path if File.exist?(path)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
Pdfh.warn_print "No configuration file was found within paths: #{CONFIG_FILE_LOCATIONS.join(", ")}"
|
53
|
+
create_settings_file
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/pdfh/version.rb
CHANGED
data/lib/pdfh.rb
CHANGED
@@ -1,18 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "base64"
|
4
4
|
require "colorize"
|
5
|
-
|
6
|
-
require "
|
7
|
-
require "
|
8
|
-
require "
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
5
|
+
require "fileutils"
|
6
|
+
require "forwardable"
|
7
|
+
require "tempfile"
|
8
|
+
require "yaml"
|
9
|
+
|
10
|
+
require_relative "ext/string"
|
11
|
+
|
12
|
+
# Models
|
13
|
+
require_relative "pdfh/models/document"
|
14
|
+
require_relative "pdfh/models/document_period"
|
15
|
+
require_relative "pdfh/models/document_type"
|
16
|
+
require_relative "pdfh/models/settings"
|
17
|
+
|
18
|
+
# Utils
|
19
|
+
require_relative "pdfh/utils/console"
|
20
|
+
require_relative "pdfh/utils/month"
|
21
|
+
require_relative "pdfh/utils/opt_parser"
|
22
|
+
require_relative "pdfh/utils/options"
|
23
|
+
require_relative "pdfh/utils/pdf_file_handler"
|
24
|
+
require_relative "pdfh/utils/settings_builder"
|
25
|
+
|
26
|
+
require_relative "pdfh/main"
|
27
|
+
require_relative "pdfh/settings_template"
|
28
|
+
require_relative "pdfh/version"
|
16
29
|
|
17
30
|
# Gem entry point
|
18
31
|
module Pdfh
|
@@ -21,117 +34,15 @@ module Pdfh
|
|
21
34
|
|
22
35
|
# Regular Date Error, when there is not match
|
23
36
|
class ReDateError < StandardError
|
37
|
+
# @return [self]
|
24
38
|
def initialize(msg = "Date regular expression did not find a match in document.")
|
25
39
|
super
|
26
40
|
end
|
27
41
|
end
|
28
42
|
|
29
43
|
class << self
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
def verbose?
|
34
|
-
@verbose
|
35
|
-
end
|
36
|
-
|
37
|
-
# @return [Boolean]
|
38
|
-
def dry?
|
39
|
-
@dry
|
40
|
-
end
|
41
|
-
|
42
|
-
# @return [Boolean]
|
43
|
-
def file_mode?
|
44
|
-
@mode == :file
|
45
|
-
end
|
46
|
-
|
47
|
-
# Returns rows, cols
|
48
|
-
# TODO: review https://gist.github.com/nixpulvis/6025433
|
49
|
-
# @return [Array<Integer, Integer>]
|
50
|
-
def console_size
|
51
|
-
`stty size`.split.map(&:to_i)
|
52
|
-
end
|
53
|
-
|
54
|
-
# Prints visual separator in shell for easier reading for humans
|
55
|
-
# @example output
|
56
|
-
# [Title Text] -----------------------
|
57
|
-
# @param msg [String]
|
58
|
-
# @return [void]
|
59
|
-
def headline(msg)
|
60
|
-
_, cols = console_size
|
61
|
-
line_length = cols - (msg.size + 5)
|
62
|
-
left = "\033[31m#{"—" * 3}\033[0m"
|
63
|
-
right = "\033[31m#{"—" * line_length}\033[0m"
|
64
|
-
puts "\n#{left} \033[1;34m#{msg}\033[0m #{right}"
|
65
|
-
end
|
66
|
-
|
67
|
-
# @param msg [Object]
|
68
|
-
# @return [void]
|
69
|
-
def verbose_print(msg = nil)
|
70
|
-
puts msg.to_s.colorize(:cyan) if verbose?
|
71
|
-
end
|
72
|
-
|
73
|
-
# @param message [String]
|
74
|
-
# @param exit_app [Boolean] exit application if true (default)
|
75
|
-
# @return [void]
|
76
|
-
def error_print(message, exit_app: true)
|
77
|
-
puts "Error, #{message}".colorize(:red)
|
78
|
-
exit 1 if exit_app
|
79
|
-
end
|
80
|
-
|
81
|
-
# @param message [String]
|
82
|
-
# @return [void]
|
83
|
-
def warn_print(message)
|
84
|
-
puts message.colorize(:yellow)
|
85
|
-
end
|
86
|
-
|
87
|
-
# @return [void]
|
88
|
-
def ident_print(field, value, color: :green, width: 3)
|
89
|
-
field_str = field.to_s.rjust(width)
|
90
|
-
value_str = value.colorize(color)
|
91
|
-
puts "#{" " * 4}#{field_str}: #{value_str}"
|
92
|
-
end
|
93
|
-
|
94
|
-
# @return [Hash]
|
95
|
-
def parse_argv
|
96
|
-
options = {}
|
97
|
-
OPT_PARSER.parse!(into: options)
|
98
|
-
options[:files] = ARGV if ARGV.any?
|
99
|
-
options.transform_keys { |key| key.to_s.tr("-", "_").to_sym }
|
100
|
-
rescue OptionParser::InvalidOption => e
|
101
|
-
error_print e.message, exit_app: false
|
102
|
-
puts OPT_PARSER.help
|
103
|
-
exit 1
|
104
|
-
end
|
105
|
-
|
106
|
-
# @return [String]
|
107
|
-
def config_file_name
|
108
|
-
File.basename($PROGRAM_NAME)
|
109
|
-
end
|
110
|
-
|
111
|
-
# @return [void]
|
112
|
-
def create_settings_file
|
113
|
-
full_path = File.join(File.expand_path("~"), "#{config_file_name}.yml")
|
114
|
-
return if File.exist?(full_path) # double check
|
115
|
-
|
116
|
-
File.write(full_path, Pdfh::SETTINGS_TEMPLATE.to_yaml)
|
117
|
-
puts "Settings #{full_path.inspect.colorize(:green)} was created."
|
118
|
-
end
|
119
|
-
|
120
|
-
# @raise [SettingsIOError] if no file is found
|
121
|
-
# @return [String]
|
122
|
-
def search_config_file
|
123
|
-
names_to_look = %W[#{config_file_name}.yml #{config_file_name}.yaml]
|
124
|
-
dir_order = [Dir.pwd, File.expand_path("~")]
|
125
|
-
|
126
|
-
dir_order.each do |dir|
|
127
|
-
names_to_look.each do |file|
|
128
|
-
path = File.join(dir, file)
|
129
|
-
return path if File.exist?(path)
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
raise SettingsIOError, "no configuration file (#{names_to_look.join(" or ")}) was found\n"\
|
134
|
-
" within paths: #{dir_order.join(", ")}"
|
135
|
-
end
|
44
|
+
extend Forwardable
|
45
|
+
def_delegators :@options, :verbose?, :dry?, :file_mode?
|
46
|
+
def_delegators :@console, :ident_print, :warn_print, :error_print, :headline, :debug, :info, :print_options
|
136
47
|
end
|
137
48
|
end
|
data/pdfh.gemspec
CHANGED
@@ -10,11 +10,11 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["iax7@users.noreply.github.com"]
|
11
11
|
|
12
12
|
spec.summary = "Organize PDF files"
|
13
|
-
spec.description = "Examine all PDF files in Look up directories, remove password (if has one), "\
|
13
|
+
spec.description = "Examine all PDF files in Look up directories, remove password (if has one), " \
|
14
14
|
"rename and copy to a new directory using regular expressions."
|
15
15
|
spec.homepage = "https://github.com/iax7/pdfh"
|
16
16
|
spec.license = "MIT"
|
17
|
-
spec.required_ruby_version = Gem::Requirement.new(">=
|
17
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 3.0.0")
|
18
18
|
|
19
19
|
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
20
20
|
|