hanami-utils 1.2.0.beta2 → 1.2.0.rc1

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 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