hanami-utils 1.2.0.beta2 → 1.2.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b955a7e152875e4cc34e5ddec80dafe8899583ab1acd02d76b8159d6b0fae97d
4
- data.tar.gz: a457c7b92519e895fd2ce3551fec98533f46719284bd252592e0306122742cb2
3
+ metadata.gz: 86da2fce7ef69bfbd91366caa3abcc403dcacbf581fca38eb3b242f4dcbca5e7
4
+ data.tar.gz: 34213163f0aff44ee06516a4ceb7fdcbe96802a4b101a1bb5c45b6220c8e8736
5
5
  SHA512:
6
- metadata.gz: ba3e080d6256a17b2fc942f5f3614ffda59041c454b3bccf7ace5dd84949a2fa8e074d1e257d279b317c138e8e7e34bff3358d4ea6f4329bb96935928eb46e5f
7
- data.tar.gz: 9b33223b53a7df21a33304fc368b95db9b218ab72da601967fa83bc650291485155afd2c9c054646e123da11fc6bd32150211c792b7af044d98658ad60bb6ade
6
+ metadata.gz: 607969cb2bc3673ef48a15102342409b5e383433c55dc39543f51dd4436e384f56050e8d48be9defddba15d2933644d001c59ce6863bffd203f020a58b34753d
7
+ data.tar.gz: c9374f9627a418002d7753536025f9b3724cfea6fb18a77344ccdfcc0a8d94375cf26c687fd8d91edda8fb036b75d7dda16674785260f37a8507672d62935a9c
data/CHANGELOG.md CHANGED
@@ -1,6 +1,13 @@
1
1
  # Hanami::Utils
2
2
  Ruby core extentions and class utilities for Hanami
3
3
 
4
+ ## v1.2.0.rc1 - 2018-03-30
5
+ ### Added
6
+ - [Oana Sipos & Sean Collins & Luca Guidi] Colored logging
7
+
8
+ ### Fixed
9
+ - [Luca Guidi] Make `Hanami::Logger` to properly log hash messages
10
+
4
11
  ## v1.2.0.beta2 - 2018-03-23
5
12
 
6
13
  ## v1.2.0.beta1 - 2018-02-28
data/README.md CHANGED
@@ -124,6 +124,14 @@ Manage directories where to find Ruby source code or web static assets. [[API do
124
124
 
125
125
  Safe logic to manage relative URLs. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/PathPrefix)]
126
126
 
127
+ ### Hanami::Utils::QueryString
128
+
129
+ URI query string transformation [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/QueryString)]
130
+
131
+ ### Hanami::Utils::ShellColor
132
+
133
+ Shell colorizer [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/ShellColor)]
134
+
127
135
  ### Hanami::Utils::String
128
136
 
129
137
  Enhanced version of Ruby's `String`. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/String)]
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logger"
4
+ require "hanami/utils/shell_color"
5
+
6
+ module Hanami
7
+ class Logger < ::Logger
8
+ # Null colorizer for logger streams that aren't a TTY (eg. files)
9
+ #
10
+ # @since 1.2.0
11
+ # @api private
12
+ class NullColorizer
13
+ # @since 1.2.0
14
+ # @api private
15
+ def call(app, severity, datetime, _progname)
16
+ ::Hash[
17
+ app: app,
18
+ severity: severity,
19
+ time: datetime,
20
+ ]
21
+ end
22
+ end
23
+
24
+ # Hanami::Logger Default Colorizer
25
+ #
26
+ # This colorizer takes in parts of the log message and returns them with
27
+ # proper shellcode to colorize when displayed to a tty.
28
+ #
29
+ # @since 1.2.0
30
+ # @api private
31
+ class Colorizer < NullColorizer
32
+ def initialize(colors: COLORS)
33
+ @colors = colors
34
+ end
35
+
36
+ # Colorize the inputs
37
+ #
38
+ # @param app [#to_s] the app name
39
+ # @param severity [#to_s] log severity
40
+ # @param datetime [#to_s] timestamp
41
+ # @param _progname [#to_s] program name - ignored, accepted for
42
+ # compatibility with Ruby's Logger
43
+ #
44
+ # @return [::Hash] an Hash containing the keys `:app`, `:severity`, and `:time`
45
+ def call(app, severity, datetime, _progname)
46
+ ::Hash[
47
+ app: app(app),
48
+ severity: severity(severity),
49
+ time: datetime(datetime),
50
+ ]
51
+ end
52
+
53
+ private
54
+
55
+ # The colors defined for the four parts of the log message
56
+ #
57
+ # These can be overridden, keeping the name keys, with acceptable values
58
+ # being any from Hanami::Utils::ShellColor::COLORS
59
+ #
60
+ # @since 1.2.0
61
+ COLORS = ::Hash[
62
+ app: :blue,
63
+ severity: :magenta,
64
+ datetime: :cyan,
65
+ ].freeze
66
+
67
+ attr_reader :colors
68
+
69
+ # @since 1.2.0
70
+ # @api private
71
+ def app(input)
72
+ colorize(input, color: colors.fetch(:app, nil))
73
+ end
74
+
75
+ # @since 1.2.0
76
+ # @api private
77
+ def severity(input)
78
+ colorize(input, color: colors.fetch(:severity, nil))
79
+ end
80
+
81
+ # @since 1.2.0
82
+ # @api private
83
+ def datetime(input)
84
+ colorize(input, color: colors.fetch(:datetime, nil))
85
+ end
86
+
87
+ # @since 1.2.0
88
+ # @api private
89
+ def colorize(message, color:)
90
+ return message if color.nil?
91
+ Hanami::Utils::ShellColor.call(message, color: color)
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logger"
4
+
5
+ module Hanami
6
+ class Logger < ::Logger
7
+ # Filtering logic
8
+ #
9
+ # @since 1.1.0
10
+ # @api private
11
+ class Filter
12
+ # @since 1.1.0
13
+ # @api private
14
+ def initialize(filters = [])
15
+ @filters = filters
16
+ end
17
+
18
+ # @since 1.1.0
19
+ # @api private
20
+ def call(hash)
21
+ _filtered_keys(hash).each do |key|
22
+ *keys, last = _actual_keys(hash, key.split("."))
23
+ keys.inject(hash, :fetch)[last] = "[FILTERED]"
24
+ end
25
+
26
+ hash
27
+ end
28
+
29
+ private
30
+
31
+ # @since 1.1.0
32
+ # @api private
33
+ attr_reader :filters
34
+
35
+ # @since 1.1.0
36
+ # @api private
37
+ def _filtered_keys(hash)
38
+ _key_paths(hash).select { |key| filters.any? { |filter| key =~ %r{(\.|\A)#{filter}(\.|\z)} } }
39
+ end
40
+
41
+ # @since 1.1.0
42
+ # @api private
43
+ def _key_paths(hash, base = nil)
44
+ hash.inject([]) do |results, (k, v)|
45
+ results + (v.respond_to?(:each) ? _key_paths(v, _build_path(base, k)) : [_build_path(base, k)])
46
+ end
47
+ end
48
+
49
+ # @since 1.1.0
50
+ # @api private
51
+ def _build_path(base, key)
52
+ [base, key.to_s].compact.join(".")
53
+ end
54
+
55
+ # @since 1.1.0
56
+ # @api private
57
+ def _actual_keys(hash, keys)
58
+ search_in = hash
59
+
60
+ keys.inject([]) do |res, key|
61
+ correct_key = search_in.key?(key.to_sym) ? key.to_sym : key
62
+ search_in = search_in[correct_key]
63
+ res + [correct_key]
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+ require "json"
5
+ require "logger"
6
+ require "hanami/utils/json"
7
+ require "hanami/utils/class_attribute"
8
+ require "hanami/utils/query_string"
9
+
10
+ module Hanami
11
+ class Logger < ::Logger
12
+ # Hanami::Logger default formatter.
13
+ # This formatter returns string in key=value format.
14
+ #
15
+ # @since 0.5.0
16
+ # @api private
17
+ #
18
+ # @see http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger/Formatter.html
19
+ class Formatter < ::Logger::Formatter
20
+ require "hanami/logger/filter"
21
+ require "hanami/logger/colorizer"
22
+
23
+ # @since 0.8.0
24
+ # @api private
25
+ SEPARATOR = " "
26
+
27
+ # @since 0.8.0
28
+ # @api private
29
+ NEW_LINE = $/
30
+
31
+ # @since 1.0.0
32
+ # @api private
33
+ RESERVED_KEYS = %i[app severity time].freeze
34
+
35
+ include Utils::ClassAttribute
36
+
37
+ class_attribute :subclasses
38
+ self.subclasses = Set.new
39
+
40
+ def self.fabricate(formatter, application_name, filters, colorizer)
41
+ fabricated_formatter = _formatter_instance(formatter)
42
+
43
+ fabricated_formatter.application_name = application_name
44
+ fabricated_formatter.filter = Filter.new(filters)
45
+ fabricated_formatter.colorizer = colorizer
46
+
47
+ fabricated_formatter
48
+ end
49
+
50
+ # @api private
51
+ def self.inherited(subclass)
52
+ super
53
+ subclasses << subclass
54
+ end
55
+
56
+ # @api private
57
+ def self.eligible?(name)
58
+ name == :default
59
+ end
60
+
61
+ # @api private
62
+ # @since 1.1.0
63
+ def self._formatter_instance(formatter)
64
+ case formatter
65
+ when Symbol
66
+ (subclasses.find { |s| s.eligible?(formatter) } || self).new
67
+ when nil
68
+ new
69
+ else
70
+ formatter
71
+ end
72
+ end
73
+ private_class_method :_formatter_instance
74
+
75
+ # @since 0.5.0
76
+ # @api private
77
+ attr_writer :application_name
78
+
79
+ # @since 1.0.0
80
+ # @api private
81
+ attr_reader :application_name
82
+
83
+ # @since 1.2.0
84
+ # @api private
85
+ attr_writer :filter
86
+
87
+ # @since 1.2.0
88
+ # @api private
89
+ attr_writer :colorizer
90
+
91
+ # @since 0.5.0
92
+ # @api private
93
+ #
94
+ # @see http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger/Formatter.html#method-i-call
95
+ def call(severity, time, progname, msg)
96
+ colorized = @colorizer.call(application_name, severity, time, progname)
97
+ colorized.merge!(_message_hash(msg))
98
+
99
+ _format(colorized)
100
+ end
101
+
102
+ private
103
+
104
+ # @since 0.8.0
105
+ # @api private
106
+ def _message_hash(message) # rubocop:disable Metrics/MethodLength
107
+ case message
108
+ when Hash
109
+ @filter.call(message)
110
+ when Exception
111
+ Hash[
112
+ message: message.message,
113
+ backtrace: message.backtrace || [],
114
+ error: message.class
115
+ ]
116
+ else
117
+ Hash[message: message]
118
+ end
119
+ end
120
+
121
+ # @since 0.8.0
122
+ # @api private
123
+ def _format(hash)
124
+ "#{_line_front_matter(hash.delete(:app), hash.delete(:severity), hash.delete(:time))}#{SEPARATOR}#{_format_message(hash)}"
125
+ end
126
+
127
+ # @since 1.2.0
128
+ # @api private
129
+ def _line_front_matter(*args)
130
+ args.map { |string| "[#{string}]" }.join(SEPARATOR)
131
+ end
132
+
133
+ # @since 1.2.0
134
+ # @api private
135
+ def _format_message(hash)
136
+ if hash.key?(:error)
137
+ _format_error(hash)
138
+ elsif hash.key?(:params)
139
+ "#{hash.values.join(SEPARATOR)}#{NEW_LINE}"
140
+ else
141
+ "#{Utils::QueryString.call(hash[:message] || hash)}#{NEW_LINE}"
142
+ end
143
+ end
144
+
145
+ # @since 1.2.0
146
+ # @api private
147
+ def _format_error(hash)
148
+ result = [hash[:error], hash[:message]].compact.join(": ").concat(NEW_LINE)
149
+ hash[:backtrace].each do |line|
150
+ result << "from #{line}#{NEW_LINE}"
151
+ end
152
+
153
+ result
154
+ end
155
+ end
156
+
157
+ # Hanami::Logger JSON formatter.
158
+ # This formatter returns string in JSON format.
159
+ #
160
+ # @since 0.5.0
161
+ # @api private
162
+ class JSONFormatter < Formatter
163
+ # @api private
164
+ def self.eligible?(name)
165
+ name == :json
166
+ end
167
+
168
+ # @api private
169
+ def colorizer=(*)
170
+ @colorizer = NullColorizer.new
171
+ end
172
+
173
+ private
174
+
175
+ # @since 0.8.0
176
+ # @api private
177
+ def _format(hash)
178
+ hash[:time] = hash[:time].utc.iso8601
179
+ Hanami::Utils::Json.generate(hash) + NEW_LINE
180
+ end
181
+ end
182
+ end
183
+ end
data/lib/hanami/logger.rb CHANGED
@@ -1,11 +1,8 @@
1
- require 'set'
2
- require 'json'
3
- require 'logger'
4
- require 'hanami/utils/string'
5
- require 'hanami/utils/json'
6
- require 'hanami/utils/hash'
7
- require 'hanami/utils/class_attribute'
8
- require 'hanami/utils/files'
1
+ # frozen_string_literal: true
2
+
3
+ require "logger"
4
+ require "hanami/utils/string"
5
+ require "hanami/utils/files"
9
6
 
10
7
  module Hanami
11
8
  # Hanami logger
@@ -82,248 +79,65 @@ module Hanami
82
79
  # # => app=FOO severity=INFO time=2016-05-27 10:14:42 UTC message=Hello
83
80
  #
84
81
  # @example Write to file
85
- # require 'hanami'
82
+ # require 'hanami/logger'
86
83
  #
87
84
  # Hanami::Logger.new(stream: 'logfile.log').info('Hello')
88
85
  # # in logfile.log
89
86
  # # => app=FOO severity=INFO time=2016-05-27 10:14:42 UTC message=Hello
90
87
  #
91
88
  # @example Use JSON formatter
92
- # require 'hanami'
89
+ # require 'hanami/logger'
93
90
  #
94
91
  # Hanami::Logger.new(formatter: Hanami::Logger::JSONFormatter).info('Hello')
95
92
  # # => "{\"app\":\"Hanami\",\"severity\":\"INFO\",\"time\":\"1988-09-01 00:00:00 UTC\",\"message\":\"Hello\"}"
93
+ #
94
+ # @example Disable colorization
95
+ # require 'hanami/logger'
96
+ #
97
+ # Hanami::Logger.new(colorizer: false)
98
+ #
99
+ # @example Use custom colors
100
+ # require 'hanami/logger'
101
+ #
102
+ # Hanami::Logger.new(colorizer: Hanami::Logger::Colorizer.new(colors: { app: :red }))
103
+ #
104
+ # @example Use custom colorizer
105
+ # require "hanami/logger"
106
+ # require "paint" # gem install paint
107
+ #
108
+ # class LogColorizer < Hanami::Logger::Colorizer
109
+ # def initialize(colors: { app: [:red, :bright], severity: [:red, :blue], datetime: [:italic, :yellow] })
110
+ # super
111
+ # end
112
+ #
113
+ # private
114
+ #
115
+ # def colorize(message, color:)
116
+ # Paint[message, *color]
117
+ # end
118
+ # end
119
+ #
120
+ # Hanami::Logger.new(colorizer: LogColorizer.new)
96
121
  class Logger < ::Logger
97
- # Hanami::Logger default formatter.
98
- # This formatter returns string in key=value format.
99
- #
100
- # @since 0.5.0
101
- # @api private
102
- #
103
- # @see http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger/Formatter.html
104
- class Formatter < ::Logger::Formatter
105
- # @since 0.8.0
106
- # @api private
107
- SEPARATOR = ' '.freeze
108
-
109
- # @since 0.8.0
110
- # @api private
111
- NEW_LINE = $/
112
-
113
- # @since 1.0.0
114
- # @api private
115
- RESERVED_KEYS = %i[app severity time].freeze
116
-
117
- include Utils::ClassAttribute
118
-
119
- class_attribute :subclasses
120
- self.subclasses = Set.new
121
-
122
- def self.fabricate(formatter, application_name, filters)
123
- fabricated_formatter = _formatter_instance(formatter)
124
-
125
- fabricated_formatter.application_name = application_name
126
- fabricated_formatter.hash_filter = HashFilter.new(filters)
127
-
128
- fabricated_formatter
129
- end
130
-
131
- # @api private
132
- def self.inherited(subclass)
133
- super
134
- subclasses << subclass
135
- end
136
-
137
- # @api private
138
- def self.eligible?(name)
139
- name == :default
140
- end
141
-
142
- # @api private
143
- # @since 1.1.0
144
- def self._formatter_instance(formatter)
145
- case formatter
146
- when Symbol
147
- (subclasses.find { |s| s.eligible?(formatter) } || self).new
148
- when nil
149
- new
150
- else
151
- formatter
152
- end
153
- end
154
- private_class_method :_formatter_instance
155
-
156
- # @since 0.5.0
157
- # @api private
158
- attr_writer :application_name
159
-
160
- # @since 1.0.0
161
- # @api private
162
- attr_reader :application_name
163
-
164
- # @since 1.1.0
165
- # @api private
166
- attr_writer :hash_filter
167
-
168
- # @since 0.5.0
169
- # @api private
170
- #
171
- # @see http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger/Formatter.html#method-i-call
172
- def call(severity, time, _progname, msg)
173
- _format({
174
- app: application_name,
175
- severity: severity,
176
- time: time
177
- }.merge(
178
- _message_hash(msg)
179
- ))
180
- end
181
-
182
- private
183
-
184
- # @since 0.8.0
185
- # @api private
186
- def _message_hash(message) # rubocop:disable Metrics/MethodLength
187
- case message
188
- when Hash
189
- @hash_filter.filter(message)
190
- when Exception
191
- Hash[
192
- message: message.message,
193
- backtrace: message.backtrace || [],
194
- error: message.class
195
- ]
196
- else
197
- Hash[message: message]
198
- end
199
- end
200
-
201
- # @since 0.8.0
202
- # @api private
203
- def _format(hash)
204
- result = RESERVED_KEYS.map { |k| "[#{hash[k]}]" }.join(SEPARATOR)
205
- return _format_error(result, hash) if hash.key?(:error)
206
-
207
- values = hash.each_with_object([]) do |(k, v), memo|
208
- memo << v unless RESERVED_KEYS.include?(k)
209
- end
210
-
211
- result << " #{values.join(SEPARATOR)}#{NEW_LINE}"
212
- result
213
- end
214
-
215
- # @api private
216
- def _format_error(result, hash)
217
- result << " #{hash[:error]}:" if hash.key?(:error)
218
- result << " #{hash[:message]}#{NEW_LINE}"
219
- if hash.key?(:backtrace)
220
- hash[:backtrace].each do |line|
221
- result << "from #{line}#{NEW_LINE}"
222
- end
223
- end
224
-
225
- result
226
- end
227
-
228
- # Filtering logic
229
- #
230
- # @since 1.1.0
231
- # @api private
232
- class HashFilter
233
- # @since 1.1.0
234
- # @api private
235
- attr_reader :filters
236
-
237
- # @since 1.1.0
238
- # @api private
239
- def initialize(filters = [])
240
- @filters = filters
241
- end
242
-
243
- # @since 1.1.0
244
- # @api private
245
- def filter(hash)
246
- _filtered_keys(hash).each do |key|
247
- *keys, last = _actual_keys(hash, key.split('.'))
248
- keys.inject(hash, :fetch)[last] = '[FILTERED]'
249
- end
250
-
251
- hash
252
- end
253
-
254
- private
255
-
256
- # @since 1.1.0
257
- # @api private
258
- def _filtered_keys(hash)
259
- _key_paths(hash).select { |key| filters.any? { |filter| key =~ %r{(\.|\A)#{filter}(\.|\z)} } }
260
- end
261
-
262
- # @since 1.1.0
263
- # @api private
264
- def _key_paths(hash, base = nil)
265
- hash.inject([]) do |results, (k, v)|
266
- results + (v.respond_to?(:each) ? _key_paths(v, _build_path(base, k)) : [_build_path(base, k)])
267
- end
268
- end
269
-
270
- # @since 1.1.0
271
- # @api private
272
- def _build_path(base, key)
273
- [base, key.to_s].compact.join('.')
274
- end
275
-
276
- # @since 1.1.0
277
- # @api private
278
- def _actual_keys(hash, keys)
279
- search_in = hash
280
-
281
- keys.inject([]) do |res, key|
282
- correct_key = search_in.key?(key.to_sym) ? key.to_sym : key
283
- search_in = search_in[correct_key]
284
- res + [correct_key]
285
- end
286
- end
287
- end
288
- end
289
-
290
- # Hanami::Logger JSON formatter.
291
- # This formatter returns string in JSON format.
292
- #
293
- # @since 0.5.0
294
- # @api private
295
- class JSONFormatter < Formatter
296
- # @api private
297
- def self.eligible?(name)
298
- name == :json
299
- end
300
-
301
- private
302
-
303
- # @since 0.8.0
304
- # @api private
305
- def _format(hash)
306
- hash[:time] = hash[:time].utc.iso8601
307
- Hanami::Utils::Json.generate(hash) + NEW_LINE
308
- end
309
- end
122
+ require "hanami/logger/formatter"
123
+ require "hanami/logger/colorizer"
310
124
 
311
125
  # Default application name.
312
126
  # This is used as a fallback for tagging purposes.
313
127
  #
314
128
  # @since 0.5.0
315
129
  # @api private
316
- DEFAULT_APPLICATION_NAME = 'hanami'.freeze
130
+ DEFAULT_APPLICATION_NAME = "hanami"
317
131
 
318
132
  # @since 0.8.0
319
133
  # @api private
320
- LEVELS = Hash[
321
- 'debug' => DEBUG,
322
- 'info' => INFO,
323
- 'warn' => WARN,
324
- 'error' => ERROR,
325
- 'fatal' => FATAL,
326
- 'unknown' => UNKNOWN
134
+ LEVELS = ::Hash[
135
+ "debug" => DEBUG,
136
+ "info" => INFO,
137
+ "warn" => WARN,
138
+ "error" => ERROR,
139
+ "fatal" => FATAL,
140
+ "unknown" => UNKNOWN
327
141
  ].freeze
328
142
 
329
143
  # @since 0.5.0
@@ -456,7 +270,7 @@ module Hanami
456
270
  # # => {"app":"Hanami","severity":"DEBUG","time":"2017-03-30T13:57:59Z","message":"Hello World"}
457
271
  # rubocop:disable Lint/HandleExceptions
458
272
  # rubocop:disable Metrics/ParameterLists
459
- def initialize(application_name = nil, *args, stream: $stdout, level: DEBUG, formatter: nil, filter: [])
273
+ def initialize(application_name = nil, *args, stream: $stdout, level: DEBUG, formatter: nil, filter: [], colorizer: nil)
460
274
  begin
461
275
  Utils::Files.mkdir_p(stream)
462
276
  rescue TypeError
@@ -467,8 +281,9 @@ module Hanami
467
281
  @level = _level(level)
468
282
  @stream = stream
469
283
  @application_name = application_name
470
- @formatter = Formatter.fabricate(formatter, self.application_name, filter)
284
+ @formatter = Formatter.fabricate(formatter, self.application_name, filter, lookup_colorizer(colorizer))
471
285
  end
286
+
472
287
  # rubocop:enable Metrics/ParameterLists
473
288
  # rubocop:enable Lint/HandleExceptions
474
289
 
@@ -500,7 +315,7 @@ module Hanami
500
315
  # @api private
501
316
  def _application_name_from_namespace
502
317
  class_name = self.class.name
503
- namespace = Utils::String.new(class_name).namespace
318
+ namespace = Utils::String.namespace(class_name)
504
319
 
505
320
  class_name != namespace and return namespace
506
321
  end
@@ -521,5 +336,18 @@ module Hanami
521
336
  LEVELS.fetch(level.to_s.downcase, DEBUG)
522
337
  end
523
338
  end
339
+
340
+ # @since 1.2.0
341
+ # @api private
342
+ def lookup_colorizer(colorizer)
343
+ return NullColorizer.new if colorizer == false
344
+ colorizer || (tty? ? Colorizer : NullColorizer).new
345
+ end
346
+
347
+ # @since 1.2.0
348
+ # @api private
349
+ def tty?
350
+ @logdev.dev.tty?
351
+ end
524
352
  end
525
353
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cgi"
4
+
5
+ module Hanami
6
+ module Utils
7
+ # URI query string transformations
8
+ #
9
+ # @since 1.2.0
10
+ module QueryString
11
+ # Serialize input into a query string
12
+ #
13
+ # @param input [Object] the input
14
+ #
15
+ # @return [::String] the query string
16
+ #
17
+ # @since 1.2.0
18
+ #
19
+ # TODO: this is a very basic implementation that needs to be expanded
20
+ def self.call(input)
21
+ case input
22
+ when ::Hash
23
+ input.to_a.join("=")
24
+ else
25
+ input.to_s
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module Utils
5
+ # Shell helper for colorizing STDOUT
6
+ #
7
+ # It doesn't check if you're writing to a file or anything, so you have to
8
+ # check that yourself before using this module.
9
+ #
10
+ # @since 1.2.0
11
+ module ShellColor
12
+ # Unknown color code error
13
+ #
14
+ # @since 1.2.0
15
+ class UnknownColorCodeError < ::StandardError
16
+ def initialize(code)
17
+ super("unknown color code: `#{code.inspect}'")
18
+ end
19
+ end
20
+
21
+ # Escape codes for terminals to output strings in colors
22
+ #
23
+ # @since 1.2.0
24
+ # @api private
25
+ COLORS = ::Hash[
26
+ black: 30,
27
+ red: 31,
28
+ green: 32,
29
+ yellow: 33,
30
+ blue: 34,
31
+ magenta: 35,
32
+ cyan: 36,
33
+ gray: 37,
34
+ ].freeze
35
+
36
+ # Colorize output
37
+ # 8 colors available: black, red, green, yellow, blue, magenta, cyan, and gray
38
+ #
39
+ # @param input [#to_s] the string to colorize
40
+ # @param color [Symbol] the color
41
+ #
42
+ # @raise [Hanami::Utils::ShellColor::UnknownColorError] if the color code is
43
+ # unknown
44
+ #
45
+ # @return [String] the colorized string
46
+ #
47
+ # @since 1.2.0
48
+ def self.call(input, color:)
49
+ "\e[#{color_code(color)}m#{input}\e[0m"
50
+ end
51
+
52
+ # Helper method to translate between color names and terminal escape codes
53
+ #
54
+ # @api private
55
+ # @since 1.2.0
56
+ #
57
+ # @raise [Hanami::Utils::ShellColor::UnknownColorError] if the color code is
58
+ # unknown
59
+ def self.color_code(code)
60
+ COLORS.fetch(code) { raise UnknownColorCodeError.new(code) }
61
+ end
62
+ end
63
+ end
64
+ end
@@ -3,6 +3,6 @@ module Hanami
3
3
  # Defines the version
4
4
  #
5
5
  # @since 0.1.0
6
- VERSION = '1.2.0.beta2'.freeze
6
+ VERSION = '1.2.0.rc1'.freeze
7
7
  end
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hanami-utils
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0.beta2
4
+ version: 1.2.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Guidi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-23 00:00:00.000000000 Z
11
+ date: 2018-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: transproc
@@ -94,6 +94,9 @@ files:
94
94
  - lib/hanami-utils.rb
95
95
  - lib/hanami/interactor.rb
96
96
  - lib/hanami/logger.rb
97
+ - lib/hanami/logger/colorizer.rb
98
+ - lib/hanami/logger/filter.rb
99
+ - lib/hanami/logger/formatter.rb
97
100
  - lib/hanami/utils.rb
98
101
  - lib/hanami/utils/basic_object.rb
99
102
  - lib/hanami/utils/blank.rb
@@ -112,6 +115,8 @@ files:
112
115
  - lib/hanami/utils/kernel.rb
113
116
  - lib/hanami/utils/load_paths.rb
114
117
  - lib/hanami/utils/path_prefix.rb
118
+ - lib/hanami/utils/query_string.rb
119
+ - lib/hanami/utils/shell_color.rb
115
120
  - lib/hanami/utils/string.rb
116
121
  - lib/hanami/utils/version.rb
117
122
  homepage: http://hanamirb.org