at_email 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +28 -0
- data/bin/at_email +24 -0
- data/lib/at_email.rb +210 -0
- data/lib/at_email/account.rb +3 -0
- data/lib/at_email/account/connection.rb +32 -0
- data/lib/at_email/config.rb +4 -0
- data/lib/at_email/config/cmd_opt_parser.rb +58 -0
- data/lib/at_email/config/config_file.rb +100 -0
- data/lib/at_email/core.rb +3 -0
- data/lib/at_email/core/default.rb +62 -0
- data/lib/at_email/tasks.rb +3 -0
- data/lib/at_email/tasks/imap_to_fs.rb +756 -0
- data/lib/at_email/threads.rb +3 -0
- data/lib/at_email/threads/thread_queue.rb +83 -0
- data/lib/at_email/version.rb +10 -0
- metadata +59 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 074b5f8e272c1f28f2fa4089e5396b13b061c1dd
|
4
|
+
data.tar.gz: f5eaf714aa99285d339078fe5ee59965ca12614c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e689e838a44d38ec50bee5c5afc6624bcefb5eb064fdc2f01231799d229bf84d8b15287d05e1cd93f84a18811da2e354672e3c8a4f5cd5d3bbc15b9e2afacd95
|
7
|
+
data.tar.gz: 83e37a70a22770e6abb9ee613aeb40af85668f196223a81f1a767b7da9af09afba065cd2cc12f13f8de24ac005a9e4cd35cd2f1957d51f607d6896f24d9e2b6b
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2018 Robin Patel
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# at_email
|
2
|
+
|
3
|
+
A project to build an e-mail management and support toolkit.
|
4
|
+
|
5
|
+
The project is currently at a very early stage and the functionality is likely to be unstable and possible destructive for the time being, I would not recommend using this software at this time. We will update this information when the software is in a more stable stage of development.
|
6
|
+
|
7
|
+
## Prerequisites
|
8
|
+
|
9
|
+
* ruby 2.3.7+
|
10
|
+
* GEM: concurrent-ruby (1.1.3)
|
11
|
+
|
12
|
+
## License
|
13
|
+
|
14
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
|
15
|
+
|
16
|
+
## Acknowledgments
|
17
|
+
|
18
|
+
* https://github.com/joeyates
|
19
|
+
|
20
|
+
## Disclaimer
|
21
|
+
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
28
|
+
SOFTWARE.
|
data/bin/at_email
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.expand_path("../lib/", __dir__))
|
4
|
+
require 'at_email'
|
5
|
+
$runtime_configure = At_email::RuntimeConfigure.new
|
6
|
+
$runtime_configure.get_app_path
|
7
|
+
$options = At_email::Config::Cmd_Opt_Parser.new(ARGV)
|
8
|
+
config_file = At_email::Config::Config_File.new
|
9
|
+
$config = config_file.get_config()
|
10
|
+
$runtime_configure.configure_logger
|
11
|
+
|
12
|
+
$logger.debug ''
|
13
|
+
$logger.warn 'Welcome to atEmail - Build: ' + At_email::VERSION + ' - PID: ' + Process.pid.to_s
|
14
|
+
$logger.debug ''
|
15
|
+
$logger.warn 'Config File: ' + $options.config_file + ' - Task Starting'
|
16
|
+
$logger.warn 'Log File: ' + $logger.log_file_default
|
17
|
+
|
18
|
+
$runtime_configure.output_config
|
19
|
+
|
20
|
+
task = At_email::Tasks::Imap_To_Fs.new()
|
21
|
+
|
22
|
+
task.execute
|
23
|
+
|
24
|
+
$logger.warn 'Config File: ' + $options.config_file + ' - Task Complete'
|
data/lib/at_email.rb
ADDED
@@ -0,0 +1,210 @@
|
|
1
|
+
module At_email ; end
|
2
|
+
|
3
|
+
### libs
|
4
|
+
require 'fileutils'
|
5
|
+
require "logger"
|
6
|
+
require 'digest'
|
7
|
+
|
8
|
+
### at_email libs
|
9
|
+
require "at_email/core"
|
10
|
+
require "at_email/version"
|
11
|
+
require "at_email/config"
|
12
|
+
require "at_email/account"
|
13
|
+
require "at_email/tasks"
|
14
|
+
require "at_email/threads"
|
15
|
+
|
16
|
+
module At_email
|
17
|
+
|
18
|
+
DEFAULT_CONFIG_FILE = 'cfg/defaults/default_config.json'
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
module At_email
|
23
|
+
|
24
|
+
class At_email::At_Logger
|
25
|
+
|
26
|
+
attr_reader :log_file_default
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
|
30
|
+
filename_timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
|
31
|
+
log_file_dir = $config[:output_base_dir] + '/_logs'
|
32
|
+
|
33
|
+
@log_file_default = log_file_dir + '/' + $config[:task] + '.' + $config[:account_id] + '.' + filename_timestamp + '.log'
|
34
|
+
if !Dir.exists?(log_file_dir)
|
35
|
+
FileUtils.mkdir_p log_file_dir
|
36
|
+
end
|
37
|
+
@logger_stdout = Logger.new(STDOUT)
|
38
|
+
@logger_stderr = Logger.new(STDERR)
|
39
|
+
@logger_file_default = Logger.new(@log_file_default)
|
40
|
+
@logger_stdout.datetime_format = '%Y-%m-%d %H:%M:%S'
|
41
|
+
@logger_stdout.formatter = proc do |severity, datetime, progname, msg|
|
42
|
+
"#{datetime} - " + $config[:task] + " - " + $config[:account_id] + " - #{msg}\n"
|
43
|
+
end
|
44
|
+
@logger_stderr.datetime_format = '%Y-%m-%d %H:%M:%S'
|
45
|
+
@logger_stderr.formatter = proc do |severity, datetime, progname, msg|
|
46
|
+
"#{datetime} - " + $config[:task] + " - " + $config[:account_id] + " - #{msg}\n"
|
47
|
+
end
|
48
|
+
@logger_file_default.datetime_format = '%Y-%m-%d %H:%M:%S'
|
49
|
+
@logger_file_default.formatter = proc do |severity, datetime, progname, msg|
|
50
|
+
"#{datetime} - " + $config[:task] + " - " + $config[:account_id] + " - #{msg}\n"
|
51
|
+
end
|
52
|
+
if $options.silent
|
53
|
+
@logger_stdout.level = Logger::FATAL
|
54
|
+
@logger_stderr.level = Logger::FATAL
|
55
|
+
@logger_file_default.level = Logger::FATAL
|
56
|
+
elsif $options.quiet
|
57
|
+
@logger_stdout.level = Logger::WARN
|
58
|
+
@logger_stderr.level = Logger::WARN
|
59
|
+
@logger_file_default.level = Logger::WARN
|
60
|
+
elsif $options.verbose
|
61
|
+
@logger_stdout.level = Logger::DEBUG
|
62
|
+
@logger_stderr.level = Logger::DEBUG
|
63
|
+
@logger_file_default.level = Logger::DEBUG
|
64
|
+
else
|
65
|
+
@logger_stdout.level = Logger::INFO
|
66
|
+
@logger_stderr.level = Logger::INFO
|
67
|
+
@logger_file_default.level = Logger::INFO
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def debug(event)
|
72
|
+
@logger_stdout.debug(event)
|
73
|
+
@logger_file_default.debug(event)
|
74
|
+
end
|
75
|
+
|
76
|
+
def info(event)
|
77
|
+
@logger_stdout.info(event)
|
78
|
+
@logger_file_default.info(event)
|
79
|
+
end
|
80
|
+
|
81
|
+
def warn(event)
|
82
|
+
@logger_stderr.warn(event)
|
83
|
+
@logger_file_default.warn(event)
|
84
|
+
end
|
85
|
+
|
86
|
+
def error(event)
|
87
|
+
@logger_stderr.error(event)
|
88
|
+
@logger_file_default.error(event)
|
89
|
+
end
|
90
|
+
|
91
|
+
def fatal(event)
|
92
|
+
@logger_stderr.fatal(event)
|
93
|
+
@logger_file_default.fatal(event)
|
94
|
+
end
|
95
|
+
|
96
|
+
def unknown(event)
|
97
|
+
@logger_stderr.unknown(event)
|
98
|
+
@logger_file_default.unknown(event)
|
99
|
+
end
|
100
|
+
|
101
|
+
def event(event_level, event_tag, event_data)
|
102
|
+
event_tag_formatted = get_event_tag_formatted(event_tag)
|
103
|
+
if event_tag_formatted === ''
|
104
|
+
log_string = event_data
|
105
|
+
else
|
106
|
+
log_string = event_tag_formatted + ' - ' + event_data
|
107
|
+
end
|
108
|
+
case event_level
|
109
|
+
when 'DEBUG'
|
110
|
+
$logger.debug log_string
|
111
|
+
when 'WARN'
|
112
|
+
$logger.error log_string
|
113
|
+
when 'ERROR'
|
114
|
+
$logger.fatal ''
|
115
|
+
$logger.error log_string
|
116
|
+
$logger.error ''
|
117
|
+
$logger.error 'Error at line number: ' + __LINE__.to_s + ' of file: ' + __FILE__
|
118
|
+
caller.each do |stack_line|
|
119
|
+
$logger.error ' ' + stack_line
|
120
|
+
end
|
121
|
+
$logger.error ''
|
122
|
+
when 'FATAL'
|
123
|
+
error_code = 1
|
124
|
+
$logger.fatal ''
|
125
|
+
$logger.fatal log_string
|
126
|
+
$logger.fatal ''
|
127
|
+
$logger.fatal 'Fatal Error at line number: ' + __LINE__.to_s + ' of file: ' + __FILE__
|
128
|
+
caller.each do |stack_line|
|
129
|
+
$logger.fatal ' ' + stack_line
|
130
|
+
end
|
131
|
+
$logger.fatal ''
|
132
|
+
$logger.fatal 'Exiting with error code: ' + error_code.to_s
|
133
|
+
$logger.fatal ''
|
134
|
+
puts "\n"
|
135
|
+
exit(error_code)
|
136
|
+
else
|
137
|
+
$logger.info log_string
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def get_event_tag_formatted(event_tag)
|
142
|
+
event_tag_formatted = ''
|
143
|
+
if event_tag.length == 0
|
144
|
+
event_tag_formatted = ''
|
145
|
+
else
|
146
|
+
if event_tag.length <= 2
|
147
|
+
event_tag_formatted = '*' + event_tag + '*'
|
148
|
+
else
|
149
|
+
event_tag_formatted = '***** ' + event_tag + ' *****'
|
150
|
+
end
|
151
|
+
end
|
152
|
+
return event_tag_formatted
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
class At_email::RuntimeConfigure
|
158
|
+
|
159
|
+
def configure_logger
|
160
|
+
$logger = At_email::At_Logger.new()
|
161
|
+
end
|
162
|
+
|
163
|
+
def output_config
|
164
|
+
$logger.debug ''
|
165
|
+
$logger.debug 'Configuration'
|
166
|
+
$config.each do |key, value|
|
167
|
+
if key.to_s === 'password'
|
168
|
+
$logger.debug ' ' + key.to_s + ' = ' + '*' * value.to_s.length
|
169
|
+
else
|
170
|
+
$logger.debug ' ' + key.to_s + ' = ' + value.to_s
|
171
|
+
end
|
172
|
+
end
|
173
|
+
$logger.debug ''
|
174
|
+
end
|
175
|
+
|
176
|
+
def get_app_path
|
177
|
+
$app_path = File.dirname(File.expand_path(File.dirname($0)))
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
|
182
|
+
class At_email::Formatting
|
183
|
+
|
184
|
+
def initialize
|
185
|
+
end
|
186
|
+
|
187
|
+
def get_hash(data, hash_length)
|
188
|
+
if !hash_length.is_a? Numeric
|
189
|
+
$logger.event('FATAL', 'FATAL ERROR', 'hash_length variable is not numeric')
|
190
|
+
return false
|
191
|
+
end
|
192
|
+
if (hash_length > 32)
|
193
|
+
$logger.event('FATAL', 'FATAL ERROR', 'hash_length maximum limit is 32 - Requested length: ' + hash_length.to_s)
|
194
|
+
return false
|
195
|
+
end
|
196
|
+
return Digest::MD5.hexdigest(data)[0...hash_length]
|
197
|
+
end
|
198
|
+
|
199
|
+
def get_random_id(id_length)
|
200
|
+
hash = get_hash(Time.now.to_f.to_s + '-' + rand(1..1000000000000).to_s, id_length)
|
201
|
+
if hash
|
202
|
+
return hash
|
203
|
+
else
|
204
|
+
return false
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
require 'net/imap'
|
3
|
+
|
4
|
+
class At_email::Account::Imap < Net::IMAP
|
5
|
+
attr_accessor :debug
|
6
|
+
end
|
7
|
+
|
8
|
+
class At_email::Account::ImapConnection
|
9
|
+
|
10
|
+
attr_reader :imap
|
11
|
+
|
12
|
+
METADATA_ATTRIBUTES = [ 'ENVELOPE', 'FLAGS' ].freeze
|
13
|
+
REQUESTED_ATTRIBUTES = [ 'INTERNALDATE', 'RFC822', 'RFC822.SIZE' ].freeze
|
14
|
+
|
15
|
+
def initialize()
|
16
|
+
$logger.info 'Connecting to IMAP server - Host: ' + $config[:server] + ' - Port: ' + $config[:port].to_s
|
17
|
+
connect_config = {port: $config[:port], ssl: $config[:ssl]}
|
18
|
+
@imap = At_email::Account::Imap.new($config[:server], connect_config)
|
19
|
+
end
|
20
|
+
|
21
|
+
def login
|
22
|
+
$logger.info 'Logging into IMAP server - Username: ' + $config[:username]
|
23
|
+
@imap.login($config[:username], $config[:password])
|
24
|
+
end
|
25
|
+
|
26
|
+
def disconnect
|
27
|
+
$logger.debug 'Logging out from IMAP server'
|
28
|
+
@imap.logout
|
29
|
+
@imap.disconnect
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
|
2
|
+
require 'optparse'
|
3
|
+
require 'optparse/time'
|
4
|
+
require 'ostruct'
|
5
|
+
require 'pp'
|
6
|
+
|
7
|
+
class At_email::Config::Cmd_Opt_Parser
|
8
|
+
|
9
|
+
attr_reader :config_file
|
10
|
+
attr_reader :verbose
|
11
|
+
attr_reader :quiet
|
12
|
+
attr_reader :silent
|
13
|
+
|
14
|
+
CODES = %w[iso-2022-jp shift_jis euc-jp utf8 binary]
|
15
|
+
CODE_ALIASES = { "jis" => "iso-2022-jp", "sjis" => "shift_jis" }
|
16
|
+
|
17
|
+
def initialize(args)
|
18
|
+
@silent = false
|
19
|
+
@quiet = false
|
20
|
+
@verbose = false
|
21
|
+
opt_parser = OptionParser.new do |opts|
|
22
|
+
opts.banner = "Usage: #{$PROGRAM_NAME} [options]"
|
23
|
+
opts.separator ""
|
24
|
+
opts.separator "Options:"
|
25
|
+
opts.on("-c [CONFIG FILE]", "--config-file [CONFIG FILE]", String, "Config File (JSON)") do |config_file|
|
26
|
+
@config_file = config_file
|
27
|
+
end
|
28
|
+
opts.on("-s", "--silent", "Run silently") do
|
29
|
+
@silent = true
|
30
|
+
end
|
31
|
+
opts.on("-q", "--quiet", "Run quietly") do
|
32
|
+
@quiet = true
|
33
|
+
if @silent
|
34
|
+
@silent = false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
opts.on("-v", "--verbose", "Run verbosely") do
|
38
|
+
@verbose = true
|
39
|
+
if @quiet
|
40
|
+
@quiet = false
|
41
|
+
end
|
42
|
+
if @silent
|
43
|
+
@silent = false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
47
|
+
puts opts
|
48
|
+
exit
|
49
|
+
end
|
50
|
+
opts.on_tail("--version", "Show version") do
|
51
|
+
puts At_email::VERSION
|
52
|
+
exit
|
53
|
+
end
|
54
|
+
end
|
55
|
+
opt_parser.parse!(args)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
class At_email::Config::Config_File
|
5
|
+
|
6
|
+
def get_config()
|
7
|
+
|
8
|
+
if File.exist?($options.config_file)
|
9
|
+
contents = File.read($options.config_file)
|
10
|
+
config = JSON.parse(contents, symbolize_names: true)
|
11
|
+
else
|
12
|
+
raise "Config file does not exist: " + $options.config_file
|
13
|
+
end
|
14
|
+
|
15
|
+
default_config_file_path = $app_path + '/' + At_email::DEFAULT_CONFIG_FILE
|
16
|
+
if File.exist?(default_config_file_path)
|
17
|
+
default_config_contents = File.read(default_config_file_path)
|
18
|
+
default_config = JSON.parse(default_config_contents, symbolize_names: true)
|
19
|
+
else
|
20
|
+
raise "Default config file does not exist: " + default_config_file_path
|
21
|
+
end
|
22
|
+
|
23
|
+
config.each do |key, value|
|
24
|
+
if !default_config[key]
|
25
|
+
raise 'Config Error: Property: ' + key.to_s + ' is not a valid configuration option for this system'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
default_config.each do |key, data|
|
30
|
+
if !config[key] && data[:value]
|
31
|
+
config[key] = data[:value]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
default_config.each do |key, data|
|
36
|
+
if config[key]
|
37
|
+
if data[:type]
|
38
|
+
if data[:type] === 'boolean'
|
39
|
+
if ![true, false].include? config[key]
|
40
|
+
raise 'Config Error: Property: ' + key.to_s + ' is not type: boolean - config value: ' + config[key].to_s
|
41
|
+
end
|
42
|
+
end
|
43
|
+
if data[:type] === 'numeric'
|
44
|
+
if !config[key].is_a? Numeric
|
45
|
+
raise 'Config Error: Property: ' + key.to_s + ' is not type: numeric - config value: ' + config[key].to_s
|
46
|
+
end
|
47
|
+
if data[:min_value]
|
48
|
+
if config[key] < data[:min_value]
|
49
|
+
raise 'Config Error: Property: ' + key.to_s + ' is below minimum value: ' + data[:min_value].to_s + ' - config value: ' + config[key].to_s
|
50
|
+
end
|
51
|
+
end
|
52
|
+
if data[:max_value]
|
53
|
+
if config[key] > data[:max_value]
|
54
|
+
raise 'Config Error: Property: ' + key.to_s + ' is above maximum value: ' + data[:max_value].to_s + ' - config value: ' + config[key].to_s
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
if data[:type] === 'string'
|
59
|
+
if !config[key].is_a? String
|
60
|
+
raise 'Config Error: Property: ' + key.to_s + ' is not type: string - value: ' + config[key].to_s
|
61
|
+
end
|
62
|
+
if data[:min_length]
|
63
|
+
if config[key].length < data[:min_length]
|
64
|
+
raise 'Config Error: Property: ' + key.to_s + ' is below minimum lenth: ' + data[:min_length].to_s + ' - config value: ' + config[key].to_s
|
65
|
+
end
|
66
|
+
end
|
67
|
+
if data[:max_length]
|
68
|
+
if config[key].length > data[:max_length]
|
69
|
+
raise 'Config Error: Property: ' + key.to_s + ' is above minimum lenth: ' + data[:max_length].to_s + ' - config value: ' + config[key].to_s
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
if data[:type] === 'path'
|
74
|
+
if data[:path_type]
|
75
|
+
if data[:path_type] === 'dir'
|
76
|
+
if !Dir.exists?(config[key].to_s)
|
77
|
+
raise 'Config Error: Property: ' + key.to_s + ' is not a directory path - config value: ' + config[key].to_s
|
78
|
+
end
|
79
|
+
end
|
80
|
+
if data[:path_type] === 'file'
|
81
|
+
if !File.exists?(config[key].to_s)
|
82
|
+
raise 'Config Error: Property: ' + key.to_s + ' is not a file path - config value: ' + config[key].to_s
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
output = {}
|
92
|
+
config.sort_by { |k, v| k }.each do |key, value|
|
93
|
+
output[key] = value
|
94
|
+
end
|
95
|
+
|
96
|
+
return output
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|