html2index 1.2.1

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