html2index 1.2.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 +7 -0
- data/bin/html2index +32 -0
- data/doc/html/html2index.html +638 -0
- data/doc/man/html2index.1.gz +0 -0
- data/doc/pdf/html2index.pdf +0 -0
- data/doc/rst/html2index.rst +265 -0
- data/html2index.gemspec +21 -0
- data/lib/argparser.rb +111 -0
- data/lib/configuration.rb +183 -0
- data/lib/constants.rb +55 -0
- data/lib/definition.rb +43 -0
- data/lib/dictionary.rb +46 -0
- data/lib/file_checking.rb +103 -0
- data/lib/html2index.rb +277 -0
- data/lib/log.conf +56 -0
- data/lib/logging.rb +206 -0
- data/lib/template.rb +134 -0
- data/lib/translating.rb +89 -0
- data/lib/translations +0 -0
- data/lib/user_input.rb +45 -0
- data/lib/version.rb +12 -0
- metadata +104 -0
data/lib/dictionary.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
#encoding: UTF-8
|
2
|
+
=begin
|
3
|
+
/***************************************************************************
|
4
|
+
* ©2015-2017 Michael Uplawski <michael.uplawski@uplawski.eu> *
|
5
|
+
* *
|
6
|
+
* This program is free software; you can redistribute it and/or modify *
|
7
|
+
* it under the terms of the GNU General Public License as published by *
|
8
|
+
* the Free Software Foundation; either version 3 of the License, or *
|
9
|
+
* (at your option) any later version. *
|
10
|
+
* *
|
11
|
+
* This program is distributed in the hope that it will be useful, *
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
14
|
+
* GNU General Public License for more details. *
|
15
|
+
* *
|
16
|
+
* You should have received a copy of the GNU General Public License *
|
17
|
+
* along with this program; if not, write to the *
|
18
|
+
* Free Software Foundation, Inc., *
|
19
|
+
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
20
|
+
***************************************************************************/
|
21
|
+
=end
|
22
|
+
require 'constants'
|
23
|
+
require_relative 'translating'
|
24
|
+
|
25
|
+
class Dictionary
|
26
|
+
|
27
|
+
attr_reader :url, :name, :xpath
|
28
|
+
attr_accessor :color
|
29
|
+
def initialize(name, url, xpath, color = nil)
|
30
|
+
@name = name
|
31
|
+
@url = url
|
32
|
+
@xpath = xpath
|
33
|
+
@color = color
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
##### TEST
|
38
|
+
if __FILE__ == $0
|
39
|
+
d1 = Dictionary.new('my_dict1','www.1.html', '../nix[@nix=nil]')
|
40
|
+
d2 = Dictionary.new('my_dict2','www.2.html', '../nil[@nix=nix]')
|
41
|
+
d1.color='#000090'
|
42
|
+
d2.color='#200010'
|
43
|
+
|
44
|
+
puts 'dict 1 is ' << d1.inspect << ", color: " << d1.color
|
45
|
+
puts 'dict 2 is ' << d2.inspect << ", color: " << d2.color
|
46
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
#encoding: UTF-8
|
2
|
+
=begin
|
3
|
+
/***************************************************************************
|
4
|
+
* ©2011-2017 Michael Uplawski <michael.uplawski@uplawski.eu> *
|
5
|
+
* *
|
6
|
+
* This program is free software; you can redistribute it and/or modify *
|
7
|
+
* it under the terms of the GNU General Public License as published by *
|
8
|
+
* the Free Software Foundation; either version 3 of the License, or *
|
9
|
+
* (at your option) any later version. *
|
10
|
+
* *
|
11
|
+
* This program is distributed in the hope that it will be useful, *
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
14
|
+
* GNU General Public License for more details. *
|
15
|
+
* *
|
16
|
+
* You should have received a copy of the GNU General Public License *
|
17
|
+
* along with this program; if not, write to the *
|
18
|
+
* Free Software Foundation, Inc., *
|
19
|
+
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
20
|
+
***************************************************************************/
|
21
|
+
=end
|
22
|
+
require 'filemagic'
|
23
|
+
|
24
|
+
=begin
|
25
|
+
A module to facilitate frequently occuring checks on
|
26
|
+
file-system objects
|
27
|
+
=end
|
28
|
+
module File_Checking
|
29
|
+
|
30
|
+
@@text_messages = {
|
31
|
+
:exist? => "does not exist!",
|
32
|
+
:exist => "does not exist!",
|
33
|
+
:readable? => "is not readable!",
|
34
|
+
:readable => "is not readable!",
|
35
|
+
:executable? => "is not executable!",
|
36
|
+
:executable => "is not executable!",
|
37
|
+
:writable? => "is not writable!",
|
38
|
+
:writable => "is not writable!",
|
39
|
+
:directory? => "is not a directory!",
|
40
|
+
:directory => "is not a directory!",
|
41
|
+
:file? => "is not a file!",
|
42
|
+
:file => "is not a file!",
|
43
|
+
:type => "is not of the right file-type!",
|
44
|
+
:mime => "does not have the right mime-type!",
|
45
|
+
}
|
46
|
+
|
47
|
+
# find the file-type for a given file name
|
48
|
+
def self::file_type(file, mime = false)
|
49
|
+
fm = FileMagic.fm
|
50
|
+
file_magic = fm.file(file)
|
51
|
+
file_mime = nil
|
52
|
+
if mime
|
53
|
+
fm.flags = [:mime_type]
|
54
|
+
file_mime = fm.file(file)
|
55
|
+
end
|
56
|
+
return file_magic, file_mime
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
# Checks if the file with the name from the first
|
61
|
+
# parameter has the properties, listed in the second.
|
62
|
+
# The messages parameter is an array of one or several
|
63
|
+
# of :exist?, :readable?, :writable?, :directory? or
|
64
|
+
# their string-representations, respectively.
|
65
|
+
# Returns nil in case of success, otherwise an
|
66
|
+
# informative message, describing the first negative
|
67
|
+
# test-result.
|
68
|
+
def file_check(file, *messages)
|
69
|
+
File_Checking.file_check(file, *messages)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Checks if the file with the name from the first
|
73
|
+
# parameter has the properties, listed in the second.
|
74
|
+
# The messages parameter is an array of one or all
|
75
|
+
# of :exist?, :readable?, :writable?, :directory? or
|
76
|
+
# their string-representations, respectively.
|
77
|
+
# Returns nil in case of success, otherwise an
|
78
|
+
# informative message, describing the first negative
|
79
|
+
# test-result.
|
80
|
+
def self.file_check(file, *messages)
|
81
|
+
msg = nil
|
82
|
+
if(file && messages.respond_to?(:to_ary) && !messages.empty?)
|
83
|
+
messages.each do |k|
|
84
|
+
if(! k.to_s.end_with?('?'))
|
85
|
+
k = (k.to_s << '?').to_sym
|
86
|
+
end
|
87
|
+
@log.debug ('checking ' << k.to_s) if @log
|
88
|
+
if(msg == nil && File.respond_to?(k) && ! File.send(k, file.to_s))
|
89
|
+
msg = "#{file} #{@@text_messages[k.to_sym]}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
msg
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
=begin
|
98
|
+
# example
|
99
|
+
|
100
|
+
include File_Checking
|
101
|
+
msg = file_check('some_file.txt', [:exist?, :readable?, 'writable'])
|
102
|
+
puts msg if msg
|
103
|
+
=end
|
data/lib/html2index.rb
ADDED
@@ -0,0 +1,277 @@
|
|
1
|
+
#encoding: UTF-8
|
2
|
+
=begin
|
3
|
+
/***************************************************************************
|
4
|
+
* ©2015-201/ Michael Uplawski <michael.uplawski@uplawski.eu> *
|
5
|
+
* *
|
6
|
+
* This program is free software; you can redistribute it and/or modify *
|
7
|
+
* it under the terms of the GNU General Public License as published by *
|
8
|
+
* the Free Software Foundation; either version 3 of the License, or *
|
9
|
+
* (at your option) any later version. *
|
10
|
+
* *
|
11
|
+
* This program is distributed in the hope that it will be useful, *
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
14
|
+
* GNU General Public License for more details. *
|
15
|
+
* *
|
16
|
+
* You should have received a copy of the GNU General Public License *
|
17
|
+
* along with this program; if not, write to the *
|
18
|
+
* Free Software Foundation, Inc., *
|
19
|
+
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
20
|
+
***************************************************************************/
|
21
|
+
=end
|
22
|
+
|
23
|
+
require_relative 'argparser'
|
24
|
+
require_relative 'logging'
|
25
|
+
require_relative 'translating'
|
26
|
+
require_relative 'user_input'
|
27
|
+
require_relative 'constants'
|
28
|
+
require_relative 'version'
|
29
|
+
require_relative 'definition'
|
30
|
+
require_relative 'dictionary'
|
31
|
+
require_relative 'template'
|
32
|
+
require 'open-uri'
|
33
|
+
require 'nokogiri'
|
34
|
+
require 'filemagic'
|
35
|
+
require_relative 'file_checking'
|
36
|
+
require_relative 'configuration'
|
37
|
+
|
38
|
+
class HTML2Index
|
39
|
+
include Logging
|
40
|
+
include Nokogiri
|
41
|
+
|
42
|
+
=begin
|
43
|
+
initialize the HTML2Index-object
|
44
|
+
=end
|
45
|
+
|
46
|
+
def initialize(*argv)
|
47
|
+
init_logger()
|
48
|
+
# ensure the configuration-file exist.
|
49
|
+
options = ArgParser.parse(argv)
|
50
|
+
$configuration = Configuration.new(options)
|
51
|
+
$configuration.user_conf
|
52
|
+
@log.level = $log_level
|
53
|
+
@file = $configuration.source
|
54
|
+
|
55
|
+
@log.debug("read #{@file}, write #{$configuration.target}")
|
56
|
+
msg = File_Checking::file_check(@file, :exist?, :readable?, :file?)
|
57
|
+
if(msg)
|
58
|
+
@log.error("Error: unsuitable file " <<@file << ": " << msg)
|
59
|
+
exit false
|
60
|
+
end
|
61
|
+
|
62
|
+
ftype = File_Checking::file_type(@file)
|
63
|
+
@log.debug('ftype for ' << @file << ' is ' << ftype.to_s)
|
64
|
+
if(ftype && !ftype.empty? && !ftype[0].downcase.match("(html|xml) .*document") )
|
65
|
+
@log.error(@file.dup << ' does not look like a valid file to scan: ' << ftype)
|
66
|
+
exit false
|
67
|
+
end
|
68
|
+
|
69
|
+
@problem_log = File.join(temp_dir, PROBLEM_LOG)
|
70
|
+
@log.debug('calling generate() ')
|
71
|
+
generate()
|
72
|
+
end
|
73
|
+
|
74
|
+
# create an unnumbered list of the consulted dictionaries.
|
75
|
+
def dict_list
|
76
|
+
list = '<ul>'
|
77
|
+
$configuration.dicts.each do |d|
|
78
|
+
list << '<li>' << d.name << ' : ' << '<a href="' << d.url << '">' << d.url << '</a></li>' << "\n"
|
79
|
+
end
|
80
|
+
@log.debug('list is ' << list.to_s)
|
81
|
+
list << "</ul>\n"
|
82
|
+
end
|
83
|
+
|
84
|
+
=begin
|
85
|
+
Parses the html-file and generates the glossary.
|
86
|
+
=end
|
87
|
+
def generate()
|
88
|
+
@log.info('Generating... PSE wait')
|
89
|
+
begin
|
90
|
+
@doc = HTML::Document.parse(File.open(@file ) )
|
91
|
+
write_html()
|
92
|
+
rescue SystemExit => ir
|
93
|
+
@log.info "Bye"
|
94
|
+
exit true
|
95
|
+
rescue Exception => ex
|
96
|
+
@log.error "line #{__LINE__}: " << ex.message
|
97
|
+
exit false
|
98
|
+
end
|
99
|
+
if(File.exist?(@problem_log ))
|
100
|
+
@log.info "Some expressions caused problems and are listed in " << @problem_log
|
101
|
+
end
|
102
|
+
end
|
103
|
+
private
|
104
|
+
=begin
|
105
|
+
Searches the expression in online-dictionaries and
|
106
|
+
creates a Definition-object for each definition, found.
|
107
|
+
Returns an array of all Definition-objects for the
|
108
|
+
expression.
|
109
|
+
=end
|
110
|
+
def dict_definition(expression)
|
111
|
+
definitions = Array.new
|
112
|
+
$configuration.dicts.each do |d|
|
113
|
+
text_array = Array.new
|
114
|
+
page = nil
|
115
|
+
begin
|
116
|
+
url = d.url.dup << URI.encode_www_form_component(expression.gsub(/\s/,'_').gsub("'", ''))
|
117
|
+
page = Nokogiri::HTML(URI.open(url))
|
118
|
+
rescue Exception => ex
|
119
|
+
url_b = d.url.dup << URI.encode_www_form_component(expression.gsub(/\s/,'_'))
|
120
|
+
@log.warn('WARNING, accessing ' << url << ': ' << ex.message)
|
121
|
+
if(url_b != url)
|
122
|
+
@log.warn("\twill try " << url_b)
|
123
|
+
begin
|
124
|
+
page = Nokogiri::HTML(URI.open(url_b, "User-Agent"=>"#{APPNAME} #{VERSION}", "From"=>"Function Test <bat.guano@don.blech.e4ward.com>"))
|
125
|
+
rescue Exception => ex
|
126
|
+
@log.warn("\tWARNING, accessing " << url_b << ': ' << ex.message)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
if(page)
|
131
|
+
nodes = page.xpath(d.xpath)
|
132
|
+
if(nodes)
|
133
|
+
text_array = nodes.collect {|n| n.inner_text.to_s.strip}
|
134
|
+
if(!text_array.empty?)
|
135
|
+
definition = Definition.new(d.name, expression.gsub('_', ' '), text_array)
|
136
|
+
definition.color = d.color
|
137
|
+
definitions.push(definition)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
else
|
141
|
+
File.open(@problem_log, 'a'){|f| f.write(expression + "\n") }
|
142
|
+
end
|
143
|
+
end
|
144
|
+
return definitions
|
145
|
+
end
|
146
|
+
|
147
|
+
=begin
|
148
|
+
Creates and returns an Array of Definitions.
|
149
|
+
=end
|
150
|
+
def index()
|
151
|
+
begin
|
152
|
+
#TODO: Hash
|
153
|
+
list = Array.new
|
154
|
+
def_list = Array.new
|
155
|
+
tag = $configuration.html_tag
|
156
|
+
attr = $configuration.html_attribute
|
157
|
+
value = $configuration.attr_value
|
158
|
+
xpath = ".//#{tag}[contains(@#{attr}, '#{value}')]"
|
159
|
+
# xpath = './/' << value << '[@' << attr << '="' << value << '"]'
|
160
|
+
@log.debug('xpath is ' << xpath)
|
161
|
+
tags = @doc.xpath(xpath)
|
162
|
+
tags.each do |t|
|
163
|
+
expression = t.attribute('title').to_s
|
164
|
+
unless (expression && !expression.strip.empty? )
|
165
|
+
expression = t.text.to_s
|
166
|
+
end
|
167
|
+
if(expression)
|
168
|
+
expression.gsub!(/\s+/, " ")
|
169
|
+
expression.strip!
|
170
|
+
if(!expression.empty? && !def_list.include?(expression) )
|
171
|
+
if(!list.include?expression.downcase)
|
172
|
+
list.push expression.downcase
|
173
|
+
definition = dict_definition(expression)
|
174
|
+
if(definition && !definition.empty?)
|
175
|
+
# @log.debug('definition is ' << definition.to_s)
|
176
|
+
def_list.push(format_index(definition) )
|
177
|
+
def_list.sort!
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
=begin
|
184
|
+
if(@log.level == Logger::DEBUG)
|
185
|
+
@log.debug('def_list is ')
|
186
|
+
def_list.each_with_index{|t, i| @log.debug( "%i %s"%[i, t]) }
|
187
|
+
end
|
188
|
+
=end
|
189
|
+
rescue Interrupt => ex
|
190
|
+
@log.warn "--------------\n\tIndex generation interrupted by user\n---------------"
|
191
|
+
end
|
192
|
+
return def_list
|
193
|
+
end
|
194
|
+
|
195
|
+
=begin
|
196
|
+
Formats the definition text.
|
197
|
+
=end
|
198
|
+
def format_index(ndef)
|
199
|
+
item = "\n<dt>"
|
200
|
+
if(ndef.respond_to?(:to_ary))
|
201
|
+
item << ndef[0].expression.dup
|
202
|
+
item << "</dt>"
|
203
|
+
ndef.each_with_index do |dn, i|
|
204
|
+
item << "\n\t<dd>(<i style='color:#" << dn.color << ";'>" << dn.origin << "</i>): " << dn.definition.join("<br/>") << "</dd>"
|
205
|
+
end
|
206
|
+
else
|
207
|
+
item << ndef.expression.dup << "</dt>"
|
208
|
+
item << "\n\t<dd>(<i><u>" << dn.origin << "</i<): " << dn.definition << "</dd>"
|
209
|
+
end
|
210
|
+
return item
|
211
|
+
end
|
212
|
+
# def temp_dir options = {:remove => true}
|
213
|
+
def temp_dir
|
214
|
+
@temp_dir ||= begin
|
215
|
+
@log.debug('creating temp_dir')
|
216
|
+
require 'tmpdir'
|
217
|
+
require 'fileutils'
|
218
|
+
path = File.join(Dir::tmpdir, "HTML2Index_#{Time.now.to_i}_#{rand(1000)}")
|
219
|
+
@log.debug('temp-dir path is ' << path)
|
220
|
+
Dir.mkdir(path)
|
221
|
+
# at_exit {FileUtils.rm_rf(path) if File.exists?(path)} if options[:remove]
|
222
|
+
File.new path
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def write_html()
|
227
|
+
tempfile = nil
|
228
|
+
out = nil
|
229
|
+
out_file = $configuration.target
|
230
|
+
template = Template.new
|
231
|
+
if out_file
|
232
|
+
if(File.exist?(out_file))
|
233
|
+
if(File.writable?(out_file))
|
234
|
+
puts "\nWARNING! File " << out_file << " exists!"
|
235
|
+
print "Do you want to overwrite it? (Y/n) "
|
236
|
+
res = wait_for_user
|
237
|
+
puts
|
238
|
+
unless(['y', 'Y'].include?(res.chr) )
|
239
|
+
puts "Okay, doing nothing."
|
240
|
+
exit false
|
241
|
+
end
|
242
|
+
else
|
243
|
+
puts "ERROR! File " << out_file << " is not writable! Aborting, bye."
|
244
|
+
exit false
|
245
|
+
end
|
246
|
+
end
|
247
|
+
else
|
248
|
+
@log.debug('out_file is STDOUT')
|
249
|
+
end
|
250
|
+
begin
|
251
|
+
html = template.to_s
|
252
|
+
|
253
|
+
# create the definitions in index()
|
254
|
+
def_list = "<dl>\n" << index().join("\n") << "\n</dl\n>"
|
255
|
+
# associate content with fields
|
256
|
+
data = {:dict_list => dict_list(), :glossary => def_list }
|
257
|
+
placeholders = $configuration.placeholders
|
258
|
+
fdelim = $configuration.fdelim
|
259
|
+
placeholders.each_pair do |k, v|
|
260
|
+
repl = fdelim.dup << v << fdelim.reverse
|
261
|
+
@log.debug('try to replace ' << repl)
|
262
|
+
html.gsub!(repl, data[k])
|
263
|
+
end
|
264
|
+
@log.debug('html is now ' << html)
|
265
|
+
html.gsub!(/\<head\>/, "<head>\n\t#{GeneratorMeta}")
|
266
|
+
File.write(out_file, html) if out_file
|
267
|
+
puts html if !out_file
|
268
|
+
rescue Exception => ex
|
269
|
+
@log.error( "line #{__LINE__} " << ex.message << ' (' << ex.class.name << ')')
|
270
|
+
ensure
|
271
|
+
out.close if out && !out.closed?
|
272
|
+
File.unlink(out) if out
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
|
data/lib/log.conf
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
#encoding: UTF-8
|
2
|
+
=begin
|
3
|
+
/***************************************************************************
|
4
|
+
* ©2013-2015 Michael Uplawski <michael.uplawski@uplawski.eu> *
|
5
|
+
* *
|
6
|
+
* This program is free software; you can redistribute it and/or modify *
|
7
|
+
* it under the terms of the GNU General Public License as published by *
|
8
|
+
* the Free Software Foundation; either version 3 of the License, or *
|
9
|
+
* (at your option) any later version. *
|
10
|
+
* *
|
11
|
+
* This program is distributed in the hope that it will be useful, *
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
14
|
+
* GNU General Public License for more details. *
|
15
|
+
* *
|
16
|
+
* You should have received a copy of the GNU General Public License *
|
17
|
+
* along with this program; if not, write to the *
|
18
|
+
* Free Software Foundation, Inc., *
|
19
|
+
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
20
|
+
***************************************************************************/
|
21
|
+
|
22
|
+
A simplified logger configuration. Set the level for each individual logger
|
23
|
+
below. Choose a different log-device or log-file if you like. Keep the
|
24
|
+
formatting intact. Do not change other sections of this file.
|
25
|
+
=end
|
26
|
+
|
27
|
+
# Do not touch from here ----->
|
28
|
+
require 'logger'
|
29
|
+
|
30
|
+
debug = Logger::DEBUG
|
31
|
+
info = Logger::INFO
|
32
|
+
error = Logger::ERROR
|
33
|
+
fatal = Logger::FATAL
|
34
|
+
warn = Logger::WARN
|
35
|
+
unknown = Logger::UNKNOWN
|
36
|
+
{
|
37
|
+
# <---------------- to here !
|
38
|
+
|
39
|
+
# Enter your settings here, but take into consideration that not all
|
40
|
+
# the named classes will really produce readable output. Well, you can
|
41
|
+
# always try... Either name just the log-level or make the log-level
|
42
|
+
# precede the output-device or output-file like in the examples.
|
43
|
+
|
44
|
+
# Example: naming a log-file
|
45
|
+
# :HtmlBuilder => [info, 'C:\temp\htmlbuilder.log']
|
46
|
+
# :HtmlBuilder => [debug, '/tmp/htmlbuilder.log'],
|
47
|
+
|
48
|
+
:HTML2Index => debug,
|
49
|
+
:Template => info,
|
50
|
+
:Configuration => info,
|
51
|
+
:ArgParser => info,
|
52
|
+
|
53
|
+
# And ignore the remainder, too.
|
54
|
+
}
|
55
|
+
|
56
|
+
#eof
|
data/lib/logging.rb
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
#encoding: UTF-8
|
2
|
+
=begin
|
3
|
+
/***************************************************************************
|
4
|
+
* ©2011-2017 Michael Uplawski <michael.uplawski@uplawski.eu> *
|
5
|
+
* *
|
6
|
+
* This program is free software; you can redistribute it and/or modify *
|
7
|
+
* it under the terms of the GNU General Public License as published by *
|
8
|
+
* the Free Software Foundation; either version 3 of the License, or *
|
9
|
+
* (at your option) any later version. *
|
10
|
+
* *
|
11
|
+
* This program is distributed in the hope that it will be useful, *
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
14
|
+
* GNU General Public License for more details. *
|
15
|
+
* *
|
16
|
+
* You should have received a copy of the GNU General Public License *
|
17
|
+
* along with this program; if not, write to the *
|
18
|
+
* Free Software Foundation, Inc., *
|
19
|
+
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
20
|
+
***************************************************************************/
|
21
|
+
=end
|
22
|
+
require 'logger'
|
23
|
+
require_relative 'file_checking'
|
24
|
+
|
25
|
+
=begin Creates a member @log and precede its output with the name of the class
|
26
|
+
of the object.
|
27
|
+
Example for a class-level logger:
|
28
|
+
# --------------------
|
29
|
+
class TClass
|
30
|
+
self.extend(Logging)
|
31
|
+
@@log = init_logger(STDOUT)
|
32
|
+
def test_log
|
33
|
+
@@log.info('class-level logger called from instance: ' << @@log.to_s)
|
34
|
+
@log = @@log
|
35
|
+
@log.info('AGAIN: class-level logger called from instance: ' << @log.to_s)
|
36
|
+
end
|
37
|
+
def self::test_log
|
38
|
+
@log.info('class-level logger called from class: ' << @log.to_s)
|
39
|
+
@@log.info('AGAIN: class-level logger called from class: ' << @@log.to_s)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
#---------------------
|
43
|
+
Example for a object-level logger:
|
44
|
+
ATTN! This means 1 logger per object.
|
45
|
+
# --------------------
|
46
|
+
class TClass
|
47
|
+
include Logging
|
48
|
+
def initialize
|
49
|
+
init_logger(STDOUT, Logger::DEBUG)
|
50
|
+
end
|
51
|
+
def test_log
|
52
|
+
@log.debug('called test_log() ')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
=end
|
56
|
+
module Logging
|
57
|
+
@@LEVELS = {:debug => Logger::DEBUG, :info => Logger::INFO, :error => Logger::ERROR, :warn => Logger::WARN, :unknown => Logger::UNKNOWN, :fatal => Logger::FATAL}
|
58
|
+
|
59
|
+
@@have_log = false
|
60
|
+
@@LOG_CONF = File.dirname(File.absolute_path(__FILE__)) << File::Separator << 'log.conf'
|
61
|
+
|
62
|
+
# Call this method in an instance-method (e.g. initialize() ) to define the
|
63
|
+
# object-level logger; i.e. an object-specific member @log.
|
64
|
+
# Call this method within the class-definition for a class-level logger; i.e.
|
65
|
+
# a member @log for class-level acces.
|
66
|
+
# The method returns the logger, so you can actually do what you want with it.
|
67
|
+
def init_logger(target = STDOUT, level = Logger::INFO)
|
68
|
+
# Prepare for a class-level logger. This is actually quite cool.
|
69
|
+
# ---> Ingeniuous code starts here
|
70
|
+
cn = (self.class == Class ? name : self.class.name)
|
71
|
+
# <--- Ingeniuous code ends here
|
72
|
+
|
73
|
+
# allow to override the set log-levels with an
|
74
|
+
# external configuration (log.conf).
|
75
|
+
log_conf(cn)
|
76
|
+
# Or use the defaults as set here or elsewhere...
|
77
|
+
|
78
|
+
@level ||= level
|
79
|
+
@target ||= target
|
80
|
+
@log = Logger.new(@target)
|
81
|
+
@log.level = @level
|
82
|
+
@log.formatter = formatter(cn)
|
83
|
+
if ! @@have_log
|
84
|
+
@log.debug cn.dup << ' reading logging-configuration from ' << @@LOG_CONF
|
85
|
+
@@have_log = true
|
86
|
+
@log.debug('level is ' << level.to_s)
|
87
|
+
end
|
88
|
+
return @log
|
89
|
+
end
|
90
|
+
|
91
|
+
def formatter(classname)
|
92
|
+
proc do |severity, datetime, progname, msg|
|
93
|
+
t = Time.now
|
94
|
+
"#{classname} (#{__LINE__}): #{severity} #{t.hour}-#{t.min}-#{t.sec}: #{msg}\n"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Set the log-target to an IO object.
|
99
|
+
def log_target=(target)
|
100
|
+
@target = target
|
101
|
+
@log = Logger.new(@target)
|
102
|
+
@log.level = @level
|
103
|
+
end
|
104
|
+
|
105
|
+
# set the log-level
|
106
|
+
|
107
|
+
def log_level=(level)
|
108
|
+
@level = level
|
109
|
+
@log.level = @level
|
110
|
+
end
|
111
|
+
|
112
|
+
# verify log level
|
113
|
+
def log_level?(level)
|
114
|
+
return @log.level == level
|
115
|
+
end
|
116
|
+
|
117
|
+
# brutal log-function: print message, than exit.
|
118
|
+
def debout(str)
|
119
|
+
puts self.class.name.dup << " DEBOUT: " << str
|
120
|
+
exit true
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
# Override or set the log-level and target-device, as set in a file 'log.conf'.
|
126
|
+
# I do not like the look of this, but it works just the way I want it to.
|
127
|
+
# "HEAVANS! Isn't there a standard way to do this in Ruby, for Christ's sake?", you say.
|
128
|
+
# Heck, I don't care. <= Read that again, I say.
|
129
|
+
def log_conf(cn = nil)
|
130
|
+
config = level = target = nil
|
131
|
+
if(File::exist?(@@LOG_CONF) )
|
132
|
+
begin
|
133
|
+
conf = File.read(@@LOG_CONF)
|
134
|
+
config = instance_eval(conf)
|
135
|
+
rescue Exception => ex
|
136
|
+
STDERR.puts "WARNING! Cannot evaluate the logger-configuration!" << ' ' << ex.message
|
137
|
+
STDERR.puts "Default log-levels apply."
|
138
|
+
end
|
139
|
+
else
|
140
|
+
puts "Default log-levels apply."
|
141
|
+
end
|
142
|
+
|
143
|
+
if(config && config.respond_to?(:to_hash) )
|
144
|
+
config.default = nil
|
145
|
+
if cn
|
146
|
+
config = config[cn.to_sym]
|
147
|
+
else
|
148
|
+
config = config[self.class.name.to_sym]
|
149
|
+
end
|
150
|
+
|
151
|
+
if(config )
|
152
|
+
if(config.respond_to?(:to_ary) && config.size == 2)
|
153
|
+
@level, @target = config
|
154
|
+
@target.downcase!
|
155
|
+
logdir = File.dirname(@target)
|
156
|
+
msg = File_Checking::file_check(logdir, :exist?, :directory?, :writable?)
|
157
|
+
if(msg)
|
158
|
+
STDERR.puts "WARNING! A logfile for '%s' cannot be written to %s (%s)!" %[self.class.name, logdir, msg]
|
159
|
+
@target = nil
|
160
|
+
end
|
161
|
+
else
|
162
|
+
@level = config
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
attr_reader :level
|
168
|
+
end
|
169
|
+
|
170
|
+
######### test
|
171
|
+
if __FILE__ == $0
|
172
|
+
class TClass
|
173
|
+
# class level ---->
|
174
|
+
self.extend(Logging)
|
175
|
+
@@log = init_logger(STDOUT, Logger::INFO)
|
176
|
+
# <------
|
177
|
+
# object-level ---->
|
178
|
+
include Logging
|
179
|
+
# <---------
|
180
|
+
|
181
|
+
def test_log
|
182
|
+
@@log.info('class-level logger called from instance: ' << @@log.to_s)
|
183
|
+
#@log = @@log # works too
|
184
|
+
@log = TClass.class_eval{@log}
|
185
|
+
@log.info('AGAIN: class-level logger called from instance: ' << @log.to_s)
|
186
|
+
@log.debug("you won't see this on log-level INFO")
|
187
|
+
|
188
|
+
# object-level ---->
|
189
|
+
init_logger
|
190
|
+
# <-----------
|
191
|
+
@log.info("That's a different thing: " << @log.to_s << " - object-level logger!")
|
192
|
+
|
193
|
+
end
|
194
|
+
def self::test_log
|
195
|
+
@log.info('class-level logger called from class: ' << @log.to_s)
|
196
|
+
@@log.info('AGAIN: class-level logger called from class: ' << @log.to_s)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
TClass.new.test_log # class-logger + 1st object-logger
|
201
|
+
TClass.new.test_log # same class-logger + 2nd object-logger
|
202
|
+
|
203
|
+
TClass::test_log # same class-logger
|
204
|
+
puts 'And just say it once clearly: THIS IS COOOL!!'
|
205
|
+
end
|
206
|
+
#EOF
|