hanami-utils 2.0.0.beta1 → 2.0.0

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: f1c3da2c837516bf458d208681a9183b4ce2255d4015d1ef436344b951f72127
4
- data.tar.gz: 1dd912dfe51da2149599514b70c1d1893651d09e2a2c6f65ffdce4b5142c7132
3
+ metadata.gz: 2575ff2a640a9ffcddf8a9930a887d536ec9da4b64bc4a2883849ed261f57b43
4
+ data.tar.gz: 70fbfe55f826d940c0f5decad39840990925e061de975e53d2b672fdae9873df
5
5
  SHA512:
6
- metadata.gz: d1fb0f77574208374688089528fc4e9479f17fe514d795c0407fd6cb65c3c14cd64aa5956bf90f9e29692042c784845ea3f27c0c6c0fc5fbcf52fb439f6ba557
7
- data.tar.gz: 5e5dd4627115e3d92f8fa5c19a323e645b5bc245e72a6156c40b449af68eed08b42148639f25a5cee98e5d2739504d9cb0bfb6dd7781f54e1ae554b77955bbd9
6
+ metadata.gz: 73d5a9fade1cb961ac4be22b8967507fceebde1a9d8edb33d71634b06ef6c06538c8d0ef2b1a32a33e8442c7609b675ae99bd63780c353c6db02485ddbfd3432
7
+ data.tar.gz: 368ffe941852e67432d9574b79220b1807b1529cbdf4289fb2fd2db92299888617bc3779bd6e0c3f03b81d5fbcb9725b8b36364733d262a3c5fa2cb257db91b1
data/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  Ruby core extensions and class utilities for Hanami
4
4
 
5
+ ## v2.0.0 - 2022-11-22
6
+
7
+ ## v2.0.0.rc1 - 2022-11-08
8
+
9
+ ### Fixed
10
+
11
+ - [Benjamin Klotz] Ensure `Hanami::Utils::String.underscore` to replace `"."` (dot character) into underscore
12
+
13
+ ### Changed
14
+
15
+ - [Luca Guidi] Removed `Hanami::Logger` in favor of `Dry::Logger`
16
+
5
17
  ## v2.0.0.beta1 - 2022-07-20
6
18
 
7
19
  ### Changed
data/hanami-utils.gemspec CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.metadata["rubygems_mfa_required"] = "true"
21
21
  spec.required_ruby_version = ">= 3.0"
22
22
 
23
- spec.add_dependency "dry-transformer", "~> 0.1"
23
+ spec.add_dependency "dry-transformer", "~> 1.0", "< 2"
24
24
  spec.add_dependency "concurrent-ruby", "~> 1.0"
25
25
 
26
26
  spec.add_development_dependency "bundler", ">= 1.6", "< 3"
@@ -1,11 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hanami
4
+ # Shared Rack middleware for Hanami apps.
5
+ #
6
+ # This module is defined in hanami-utils so that any gem providing middleware can use its
7
+ # resources.
8
+ #
9
+ # @since 2.0.0
4
10
  module Middleware
5
- # Hanami middleware utils
6
-
11
+ # Base class for all errors raised during middleware loading.
12
+ #
7
13
  # @since 2.0.0
8
- class Error < ::StandardError
14
+ # @api public
15
+ class Error < StandardError
9
16
  end
10
17
  end
11
18
  end
@@ -71,33 +71,6 @@ module Hanami
71
71
  def self.load(name, namespace = Object)
72
72
  load!(name, namespace) if namespace.const_defined?(name.to_s, false)
73
73
  end
74
-
75
- def self.tokenize(pattern)
76
- if match = TOKENIZE_REGEXP.match(pattern)
77
- pre = match.pre_match
78
- post = match.post_match
79
- tokens = match[1].split(TOKENIZE_SEPARATOR)
80
- tokens.each do |token|
81
- yield("#{pre}#{token}#{post}")
82
- end
83
- else
84
- yield(pattern)
85
- end
86
-
87
- nil
88
- end
89
-
90
- # Regexp for .tokenize
91
- #
92
- # @since 1.3.0
93
- # @api private
94
- TOKENIZE_REGEXP = /\((.*)\)/
95
-
96
- # Separator for .tokenize
97
- #
98
- # @since 1.3.0
99
- # @api private
100
- TOKENIZE_SEPARATOR = "|"
101
74
  end
102
75
  end
103
76
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry-transformer"
3
+ require "dry/transformer"
4
4
 
5
5
  module Hanami
6
6
  module Utils
@@ -35,10 +35,20 @@ module Hanami
35
35
 
36
36
  # rubocop:disable Style/ClassVars
37
37
  if defined?(MultiJson)
38
+ # @since 0.8.0
39
+ # @api private
38
40
  @@engine = MultiJsonAdapter.new
41
+
42
+ # @since 0.8.0
43
+ # @api private
39
44
  ParserError = MultiJson::ParseError
40
45
  else
46
+ # @since 0.8.0
47
+ # @api private
41
48
  @@engine = ::JSON
49
+
50
+ # @since 0.8.0
51
+ # @api private
42
52
  ParserError = ::JSON::ParserError
43
53
  end
44
54
  # rubocop:enable Style/ClassVars
@@ -5,6 +5,7 @@ require "date"
5
5
  require "time"
6
6
  require "pathname"
7
7
  require "bigdecimal"
8
+ require "bigdecimal/util"
8
9
  require "hanami/utils"
9
10
  require "hanami/utils/string"
10
11
 
@@ -7,12 +7,16 @@ module Hanami
7
7
  # It doesn't check if you're writing to a file or anything, so you have to
8
8
  # check that yourself before using this module.
9
9
  #
10
+ # @api public
10
11
  # @since 1.2.0
11
12
  module ShellColor
12
- # Unknown color code error
13
+ # Error raised for unknown color codes.
13
14
  #
14
15
  # @since 1.2.0
16
+ # @api public
15
17
  class UnknownColorCodeError < ::StandardError
18
+ # @since 1.2.0
19
+ # @api private
16
20
  def initialize(code)
17
21
  super("unknown color code: `#{code.inspect}'")
18
22
  end
@@ -231,7 +231,7 @@ module Hanami
231
231
  string.gsub!(NAMESPACE_SEPARATOR, UNDERSCORE_SEPARATOR)
232
232
  string.gsub!(/([A-Z\d]+)([A-Z][a-z])/, UNDERSCORE_DIVISION_TARGET)
233
233
  string.gsub!(/([a-z\d])([A-Z])/, UNDERSCORE_DIVISION_TARGET)
234
- string.gsub!(/[[:space:]]|-/, UNDERSCORE_DIVISION_TARGET)
234
+ string.gsub!(/[[:space:]]|-|\./, UNDERSCORE_DIVISION_TARGET)
235
235
  string.downcase
236
236
  end
237
237
 
@@ -2,9 +2,10 @@
2
2
 
3
3
  module Hanami
4
4
  module Utils
5
- # Defines the version
5
+ # The current hanami-utils version.
6
6
  #
7
7
  # @since 0.1.0
8
- VERSION = "2.0.0.beta1"
8
+ # @api public
9
+ VERSION = "2.0.0"
9
10
  end
10
11
  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: 2.0.0.beta1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Guidi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-20 00:00:00.000000000 Z
11
+ date: 2022-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-transformer
@@ -16,14 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.1'
19
+ version: '1.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '2'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
27
  - - "~>"
25
28
  - !ruby/object:Gem::Version
26
- version: '0.1'
29
+ version: '1.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '2'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: concurrent-ruby
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -112,10 +118,6 @@ files:
112
118
  - README.md
113
119
  - hanami-utils.gemspec
114
120
  - lib/hanami-utils.rb
115
- - lib/hanami/logger.rb
116
- - lib/hanami/logger/colorizer.rb
117
- - lib/hanami/logger/filter.rb
118
- - lib/hanami/logger/formatter.rb
119
121
  - lib/hanami/middleware.rb
120
122
  - lib/hanami/utils.rb
121
123
  - lib/hanami/utils/blank.rb
@@ -153,11 +155,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
153
155
  version: '3.0'
154
156
  required_rubygems_version: !ruby/object:Gem::Requirement
155
157
  requirements:
156
- - - ">"
158
+ - - ">="
157
159
  - !ruby/object:Gem::Version
158
- version: 1.3.1
160
+ version: '0'
159
161
  requirements: []
160
- rubygems_version: 3.3.3
162
+ rubygems_version: 3.3.7
161
163
  signing_key:
162
164
  specification_version: 4
163
165
  summary: Ruby core extentions and Hanami utilities
@@ -1,105 +0,0 @@
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 three parts of the log message
56
- #
57
- # @since 1.2.0
58
- # @api private
59
- COLORS = ::Hash[
60
- app: :blue,
61
- datetime: :cyan,
62
- ].freeze
63
-
64
- # @since 1.2.0
65
- # @api private
66
- LEVELS = ::Hash[
67
- Hanami::Logger::DEBUG => :cyan,
68
- Hanami::Logger::INFO => :magenta,
69
- Hanami::Logger::WARN => :yellow,
70
- Hanami::Logger::ERROR => :red,
71
- Hanami::Logger::FATAL => :red,
72
- Hanami::Logger::UNKNOWN => :blue,
73
- ].freeze
74
-
75
- attr_reader :colors
76
-
77
- # @since 1.2.0
78
- # @api private
79
- def app(input)
80
- colorize(input, color: colors.fetch(:app, nil))
81
- end
82
-
83
- # @since 1.2.0
84
- # @api private
85
- def severity(input)
86
- color = LEVELS.fetch(Hanami::Logger.level(input), :gray)
87
- colorize(input, color: color)
88
- end
89
-
90
- # @since 1.2.0
91
- # @api private
92
- def datetime(input)
93
- colorize(input, color: colors.fetch(:datetime, nil))
94
- end
95
-
96
- # @since 1.2.0
97
- # @api private
98
- def colorize(message, color:)
99
- return message if color.nil?
100
-
101
- Hanami::Utils::ShellColor.call(message, color: color)
102
- end
103
- end
104
- end
105
- end
@@ -1,144 +0,0 @@
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.3.7
13
- # @api private
14
- FILTERED_VALUE = "[FILTERED]"
15
-
16
- def initialize(filters = [], mask: FILTERED_VALUE)
17
- @filters = filters
18
- @mask = mask
19
- end
20
-
21
- # @since 1.1.0
22
- # @api private
23
- def call(params)
24
- _filter(_copy_params(params))
25
- end
26
-
27
- private
28
-
29
- # @since 1.1.0
30
- # @api private
31
- attr_reader :filters
32
-
33
- # @since 1.3.7
34
- # @api private
35
- attr_reader :mask
36
-
37
- # This is a simple deep merge to merge the original input
38
- # with the filtered hash which contains '[FILTERED]' string.
39
- #
40
- # It only deep-merges if the conflict values are both hashes.
41
- #
42
- # @since 1.3.7
43
- # @api private
44
- def _deep_merge(original_hash, filtered_hash)
45
- original_hash.merge(filtered_hash) do |_key, original_item, filtered_item|
46
- if original_item.is_a?(Hash) && filtered_item.is_a?(Hash)
47
- _deep_merge(original_item, filtered_item)
48
- elsif filtered_item == FILTERED_VALUE
49
- filtered_item
50
- else
51
- original_item
52
- end
53
- end
54
- end
55
-
56
- # @since 1.1.0
57
- # @api private
58
- def _filtered_keys(hash)
59
- _key_paths(hash).select { |key| filters.any? { |filter| key =~ %r{(\.|\A)#{filter}(\.|\z)} } }
60
- end
61
-
62
- # @since 1.1.0
63
- # @api private
64
- def _key_paths(hash, base = nil)
65
- hash.inject([]) do |results, (k, v)|
66
- results + (_key_paths?(v) ? _key_paths(v, _build_path(base, k)) : [_build_path(base, k)])
67
- end
68
- end
69
-
70
- # @since 1.1.0
71
- # @api private
72
- def _build_path(base, key)
73
- [base, key.to_s].compact.join(".")
74
- end
75
-
76
- # @since 1.1.0
77
- # @api private
78
- def _actual_keys(hash, keys)
79
- search_in = hash
80
-
81
- keys.inject([]) do |res, key|
82
- correct_key = search_in.key?(key.to_sym) ? key.to_sym : key
83
- search_in = search_in[correct_key]
84
- res + [correct_key]
85
- end
86
- end
87
-
88
- # Check if the given value can be iterated (`Enumerable`) and that isn't a `File`.
89
- # This is useful to detect closed `Tempfiles`.
90
- #
91
- # @since 1.3.5
92
- # @api private
93
- #
94
- # @see https://github.com/hanami/utils/pull/342
95
- def _key_paths?(value)
96
- value.is_a?(Enumerable) && !value.is_a?(File)
97
- end
98
-
99
- # @since 1.3.7
100
- # @api private
101
- def _deep_dup(hash)
102
- hash.transform_values do |value|
103
- if value.is_a?(Hash)
104
- _deep_dup(value)
105
- else
106
- _key_paths?(value) ? value.dup : value
107
- end
108
- end
109
- end
110
-
111
- # @since 1.3.7
112
- # @api private
113
- def _copy_params(params)
114
- case params
115
- when Hash
116
- _deep_dup(params)
117
- when Array
118
- params.map { |hash| _deep_dup(hash) }
119
- end
120
- end
121
-
122
- # @since 1.3.7
123
- # @api private
124
- def _filter_hash(hash)
125
- _filtered_keys(hash).each do |key|
126
- *keys, last = _actual_keys(hash, key.split("."))
127
- keys.inject(hash, :fetch)[last] = mask
128
- end
129
- hash
130
- end
131
-
132
- # @since 1.3.7
133
- # @api private
134
- def _filter(params)
135
- case params
136
- when Hash
137
- _filter_hash(params)
138
- when Array
139
- params.map { |hash| _filter_hash(hash) }
140
- end
141
- end
142
- end
143
- end
144
- end
@@ -1,183 +0,0 @@
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)
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)}" # rubocop:disable Layout/LineLength
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 DELETED
@@ -1,362 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "logger"
4
- require "hanami/utils/string"
5
- require "hanami/utils/files"
6
-
7
- module Hanami
8
- # Hanami logger
9
- #
10
- # Implementation with the same interface of Ruby std lib `Logger`.
11
- # It uses `STDOUT`, `STDERR`, file name or open file as output stream.
12
- #
13
- #
14
- # When a Hanami application is initialized, it creates a logger for that specific application.
15
- # For instance for a `Bookshelf::Application` a `Bookshelf::Logger` will be available.
16
- #
17
- # This is useful for auto-tagging the output. Eg (`app=Booshelf`).
18
- #
19
- # When used standalone (eg. `Hanami::Logger.info`), it tags lines with `app=Shared`.
20
- #
21
- #
22
- # The available severity levels are the same of `Logger`:
23
- #
24
- # * DEBUG
25
- # * INFO
26
- # * WARN
27
- # * ERROR
28
- # * FATAL
29
- # * UNKNOWN
30
- #
31
- # Those levels are available both as class and instance methods.
32
- #
33
- # Also Hanami::Logger supports different formatters. Now available only two:
34
- #
35
- # * Formatter (default)
36
- # * JSONFormatter
37
- #
38
- # And if you want to use custom formatter you need create new class inherited from
39
- # `Formatter` class and define `_format` private method like this:
40
- #
41
- # class CustomFormatter < Formatter
42
- # private
43
- # def _format(hash)
44
- # # ...
45
- # end
46
- # end
47
- #
48
- # @since 0.5.0
49
- #
50
- # @see http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger.html
51
- # @see http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger/Severity.html
52
- #
53
- # @example Basic usage
54
- # require 'hanami'
55
- #
56
- # module Bookshelf
57
- # class Application < Hanami::Application
58
- # end
59
- # end
60
- #
61
- # # Initialize the application with the following code:
62
- # Bookshelf::Application.load!
63
- # # or
64
- # Bookshelf::Application.new
65
- #
66
- # Bookshelf::Logger.new.info('Hello')
67
- # # => app=Bookshelf severity=INFO time=1988-09-01 00:00:00 UTC message=Hello
68
- #
69
- # @example Standalone usage
70
- # require 'hanami/logger'
71
- #
72
- # Hanami::Logger.new.info('Hello')
73
- # # => app=Hanami severity=INFO time=2016-05-27 10:14:42 UTC message=Hello
74
- #
75
- # @example Custom tagging
76
- # require 'hanami/logger'
77
- #
78
- # Hanami::Logger.new('FOO').info('Hello')
79
- # # => app=FOO severity=INFO time=2016-05-27 10:14:42 UTC message=Hello
80
- #
81
- # @example Write to file
82
- # require 'hanami/logger'
83
- #
84
- # Hanami::Logger.new(stream: 'logfile.log').info('Hello')
85
- # # in logfile.log
86
- # # => app=FOO severity=INFO time=2016-05-27 10:14:42 UTC message=Hello
87
- #
88
- # @example Use JSON formatter
89
- # require 'hanami/logger'
90
- #
91
- # Hanami::Logger.new(formatter: Hanami::Logger::JSONFormatter).info('Hello')
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)
121
- class Logger < ::Logger
122
- require "hanami/logger/formatter"
123
- require "hanami/logger/colorizer"
124
-
125
- # Default application name.
126
- # This is used as a fallback for tagging purposes.
127
- #
128
- # @since 0.5.0
129
- # @api private
130
- DEFAULT_APPLICATION_NAME = "hanami"
131
-
132
- # @since 0.8.0
133
- # @api private
134
- LEVELS = ::Hash[
135
- "debug" => DEBUG,
136
- "info" => INFO,
137
- "warn" => WARN,
138
- "error" => ERROR,
139
- "fatal" => FATAL,
140
- "unknown" => UNKNOWN
141
- ].freeze
142
-
143
- # @since 1.2.0
144
- # @api private
145
- def self.level(level)
146
- case level
147
- when DEBUG..UNKNOWN
148
- level
149
- else
150
- LEVELS.fetch(level.to_s.downcase, DEBUG)
151
- end
152
- end
153
-
154
- # @since 0.5.0
155
- # @api private
156
- attr_writer :application_name
157
-
158
- # Initialize a logger
159
- #
160
- # @param application_name [String] an optional application name used for
161
- # tagging purposes
162
- #
163
- # @param args [Array<Object>] an optional set of arguments to honor Ruby's
164
- # `Logger#initialize` arguments. See Ruby documentation for details.
165
- #
166
- # @param stream [String, IO, StringIO, Pathname] an optional log stream.
167
- # This is a filename (`String`) or `IO` object (typically `$stdout`,
168
- # `$stderr`, or an open file). It defaults to `$stderr`.
169
- #
170
- # @param level [Integer,String] logging level. It can be expressed as an
171
- # integer, according to Ruby's `Logger` from standard library or as a
172
- # string with the name of the level
173
- #
174
- # @param formatter [Symbol,#_format] a formatter - We support `:json` as
175
- # JSON formatter or an object that respond to `#_format(data)`
176
- #
177
- # @since 0.5.0
178
- #
179
- # @see https://ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger.html#class-Logger-label-How+to+create+a+logger
180
- #
181
- # @example Basic usage
182
- # require 'hanami/logger'
183
- #
184
- # logger = Hanami::Logger.new
185
- # logger.info "Hello World"
186
- #
187
- # # => [Hanami] [DEBUG] [2017-03-30 15:41:01 +0200] Hello World
188
- #
189
- # @example Custom application name
190
- # require 'hanami/logger'
191
- #
192
- # logger = Hanami::Logger.new('bookshelf')
193
- # logger.info "Hello World"
194
- #
195
- # # => [bookshelf] [DEBUG] [2017-03-30 15:44:23 +0200] Hello World
196
- #
197
- # @example Logger level (Integer)
198
- # require 'hanami/logger'
199
- #
200
- # logger = Hanami::Logger.new(level: 2) # WARN
201
- # logger.info "Hello World"
202
- # # => true
203
- #
204
- # logger.info "Hello World"
205
- # # => true
206
- #
207
- # logger.warn "Hello World"
208
- # # => [Hanami] [WARN] [2017-03-30 16:00:48 +0200] Hello World
209
- #
210
- # @example Logger level (Constant)
211
- # require 'hanami/logger'
212
- #
213
- # logger = Hanami::Logger.new(level: Hanami::Logger::WARN)
214
- # logger.info "Hello World"
215
- # # => true
216
- #
217
- # logger.info "Hello World"
218
- # # => true
219
- #
220
- # logger.warn "Hello World"
221
- # # => [Hanami] [WARN] [2017-03-30 16:00:48 +0200] Hello World
222
- #
223
- # @example Logger level (String)
224
- # require 'hanami/logger'
225
- #
226
- # logger = Hanami::Logger.new(level: 'warn')
227
- # logger.info "Hello World"
228
- # # => true
229
- #
230
- # logger.info "Hello World"
231
- # # => true
232
- #
233
- # logger.warn "Hello World"
234
- # # => [Hanami] [WARN] [2017-03-30 16:00:48 +0200] Hello World
235
- #
236
- # @example Use a file
237
- # require 'hanami/logger'
238
- #
239
- # logger = Hanami::Logger.new(stream: "development.log")
240
- # logger.info "Hello World"
241
- #
242
- # # => true
243
- #
244
- # File.read("development.log")
245
- # # =>
246
- # # # Logfile created on 2017-03-30 15:52:48 +0200 by logger.rb/56815
247
- # # [Hanami] [DEBUG] [2017-03-30 15:52:54 +0200] Hello World
248
- #
249
- # @example Period rotation
250
- # require 'hanami/logger'
251
- #
252
- # # Rotate daily
253
- # logger = Hanami::Logger.new('bookshelf', 'daily', stream: 'development.log')
254
- #
255
- # @example File size rotation
256
- # require 'hanami/logger'
257
- #
258
- # # leave 10 old log files where the size is about 1,024,000 bytes
259
- # logger = Hanami::Logger.new('bookshelf', 10, 1024000, stream: 'development.log')
260
- #
261
- # @example Use a StringIO
262
- # require 'hanami/logger'
263
- #
264
- # stream = StringIO.new
265
- # logger = Hanami::Logger.new(stream: stream)
266
- # logger.info "Hello World"
267
- #
268
- # # => true
269
- #
270
- # stream.rewind
271
- # stream.read
272
- #
273
- # # => "[Hanami] [DEBUG] [2017-03-30 15:55:22 +0200] Hello World\n"
274
- #
275
- # @example JSON formatter
276
- # require 'hanami/logger'
277
- #
278
- # logger = Hanami::Logger.new(formatter: :json)
279
- # logger.info "Hello World"
280
- #
281
- # # => {"app":"Hanami","severity":"DEBUG","time":"2017-03-30T13:57:59Z","message":"Hello World"}
282
- # rubocop:disable Lint/SuppressedException
283
- # rubocop:disable Metrics/ParameterLists
284
- def initialize(application_name = nil, *args, stream: $stdout, level: DEBUG, formatter: nil, filter: [], colorizer: nil) # rubocop:disable Layout/LineLength
285
- begin
286
- Utils::Files.mkdir_p(stream)
287
- rescue TypeError
288
- end
289
-
290
- super(stream, *args)
291
-
292
- @level = _level(level)
293
- @stream = stream
294
- @application_name = application_name
295
- @formatter = Formatter.fabricate(formatter, self.application_name, filter, lookup_colorizer(colorizer))
296
- end
297
-
298
- # rubocop:enable Metrics/ParameterLists
299
- # rubocop:enable Lint/SuppressedException
300
-
301
- # Returns the current application name, this is used for tagging purposes
302
- #
303
- # @return [String] the application name
304
- #
305
- # @since 0.5.0
306
- def application_name
307
- @application_name || _application_name_from_namespace || _default_application_name
308
- end
309
-
310
- # @since 0.8.0
311
- # @api private
312
- def level=(value)
313
- super _level(value)
314
- end
315
-
316
- # Closes the logging stream if this stream isn't an STDOUT
317
- #
318
- # @since 0.8.0
319
- def close
320
- super unless [STDOUT, $stdout].include?(@stream) # rubocop:disable Style/GlobalStdStream
321
- end
322
-
323
- private
324
-
325
- # @since 0.5.0
326
- # @api private
327
- def _application_name_from_namespace
328
- class_name = self.class.name
329
- namespace = Utils::String.namespace(class_name)
330
-
331
- class_name != namespace and return namespace
332
- end
333
-
334
- # @since 0.5.0
335
- # @api private
336
- def _default_application_name
337
- DEFAULT_APPLICATION_NAME
338
- end
339
-
340
- # @since 0.8.0
341
- # @api private
342
- def _level(level)
343
- self.class.level(level)
344
- end
345
-
346
- # @since 1.2.0
347
- # @api private
348
- def lookup_colorizer(colorizer)
349
- return NullColorizer.new if colorizer == false
350
-
351
- colorizer || (tty? ? Colorizer : NullColorizer).new
352
- end
353
-
354
- # @since 1.2.0
355
- # @api private
356
- def tty?
357
- return false if @logdev.nil?
358
-
359
- @logdev.dev.tty?
360
- end
361
- end
362
- end