rbs 0.5.0 → 0.9.1

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/Gemfile +2 -0
  4. data/Rakefile +2 -3
  5. data/docs/stdlib.md +0 -2
  6. data/docs/syntax.md +6 -3
  7. data/goodcheck.yml +65 -0
  8. data/lib/rbs.rb +1 -0
  9. data/lib/rbs/ast/comment.rb +6 -0
  10. data/lib/rbs/ast/declarations.rb +44 -6
  11. data/lib/rbs/cli.rb +21 -3
  12. data/lib/rbs/constant_table.rb +1 -1
  13. data/lib/rbs/definition_builder.rb +290 -124
  14. data/lib/rbs/environment.rb +50 -37
  15. data/lib/rbs/errors.rb +68 -25
  16. data/lib/rbs/factory.rb +14 -0
  17. data/lib/rbs/location.rb +15 -0
  18. data/lib/rbs/parser.y +89 -28
  19. data/lib/rbs/prototype/rb.rb +2 -2
  20. data/lib/rbs/prototype/rbi.rb +1 -1
  21. data/lib/rbs/prototype/runtime.rb +1 -1
  22. data/lib/rbs/substitution.rb +6 -2
  23. data/lib/rbs/test.rb +82 -3
  24. data/lib/rbs/test/errors.rb +5 -1
  25. data/lib/rbs/test/hook.rb +133 -259
  26. data/lib/rbs/test/observer.rb +17 -0
  27. data/lib/rbs/test/setup.rb +37 -20
  28. data/lib/rbs/test/setup_helper.rb +29 -0
  29. data/lib/rbs/test/spy.rb +0 -321
  30. data/lib/rbs/test/tester.rb +118 -0
  31. data/lib/rbs/test/type_check.rb +42 -5
  32. data/lib/rbs/validator.rb +4 -0
  33. data/lib/rbs/version.rb +1 -1
  34. data/lib/rbs/writer.rb +2 -2
  35. data/schema/decls.json +21 -10
  36. data/stdlib/builtin/enumerable.rbs +2 -2
  37. data/stdlib/builtin/proc.rbs +1 -2
  38. data/stdlib/json/json.rbs +6 -0
  39. data/stdlib/logger/formatter.rbs +23 -0
  40. data/stdlib/logger/log_device.rbs +39 -0
  41. data/stdlib/logger/logger.rbs +507 -0
  42. data/stdlib/logger/period.rbs +7 -0
  43. data/stdlib/logger/severity.rbs +8 -0
  44. data/stdlib/pty/pty.rbs +159 -0
  45. metadata +13 -3
  46. data/lib/rbs/test/test_helper.rb +0 -180
@@ -0,0 +1,118 @@
1
+ module RBS
2
+ module Test
3
+ class Tester
4
+ attr_reader :env
5
+ attr_reader :targets
6
+
7
+ def initialize(env:)
8
+ @env = env
9
+ @targets = []
10
+ end
11
+
12
+ def factory
13
+ @factory ||= Factory.new
14
+ end
15
+
16
+ def builder
17
+ @builder ||= DefinitionBuilder.new(env: env)
18
+ end
19
+
20
+ def install!(klass, sample_size:)
21
+ RBS.logger.info { "Installing runtime type checker in #{klass}..." }
22
+
23
+ type_name = factory.type_name(klass.name).absolute!
24
+
25
+ builder.build_instance(type_name).tap do |definition|
26
+ instance_key = new_key(type_name, "InstanceChecker")
27
+ Observer.register(instance_key, MethodCallTester.new(klass, builder, definition, kind: :instance, sample_size: sample_size))
28
+
29
+ definition.methods.each do |name, method|
30
+ if method.implemented_in == type_name
31
+ RBS.logger.info { "Setting up method hook in ##{name}..." }
32
+ Hook.hook_instance_method klass, name, key: instance_key
33
+ end
34
+ end
35
+ end
36
+
37
+ builder.build_singleton(type_name).tap do |definition|
38
+ singleton_key = new_key(type_name, "SingletonChecker")
39
+ Observer.register(singleton_key, MethodCallTester.new(klass.singleton_class, builder, definition, kind: :singleton, sample_size: sample_size))
40
+
41
+ definition.methods.each do |name, method|
42
+ if method.implemented_in == type_name || name == :new
43
+ RBS.logger.info { "Setting up method hook in .#{name}..." }
44
+ Hook.hook_singleton_method klass, name, key: singleton_key
45
+ end
46
+ end
47
+ end
48
+
49
+ targets << klass
50
+ end
51
+
52
+ def new_key(type_name, prefix)
53
+ "#{prefix}__#{type_name}__#{SecureRandom.hex(10)}"
54
+ end
55
+
56
+ class TypeError < Exception
57
+ attr_reader :errors
58
+
59
+ def initialize(errors)
60
+ @errors = errors
61
+
62
+ super "TypeError: #{errors.map {|e| Errors.to_string(e) }.join(", ")}"
63
+ end
64
+ end
65
+
66
+ class MethodCallTester
67
+ attr_reader :self_class
68
+ attr_reader :definition
69
+ attr_reader :builder
70
+ attr_reader :kind
71
+ attr_reader :sample_size
72
+
73
+ def initialize(self_class, builder, definition, kind:, sample_size:)
74
+ @self_class = self_class
75
+ @definition = definition
76
+ @builder = builder
77
+ @kind = kind
78
+ @sample_size = sample_size
79
+ end
80
+
81
+ def env
82
+ builder.env
83
+ end
84
+
85
+ def check
86
+ @check ||= TypeCheck.new(self_class: self_class, builder: builder, sample_size: sample_size)
87
+ end
88
+
89
+ def format_method_name(name)
90
+ case kind
91
+ when :instance
92
+ "##{name}"
93
+ when :singleton
94
+ ".#{name}"
95
+ end
96
+ end
97
+
98
+ def call(receiver, trace)
99
+ method_name = trace.method_name
100
+ method = definition.methods[method_name]
101
+ if method
102
+ RBS.logger.debug { "Type checking `#{self_class}#{format_method_name(method_name)}`..."}
103
+ errors = check.overloaded_call(method, format_method_name(method_name), trace, errors: [])
104
+
105
+ if errors.empty?
106
+ RBS.logger.debug { "No type error detected 👏" }
107
+ else
108
+ RBS.logger.debug { "Detected type error 🚨" }
109
+ raise TypeError.new(errors)
110
+ end
111
+ else
112
+ RBS.logger.error { "Type checking `#{self_class}#{method_name}` call but no method found in definition" }
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -3,10 +3,38 @@ module RBS
3
3
  class TypeCheck
4
4
  attr_reader :self_class
5
5
  attr_reader :builder
6
+ attr_reader :sample_size
6
7
 
7
- def initialize(self_class:, builder:)
8
+ DEFAULT_SAMPLE_SIZE = 100
9
+
10
+ def initialize(self_class:, builder:, sample_size:)
8
11
  @self_class = self_class
9
12
  @builder = builder
13
+ @sample_size = sample_size
14
+ end
15
+
16
+ def overloaded_call(method, method_name, call, errors:)
17
+ es = method.method_types.map do |method_type|
18
+ es = method_call(method_name, method_type, call, errors: [])
19
+
20
+ if es.empty?
21
+ return errors
22
+ else
23
+ es
24
+ end
25
+ end
26
+
27
+ if es.size == 1
28
+ errors.push(*es[0])
29
+ else
30
+ errors << Errors::UnresolvedOverloadingError.new(
31
+ klass: self_class,
32
+ method_name: method_name,
33
+ method_types: method.method_types
34
+ )
35
+ end
36
+
37
+ errors
10
38
  end
11
39
 
12
40
  def method_call(method_name, method_type, call, errors:)
@@ -56,7 +84,7 @@ module RBS
56
84
  end
57
85
 
58
86
  def return(method_name, method_type, fun, call, errors, return_error:)
59
- unless call.exception
87
+ if call.return?
60
88
  unless value(call.return_value, fun.return_type)
61
89
  errors << return_error.new(klass: self_class,
62
90
  method_name: method_name,
@@ -151,6 +179,10 @@ module RBS
151
179
  end
152
180
  end
153
181
 
182
+ def sample(array)
183
+ sample_size && (array.size > sample_size) ? array.sample(sample_size) : array
184
+ end
185
+
154
186
  def value(val, type)
155
187
  case type
156
188
  when Types::Bases::Any
@@ -175,9 +207,14 @@ module RBS
175
207
  klass = Object.const_get(type.name.to_s)
176
208
  case
177
209
  when klass == ::Array
178
- Test.call(val, IS_AP, klass) && val.all? {|v| value(v, type.args[0]) }
210
+ Test.call(val, IS_AP, klass) && sample(val).yield_self do |val|
211
+ val.all? {|v| value(v, type.args[0]) }
212
+ end
179
213
  when klass == ::Hash
180
- Test.call(val, IS_AP, klass) && val.all? {|k, v| value(k, type.args[0]) && value(v, type.args[1]) }
214
+ Test.call(val, IS_AP, klass) && sample(val.keys).yield_self do |keys|
215
+ values = val.values_at(*keys)
216
+ keys.all? {|key| value(key, type.args[0]) } && values.all? {|v| value(v, type.args[1]) }
217
+ end
181
218
  when klass == ::Range
182
219
  Test.call(val, IS_AP, klass) && value(val.begin, type.args[0]) && value(val.end, type.args[0])
183
220
  when klass == ::Enumerator
@@ -198,7 +235,7 @@ module RBS
198
235
  end
199
236
  end
200
237
 
201
- values.all? do |v|
238
+ sample(values).all? do |v|
202
239
  if v.size == 1
203
240
  # Only one block argument.
204
241
  value(v[0], type.args[0]) || value(v, type.args[0])
@@ -41,6 +41,10 @@ module RBS
41
41
  params: type_params.each.map(&:name),
42
42
  location: type.location
43
43
  )
44
+
45
+ when Types::Alias, Types::ClassSingleton
46
+ type = absolute_type(type, context: context) { type.name.absolute! }
47
+ NoTypeFoundError.check!(type.name, env: env, location: type.location)
44
48
  end
45
49
 
46
50
  type.each_type do |type|
@@ -1,3 +1,3 @@
1
1
  module RBS
2
- VERSION = "0.5.0"
2
+ VERSION = "0.9.1"
3
3
  end
@@ -85,8 +85,8 @@ module RBS
85
85
  puts "end"
86
86
 
87
87
  when AST::Declarations::Module
88
- self_type = if decl.self_type
89
- " : #{decl.self_type}"
88
+ self_type = unless decl.self_types.empty?
89
+ " : #{decl.self_types.join(", ")}"
90
90
  end
91
91
 
92
92
  write_comment decl.comment
@@ -226,15 +226,11 @@
226
226
  "$ref": "#/definitions/classMember"
227
227
  }
228
228
  },
229
- "self_type": {
230
- "oneOf": [
231
- {
232
- "$ref": "types.json"
233
- },
234
- {
235
- "type": "null"
236
- }
237
- ]
229
+ "self_types": {
230
+ "type": "array",
231
+ "items": {
232
+ "$ref": "#/definitions/moduleSelf"
233
+ }
238
234
  },
239
235
  "annotations": {
240
236
  "type": "array",
@@ -249,7 +245,22 @@
249
245
  "$ref": "location.json"
250
246
  }
251
247
  },
252
- "required": ["declaration", "name", "type_params", "members", "self_type", "annotations", "location", "comment"]
248
+ "required": ["declaration", "name", "type_params", "members", "self_types", "annotations", "location", "comment"]
249
+ },
250
+ "moduleSelf": {
251
+ "type": "object",
252
+ "properties": {
253
+ "name": {
254
+ "type": "string"
255
+ },
256
+ "args": {
257
+ "type": "array",
258
+ "items": {
259
+ "$ref": "types.json"
260
+ }
261
+ }
262
+ },
263
+ "required": ["name", "args"]
253
264
  },
254
265
  "interfaceMember": {
255
266
  "oneOf": [
@@ -46,7 +46,7 @@ module Enumerable[unchecked out Elem, out Return]: _Each[Elem, Return]
46
46
  | () { (Elem arg0) -> untyped } -> bool
47
47
 
48
48
  def collect: [U] () { (Elem arg0) -> U } -> ::Array[U]
49
- | () -> ::Enumerator[Elem, Return]
49
+ | () -> ::Enumerator[Elem, ::Array[untyped]]
50
50
 
51
51
  def collect_concat: [U] () { (Elem arg0) -> ::Enumerator[U, untyped] } -> ::Array[U]
52
52
 
@@ -317,7 +317,7 @@ module Enumerable[unchecked out Elem, out Return]: _Each[Elem, Return]
317
317
  | () -> ::Enumerator[Elem, Return]
318
318
 
319
319
  def map: [U] () { (Elem arg0) -> U } -> ::Array[U]
320
- | () -> ::Enumerator[Elem, Return]
320
+ | () -> ::Enumerator[Elem, ::Array[untyped]]
321
321
 
322
322
  def member?: (untyped arg0) -> bool
323
323
 
@@ -269,8 +269,7 @@ class Proc < Object
269
269
  # See also Object\#hash.
270
270
  def hash: () -> Integer
271
271
 
272
- def initialize: () -> void
273
- | () { (*untyped) -> untyped } -> void
272
+ def initialize: () { (*untyped) -> untyped } -> void
274
273
 
275
274
  # Returns `true` for a [Proc](Proc.downloaded.ruby_doc) object for which
276
275
  # argument handling is rigid. Such procs are typically generated by
@@ -42,6 +42,9 @@ end
42
42
  class JSON::Ext::Generator::State
43
43
  end
44
44
 
45
+ class JSON::Ext::Parser
46
+ end
47
+
45
48
  module JSON::Pure
46
49
  end
47
50
 
@@ -51,6 +54,9 @@ end
51
54
  class JSON::Pure::Generator::State
52
55
  end
53
56
 
57
+ class JSON::Pure::Parser
58
+ end
59
+
54
60
  type json_generator = singleton(::JSON::Ext::Generator) | singleton(::JSON::Pure::Generator)
55
61
  type json_parser = singleton(::JSON::Ext::Parser) | singleton(::JSON::Pure::Parser)
56
62
  type json_state = singleton(JSON::Ext::Generator::State) | singleton(JSON::Pure::Generator::State)
@@ -0,0 +1,23 @@
1
+ class Logger
2
+ class Formatter
3
+ public
4
+
5
+ attr_accessor datetime_format: String?
6
+
7
+ def call: (String severity, Time time, untyped progname, untyped msg) -> String
8
+
9
+ private
10
+
11
+ def format_datetime: (Time time) -> untyped
12
+
13
+ def initialize: () -> void
14
+
15
+ def msg2str: (String | Exception | untyped msg) -> String
16
+ end
17
+
18
+ interface _Formatter
19
+ def call: (String severity, Time time, untyped progname, untyped msg) -> _ToS
20
+ end
21
+ end
22
+
23
+ Logger::Formatter::Format: String
@@ -0,0 +1,39 @@
1
+ class Logger
2
+ class LogDevice
3
+ # TODO: Write type signature for MonitorMixin
4
+ # include MonitorMixin
5
+
6
+ include Period
7
+
8
+ attr_reader dev: _WriteCloser
9
+ attr_reader filename: String?
10
+
11
+ public
12
+
13
+ def close: () -> nil
14
+
15
+ def reopen: (?logdev log) -> self
16
+
17
+ def write: (untyped message) -> untyped
18
+
19
+ private
20
+
21
+ def add_log_header: (IO file) -> untyped
22
+
23
+ def check_shift_log: () -> untyped
24
+
25
+ def create_logfile: (String filename) -> File
26
+
27
+ def initialize: (?untyped logdev, ?binmode: bool, ?shift_period_suffix: String, ?shift_size: Integer, ?shift_age: Numeric | String) -> void
28
+
29
+ def lock_shift_log: () { () -> untyped } -> untyped
30
+
31
+ def open_logfile: (String filename) -> File
32
+
33
+ def set_dev: (logdev log) -> untyped
34
+
35
+ def shift_log_age: () -> true
36
+
37
+ def shift_log_period: (Time period_end) -> true
38
+ end
39
+ end
@@ -0,0 +1,507 @@
1
+ # ## Description
2
+ #
3
+ # The Logger class provides a simple but sophisticated logging utility that you
4
+ # can use to output messages.
5
+ #
6
+ # The messages have associated levels, such as `INFO` or `ERROR` that indicate
7
+ # their importance. You can then give the Logger a level, and only messages at
8
+ # that level or higher will be printed.
9
+ #
10
+ # The levels are:
11
+ #
12
+ # `UNKNOWN`
13
+ # : An unknown message that should always be logged.
14
+ # `FATAL`
15
+ # : An unhandleable error that results in a program crash.
16
+ # `ERROR`
17
+ # : A handleable error condition.
18
+ # `WARN`
19
+ # : A warning.
20
+ # `INFO`
21
+ # : Generic (useful) information about system operation.
22
+ # `DEBUG`
23
+ # : Low-level information for developers.
24
+ #
25
+ #
26
+ # For instance, in a production system, you may have your Logger set to `INFO`
27
+ # or even `WARN`. When you are developing the system, however, you probably want
28
+ # to know about the program's internal state, and would set the Logger to
29
+ # `DEBUG`.
30
+ #
31
+ # **Note**: Logger does not escape or sanitize any messages passed to it.
32
+ # Developers should be aware of when potentially malicious data (user-input) is
33
+ # passed to Logger, and manually escape the untrusted data:
34
+ #
35
+ # logger.info("User-input: #{input.dump}")
36
+ # logger.info("User-input: %p" % input)
37
+ #
38
+ # You can use #formatter= for escaping all data.
39
+ #
40
+ # original_formatter = Logger::Formatter.new
41
+ # logger.formatter = proc { |severity, datetime, progname, msg|
42
+ # original_formatter.call(severity, datetime, progname, msg.dump)
43
+ # }
44
+ # logger.info(input)
45
+ #
46
+ # ### Example
47
+ #
48
+ # This creates a Logger that outputs to the standard output stream, with a level
49
+ # of `WARN`:
50
+ #
51
+ # require 'logger'
52
+ #
53
+ # logger = Logger.new(STDOUT)
54
+ # logger.level = Logger::WARN
55
+ #
56
+ # logger.debug("Created logger")
57
+ # logger.info("Program started")
58
+ # logger.warn("Nothing to do!")
59
+ #
60
+ # path = "a_non_existent_file"
61
+ #
62
+ # begin
63
+ # File.foreach(path) do |line|
64
+ # unless line =~ /^(\w+) = (.*)$/
65
+ # logger.error("Line in wrong format: #{line.chomp}")
66
+ # end
67
+ # end
68
+ # rescue => err
69
+ # logger.fatal("Caught exception; exiting")
70
+ # logger.fatal(err)
71
+ # end
72
+ #
73
+ # Because the Logger's level is set to `WARN`, only the warning, error, and
74
+ # fatal messages are recorded. The debug and info messages are silently
75
+ # discarded.
76
+ #
77
+ # ### Features
78
+ #
79
+ # There are several interesting features that Logger provides, like auto-rolling
80
+ # of log files, setting the format of log messages, and specifying a program
81
+ # name in conjunction with the message. The next section shows you how to
82
+ # achieve these things.
83
+ #
84
+ # ## HOWTOs
85
+ #
86
+ # ### How to create a logger
87
+ #
88
+ # The options below give you various choices, in more or less increasing
89
+ # complexity.
90
+ #
91
+ # 1. Create a logger which logs messages to STDERR/STDOUT.
92
+ #
93
+ # logger = Logger.new(STDERR)
94
+ # logger = Logger.new(STDOUT)
95
+ #
96
+ # 2. Create a logger for the file which has the specified name.
97
+ #
98
+ # logger = Logger.new('logfile.log')
99
+ #
100
+ # 3. Create a logger for the specified file.
101
+ #
102
+ # file = File.open('foo.log', File::WRONLY | File::APPEND)
103
+ # # To create new logfile, add File::CREAT like:
104
+ # # file = File.open('foo.log', File::WRONLY | File::APPEND | File::CREAT)
105
+ # logger = Logger.new(file)
106
+ #
107
+ # 4. Create a logger which ages the logfile once it reaches a certain size.
108
+ # Leave 10 "old" log files where each file is about 1,024,000 bytes.
109
+ #
110
+ # logger = Logger.new('foo.log', 10, 1024000)
111
+ #
112
+ # 5. Create a logger which ages the logfile daily/weekly/monthly.
113
+ #
114
+ # logger = Logger.new('foo.log', 'daily')
115
+ # logger = Logger.new('foo.log', 'weekly')
116
+ # logger = Logger.new('foo.log', 'monthly')
117
+ #
118
+ #
119
+ # ### How to log a message
120
+ #
121
+ # Notice the different methods (`fatal`, `error`, `info`) being used to log
122
+ # messages of various levels? Other methods in this family are `warn` and
123
+ # `debug`. `add` is used below to log a message of an arbitrary (perhaps
124
+ # dynamic) level.
125
+ #
126
+ # 1. Message in a block.
127
+ #
128
+ # logger.fatal { "Argument 'foo' not given." }
129
+ #
130
+ # 2. Message as a string.
131
+ #
132
+ # logger.error "Argument #{@foo} mismatch."
133
+ #
134
+ # 3. With progname.
135
+ #
136
+ # logger.info('initialize') { "Initializing..." }
137
+ #
138
+ # 4. With severity.
139
+ #
140
+ # logger.add(Logger::FATAL) { 'Fatal error!' }
141
+ #
142
+ #
143
+ # The block form allows you to create potentially complex log messages, but to
144
+ # delay their evaluation until and unless the message is logged. For example,
145
+ # if we have the following:
146
+ #
147
+ # logger.debug { "This is a " + potentially + " expensive operation" }
148
+ #
149
+ # If the logger's level is `INFO` or higher, no debug messages will be logged,
150
+ # and the entire block will not even be evaluated. Compare to this:
151
+ #
152
+ # logger.debug("This is a " + potentially + " expensive operation")
153
+ #
154
+ # Here, the string concatenation is done every time, even if the log level is
155
+ # not set to show the debug message.
156
+ #
157
+ # ### How to close a logger
158
+ #
159
+ # logger.close
160
+ #
161
+ # ### Setting severity threshold
162
+ #
163
+ # 1. Original interface.
164
+ #
165
+ # logger.sev_threshold = Logger::WARN
166
+ #
167
+ # 2. Log4r (somewhat) compatible interface.
168
+ #
169
+ # logger.level = Logger::INFO
170
+ #
171
+ # # DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN
172
+ #
173
+ # 3. Symbol or String (case insensitive)
174
+ #
175
+ # logger.level = :info
176
+ # logger.level = 'INFO'
177
+ #
178
+ # # :debug < :info < :warn < :error < :fatal < :unknown
179
+ #
180
+ # 4. Constructor
181
+ #
182
+ # Logger.new(logdev, level: Logger::INFO)
183
+ # Logger.new(logdev, level: :info)
184
+ # Logger.new(logdev, level: 'INFO')
185
+ #
186
+ #
187
+ # ## Format
188
+ #
189
+ # Log messages are rendered in the output stream in a certain format by default.
190
+ # The default format and a sample are shown below:
191
+ #
192
+ # Log format:
193
+ # SeverityID, [DateTime #pid] SeverityLabel -- ProgName: message
194
+ #
195
+ # Log sample:
196
+ # I, [1999-03-03T02:34:24.895701 #19074] INFO -- Main: info.
197
+ #
198
+ # You may change the date and time format via #datetime_format=.
199
+ #
200
+ # logger.datetime_format = '%Y-%m-%d %H:%M:%S'
201
+ # # e.g. "2004-01-03 00:54:26"
202
+ #
203
+ # or via the constructor.
204
+ #
205
+ # Logger.new(logdev, datetime_format: '%Y-%m-%d %H:%M:%S')
206
+ #
207
+ # Or, you may change the overall format via the #formatter= method.
208
+ #
209
+ # logger.formatter = proc do |severity, datetime, progname, msg|
210
+ # "#{datetime}: #{msg}\n"
211
+ # end
212
+ # # e.g. "2005-09-22 08:51:08 +0900: hello world"
213
+ #
214
+ # or via the constructor.
215
+ #
216
+ # Logger.new(logdev, formatter: proc {|severity, datetime, progname, msg|
217
+ # "#{datetime}: #{msg}\n"
218
+ # })
219
+ #
220
+ class Logger
221
+ interface _WriteCloser
222
+ def write: (_ToS) -> untyped
223
+
224
+ def close: () -> untyped
225
+ end
226
+ type logdev = _WriteCloser | String
227
+
228
+ include Logger::Severity
229
+
230
+ public
231
+
232
+ # Dump given message to the log device without any formatting. If no log device
233
+ # exists, return `nil`.
234
+ #
235
+ def <<: (untyped msg) -> (untyped | nil)
236
+
237
+ # ### Args
238
+ #
239
+ # `severity`
240
+ # : Severity. Constants are defined in Logger namespace: `DEBUG`, `INFO`,
241
+ # `WARN`, `ERROR`, `FATAL`, or `UNKNOWN`.
242
+ # `message`
243
+ # : The log message. A String or Exception.
244
+ # `progname`
245
+ # : Program name string. Can be omitted. Treated as a message if no
246
+ # `message` and `block` are given.
247
+ # `block`
248
+ # : Can be omitted. Called to get a message string if `message` is nil.
249
+ #
250
+ #
251
+ # ### Return
252
+ #
253
+ # When the given severity is not high enough (for this particular logger), log
254
+ # no message, and return `true`.
255
+ #
256
+ # ### Description
257
+ #
258
+ # Log a message if the given severity is high enough. This is the generic
259
+ # logging method. Users will be more inclined to use #debug, #info, #warn,
260
+ # #error, and #fatal.
261
+ #
262
+ # **Message format**: `message` can be any object, but it has to be converted to
263
+ # a String in order to log it. Generally, `inspect` is used if the given object
264
+ # is not a String. A special case is an `Exception` object, which will be
265
+ # printed in detail, including message, class, and backtrace. See #msg2str for
266
+ # the implementation if required.
267
+ #
268
+ # ### Bugs
269
+ #
270
+ # * Logfile is not locked.
271
+ # * Append open does not need to lock file.
272
+ # * If the OS supports multi I/O, records possibly may be mixed.
273
+ #
274
+ def add: (Integer severity, ?untyped message, ?untyped progname) ?{ () -> untyped } -> true
275
+
276
+ # Close the logging device.
277
+ #
278
+ def close: () -> untyped
279
+
280
+ # Returns the date format being used. See #datetime_format=
281
+ #
282
+ def datetime_format: () -> String?
283
+
284
+ # Set date-time format.
285
+ #
286
+ # `datetime_format`
287
+ # : A string suitable for passing to `strftime`.
288
+ #
289
+ def datetime_format=: (String datetime_format) -> String
290
+ | (nil datetime_format) -> nil
291
+
292
+ # Log a `DEBUG` message.
293
+ #
294
+ # See #info for more information.
295
+ #
296
+ def debug: (?untyped progname) ?{ () -> untyped } -> true
297
+
298
+ # Sets the severity to DEBUG.
299
+ #
300
+ def debug!: () -> Integer
301
+
302
+ # Returns `true` iff the current severity level allows for the printing of
303
+ # `DEBUG` messages.
304
+ #
305
+ def debug?: () -> bool
306
+
307
+ # Log an `ERROR` message.
308
+ #
309
+ # See #info for more information.
310
+ #
311
+ def error: (?untyped progname) ?{ () -> untyped } -> true
312
+
313
+ # Sets the severity to ERROR.
314
+ #
315
+ def error!: () -> Integer
316
+
317
+ # Returns `true` iff the current severity level allows for the printing of
318
+ # `ERROR` messages.
319
+ #
320
+ def error?: () -> bool
321
+
322
+ # Log a `FATAL` message.
323
+ #
324
+ # See #info for more information.
325
+ #
326
+ def fatal: (?untyped progname) ?{ () -> untyped } -> true
327
+
328
+ # Sets the severity to FATAL.
329
+ #
330
+ def fatal!: () -> Integer
331
+
332
+ # Returns `true` iff the current severity level allows for the printing of
333
+ # `FATAL` messages.
334
+ #
335
+ def fatal?: () -> bool
336
+
337
+ # Logging formatter, as a `Proc` that will take four arguments and return the
338
+ # formatted message. The arguments are:
339
+ #
340
+ # `severity`
341
+ # : The Severity of the log message.
342
+ # `time`
343
+ # : A Time instance representing when the message was logged.
344
+ # `progname`
345
+ # : The #progname configured, or passed to the logger method.
346
+ # `msg`
347
+ # : The *Object* the user passed to the log message; not necessarily a String.
348
+ #
349
+ #
350
+ # The block should return an Object that can be written to the logging device
351
+ # via `write`. The default formatter is used when no formatter is set.
352
+ #
353
+ def formatter: () -> (_Formatter | nil)
354
+
355
+ def formatter=: (_Formatter) -> _Formatter
356
+ | (nil) -> nil
357
+
358
+ # Log an `INFO` message.
359
+ #
360
+ # `message`
361
+ # : The message to log; does not need to be a String.
362
+ # `progname`
363
+ # : In the block form, this is the #progname to use in the log message. The
364
+ # default can be set with #progname=.
365
+ # `block`
366
+ # : Evaluates to the message to log. This is not evaluated unless the
367
+ # logger's level is sufficient to log the message. This allows you to
368
+ # create potentially expensive logging messages that are only called when
369
+ # the logger is configured to show them.
370
+ #
371
+ #
372
+ # ### Examples
373
+ #
374
+ # logger.info("MainApp") { "Received connection from #{ip}" }
375
+ # # ...
376
+ # logger.info "Waiting for input from user"
377
+ # # ...
378
+ # logger.info { "User typed #{input}" }
379
+ #
380
+ # You'll probably stick to the second form above, unless you want to provide a
381
+ # program name (which you can do with #progname= as well).
382
+ #
383
+ # ### Return
384
+ #
385
+ # See #add.
386
+ #
387
+ def info: (?untyped progname) ?{ () -> untyped } -> true
388
+
389
+ # Sets the severity to INFO.
390
+ #
391
+ def info!: () -> Integer
392
+
393
+ # Returns `true` iff the current severity level allows for the printing of
394
+ # `INFO` messages.
395
+ #
396
+ def info?: () -> bool
397
+
398
+ # Logging severity threshold (e.g. `Logger::INFO`).
399
+ #
400
+ def level: () -> Integer
401
+
402
+ # Set logging severity threshold.
403
+ #
404
+ # `severity`
405
+ # : The Severity of the log message.
406
+ #
407
+ #
408
+ def level=: (Integer | String severity) -> Integer
409
+
410
+ alias log add
411
+
412
+ # Program name to include in log messages.
413
+ #
414
+ def progname: () -> untyped
415
+
416
+ def progname=: (untyped) -> untyped
417
+
418
+ # ### Args
419
+ #
420
+ # `logdev`
421
+ # : The log device. This is a filename (String) or IO object (typically
422
+ # `STDOUT`, `STDERR`, or an open file). reopen the same filename if it is
423
+ # `nil`, do nothing for IO. Default is `nil`.
424
+ #
425
+ #
426
+ # ### Description
427
+ #
428
+ # Reopen a log device.
429
+ #
430
+ def reopen: () -> self
431
+ | (logdev?) -> self
432
+
433
+ # Logging severity threshold (e.g. `Logger::INFO`).
434
+ #
435
+ alias sev_threshold level
436
+
437
+ alias sev_threshold= level=
438
+
439
+ # Log an `UNKNOWN` message. This will be printed no matter what the logger's
440
+ # level is.
441
+ #
442
+ # See #info for more information.
443
+ #
444
+ def unknown: (?untyped progname) ?{ () -> untyped } -> true
445
+
446
+ # Log a `WARN` message.
447
+ #
448
+ # See #info for more information.
449
+ #
450
+ def warn: (?untyped progname) ?{ () -> untyped } -> true
451
+
452
+ # Sets the severity to WARN.
453
+ #
454
+ def warn!: () -> Integer
455
+
456
+ # Returns `true` iff the current severity level allows for the printing of
457
+ # `WARN` messages.
458
+ #
459
+ def warn?: () -> bool
460
+
461
+ private
462
+
463
+ def format_message: (String severity, Time datetime, untyped progname, untyped msg) -> _ToS
464
+
465
+ def format_severity: (Integer severity) -> String
466
+
467
+ # ### Args
468
+ #
469
+ # `logdev`
470
+ # : The log device. This is a filename (String) or IO object (typically
471
+ # `STDOUT`, `STDERR`, or an open file).
472
+ # `shift_age`
473
+ # : Number of old log files to keep, **or** frequency of rotation (`daily`,
474
+ # `weekly` or `monthly`). Default value is 0, which disables log file
475
+ # rotation.
476
+ # `shift_size`
477
+ # : Maximum logfile size in bytes (only applies when `shift_age` is a positive
478
+ # Integer). Defaults to `1048576` (1MB).
479
+ # `level`
480
+ # : Logging severity threshold. Default values is Logger::DEBUG.
481
+ # `progname`
482
+ # : Program name to include in log messages. Default value is nil.
483
+ # `formatter`
484
+ # : Logging formatter. Default values is an instance of Logger::Formatter.
485
+ # `datetime_format`
486
+ # : Date and time format. Default value is '%Y-%m-%d %H:%M:%S'.
487
+ # `binmode`
488
+ # : Use binary mode on the log device. Default value is false.
489
+ # `shift_period_suffix`
490
+ # : The log file suffix format for `daily`, `weekly` or `monthly` rotation.
491
+ # Default is '%Y%m%d'.
492
+ #
493
+ #
494
+ # ### Description
495
+ #
496
+ # Create an instance.
497
+ #
498
+ def initialize: (logdev logdev, ?Numeric | String shift_age, ?Integer shift_size, ?shift_period_suffix: String, ?binmode: bool, ?datetime_format: String, ?formatter: _Formatter, ?progname: String, ?level: Integer) -> void
499
+ end
500
+
501
+ Logger::ProgName: String
502
+
503
+ # Severity label for logging (max 5 chars).
504
+ #
505
+ Logger::SEV_LABEL: Array[String]
506
+
507
+ Logger::VERSION: String