console 0.5 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,119 @@
1
+ # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative 'buffer'
22
+
23
+ module Console
24
+ UNKNOWN = 'unknown'
25
+
26
+ class Filter
27
+ def self.[] **levels
28
+ klass = Class.new(self)
29
+
30
+ klass.instance_exec do
31
+ const_set(:LEVELS, levels)
32
+ const_set(:MAXIMUM_LEVEL, levels.values.max)
33
+
34
+ levels.each do |name, level|
35
+ const_set(name.to_s.upcase, level)
36
+
37
+ define_method(name) do |subject = nil, *arguments, &block|
38
+ enabled = @subjects[subject.class]
39
+
40
+ if enabled == true or (enabled != false and level >= @level)
41
+ self.call(subject, *arguments, severity: name, **@options, &block)
42
+ end
43
+ end
44
+
45
+ define_method("#{name}!") do
46
+ @level = level
47
+ end
48
+
49
+ define_method("#{name}?") do
50
+ @level >= level
51
+ end
52
+ end
53
+ end
54
+
55
+ return klass
56
+ end
57
+
58
+ def initialize(output, verbose: true, level: 0, **options)
59
+ @level = level
60
+ @verbose = verbose
61
+
62
+ @subjects = {}
63
+
64
+ @output = output
65
+ @options = options
66
+ end
67
+
68
+ def dup(**options)
69
+ super().tap do |logger|
70
+ logger.options = @options.merge(options)
71
+ end
72
+ end
73
+
74
+ attr :level
75
+ attr :verbose
76
+
77
+ attr :subjects
78
+
79
+ attr_accessor :output
80
+ attr_accessor :options
81
+
82
+ def level= level
83
+ if level.is_a? Symbol
84
+ @level = self.class::LEVELS[level]
85
+ else
86
+ @level = level
87
+ end
88
+ end
89
+
90
+ def verbose!(value = true)
91
+ @verbose = value
92
+ @output.verbose!(value)
93
+ end
94
+
95
+ def off!
96
+ @level = -1
97
+ end
98
+
99
+ def all!
100
+ @level = self.class::MAXIMUM_LEVEL
101
+ end
102
+
103
+ def enabled?(subject)
104
+ @subjects[subject.class] == true
105
+ end
106
+
107
+ def enable(subject)
108
+ @subjects[subject.class] = true
109
+ end
110
+
111
+ def disable(subject)
112
+ @subjects[subject.class] = false
113
+ end
114
+
115
+ def call(*arguments, &block)
116
+ @output.call(*arguments, &block)
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,26 @@
1
+ # Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ module Console
22
+ class Generic
23
+ def format(buffer, terminal)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,31 @@
1
+ # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative 'filter'
22
+
23
+ module Console
24
+ class Logger < Filter[debug: 0, info: 1, warn: 2, error: 3, fatal: 4]
25
+ def initialize(output, **options)
26
+ super(output, **options)
27
+
28
+ self.info!
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,71 @@
1
+ # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative '../buffer'
22
+ require_relative '../filter'
23
+
24
+ require 'time'
25
+ require 'json'
26
+
27
+ module Console
28
+ module Serialized
29
+ class Logger
30
+ def initialize(io = $stderr, format: JSON)
31
+ @io = io
32
+ @start = Time.now
33
+ @format = format
34
+ end
35
+
36
+ attr :io
37
+ attr :start
38
+ attr :format
39
+
40
+ def verbose!(value = true)
41
+ end
42
+
43
+ def call(subject = nil, *arguments, severity: UNKNOWN, &block)
44
+ message = {
45
+ time: Time.now.iso8601,
46
+ severity: severity,
47
+ }
48
+
49
+ if subject
50
+ message[:subject] = subject
51
+ end
52
+
53
+ if arguments.any?
54
+ message[:arguments] = arguments
55
+ end
56
+
57
+ if block_given?
58
+ if block.arity.zero?
59
+ message[:message] = yield
60
+ else
61
+ buffer = StringIO.new
62
+ yield buffer
63
+ message[:message] = buffer.string
64
+ end
65
+ end
66
+
67
+ @io.puts(@format.dump(message))
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,65 @@
1
+ # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative 'generic'
22
+
23
+ module Console
24
+ class Shell < Generic
25
+ def self.for(*arguments, **options)
26
+ if arguments.first.is_a?(Hash)
27
+ self.new(*arguments, **options)
28
+ else
29
+ self.new(nil, arguments, **options)
30
+ end
31
+ end
32
+
33
+ def initialize(environment, *arguments, **options)
34
+ @environment = environment
35
+ @arguments = arguments
36
+ @options = options
37
+ end
38
+
39
+ attr :environment
40
+ attr :arguments
41
+ attr :options
42
+
43
+ def chdir_string(options)
44
+ if options and chdir = options[:chdir]
45
+ " in #{chdir}"
46
+ end
47
+ end
48
+
49
+ def self.register(terminal)
50
+ terminal[:shell_command] ||= terminal.style(:blue, nil, :bold)
51
+ end
52
+
53
+ def format(output, terminal, verbose)
54
+ arguments = @arguments.flatten.collect(&:to_s)
55
+
56
+ output.puts " #{terminal[:shell_command]}#{arguments.join(' ')}#{terminal.reset}#{chdir_string(options)}"
57
+
58
+ if verbose and @environment
59
+ @environment.each do |key, value|
60
+ output.puts " export #{key}=#{value}"
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,42 @@
1
+
2
+ # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+
22
+ module Console
23
+ class Split
24
+ def self.[] *outputs
25
+ self.new(outputs)
26
+ end
27
+
28
+ def initialize(outputs)
29
+ @outputs = outputs
30
+ end
31
+
32
+ def verbose!(value = true)
33
+ @outputs.each{|output| output.verbose!(value)}
34
+ end
35
+
36
+ def call(level, subject = nil, *arguments, **options, &block)
37
+ @outputs.each do |output|
38
+ output.call(level, subject, *arguments, **options, &block)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,21 @@
1
+ # Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative 'terminal/logger'
@@ -0,0 +1,150 @@
1
+ # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative '../buffer'
22
+
23
+ require_relative '../shell'
24
+ require_relative '../error'
25
+
26
+ require_relative 'text'
27
+ require_relative 'xterm'
28
+
29
+ module Console
30
+ module Terminal
31
+ def self.for(io)
32
+ if io.isatty
33
+ XTerm.new(io)
34
+ else
35
+ Text.new(io)
36
+ end
37
+ end
38
+
39
+ class Logger
40
+ def initialize(io = $stderr, verbose: false, **options)
41
+ @io = io
42
+ @verbose = verbose
43
+ @start = Time.now
44
+
45
+ @terminal = Terminal.for(io)
46
+ @terminal[:logger_prefix] ||= @terminal.style(nil, nil, :bold)
47
+ @terminal[:logger_suffix] ||= @terminal.style(:white, nil, :faint)
48
+ @terminal[:debug] = @terminal.style(:cyan)
49
+ @terminal[:info] = @terminal.style(:green)
50
+ @terminal[:warn] = @terminal.style(:yellow)
51
+ @terminal[:error] = @terminal.style(:red)
52
+ @terminal[:fatal] = @terminal[:error]
53
+
54
+ self.register_defaults(@terminal)
55
+ end
56
+
57
+ attr :io
58
+ attr_accessor :verbose
59
+ attr :start
60
+ attr :terminal
61
+
62
+ def verbose!(value = true)
63
+ @verbose = value
64
+ end
65
+
66
+ def register_defaults(terminal)
67
+ Shell.register(terminal)
68
+ Error.register(terminal)
69
+ end
70
+
71
+ UNKNOWN = 'unknown'
72
+
73
+ def call(subject = nil, *arguments, name: nil, severity: UNKNOWN, &block)
74
+ prefix = build_prefix(name || severity.to_s)
75
+ indent = " " * prefix.size
76
+
77
+ buffer = Buffer.new("#{indent}| ")
78
+
79
+ if subject
80
+ format_subject(severity, prefix, subject, buffer)
81
+ end
82
+
83
+ arguments.each do |argument|
84
+ format_argument(argument, buffer)
85
+ end
86
+
87
+ if block_given?
88
+ if block.arity.zero?
89
+ format_argument(yield, buffer)
90
+ else
91
+ yield(buffer, @terminal)
92
+ end
93
+ end
94
+
95
+ @io.write buffer.string
96
+ end
97
+
98
+ protected
99
+
100
+ def format_argument(argument, output)
101
+ case argument
102
+ when Exception
103
+ Error.new(argument).format(output, @terminal, @verbose)
104
+ when Generic
105
+ argument.format(output, @terminal, @verbose)
106
+ else
107
+ format_value(argument, output)
108
+ end
109
+ end
110
+
111
+ def format_subject(severity, prefix, subject, output)
112
+ prefix_style = @terminal[severity]
113
+
114
+ if @verbose
115
+ suffix = " #{@terminal[:logger_suffix]}[pid=#{Process.pid}]#{@terminal.reset}"
116
+ end
117
+
118
+ output.puts "#{@terminal[:logger_prefix]}#{subject}#{@terminal.reset}#{suffix}", prefix: "#{prefix_style}#{prefix}:#{@terminal.reset} "
119
+ end
120
+
121
+ def format_value(value, output)
122
+ string = value.to_s
123
+
124
+ string.each_line do |line|
125
+ output.puts "#{line}"
126
+ end
127
+ end
128
+
129
+ def time_offset_prefix
130
+ offset = Time.now - @start
131
+ minutes = (offset/60).floor
132
+ seconds = (offset - (minutes*60))
133
+
134
+ if minutes > 0
135
+ "#{minutes}m#{seconds.floor}s"
136
+ else
137
+ "#{seconds.round(2)}s"
138
+ end.rjust(6)
139
+ end
140
+
141
+ def build_prefix(name)
142
+ if @verbose
143
+ "#{time_offset_prefix} #{name.rjust(8)}"
144
+ else
145
+ time_offset_prefix
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end