From 9b679f6cdd2b23ed74c601dbe18cb21ebbd012e7 Mon Sep 17 00:00:00 2001 From: ksss Date: Sun, 15 Feb 2026 23:42:30 +0900 Subject: [PATCH] Add bundled gems for Ruby v4.0 The following gems have been "bundled gems" since Ruby v4.0. * benchmark * logger * pstore rdoc is used within rbs and requires sufficient consideration, so we will defer it for now. --- gems/benchmark/.rubocop.yml | 19 + gems/benchmark/0.5/benchmark.rbs | 452 ++++++++++++++++++ gems/benchmark/_reviewers.yaml | 2 + gems/logger/.rubocop.yml | 19 + gems/logger/1.7/formatter.rbs | 45 ++ gems/logger/1.7/log_device.rbs | 100 ++++ gems/logger/1.7/logger.rbs | 796 +++++++++++++++++++++++++++++++ gems/logger/1.7/manifest.yaml | 2 + gems/logger/1.7/period.rbs | 17 + gems/logger/1.7/severity.rbs | 34 ++ gems/logger/_reviewers.yaml | 2 + gems/pstore/.rubocop.yml | 19 + gems/pstore/0.2/pstore.rbs | 608 +++++++++++++++++++++++ gems/pstore/_reviewers.yaml | 2 + 14 files changed, 2117 insertions(+) create mode 100644 gems/benchmark/.rubocop.yml create mode 100644 gems/benchmark/0.5/benchmark.rbs create mode 100644 gems/benchmark/_reviewers.yaml create mode 100644 gems/logger/.rubocop.yml create mode 100644 gems/logger/1.7/formatter.rbs create mode 100644 gems/logger/1.7/log_device.rbs create mode 100644 gems/logger/1.7/logger.rbs create mode 100644 gems/logger/1.7/manifest.yaml create mode 100644 gems/logger/1.7/period.rbs create mode 100644 gems/logger/1.7/severity.rbs create mode 100644 gems/logger/_reviewers.yaml create mode 100644 gems/pstore/.rubocop.yml create mode 100644 gems/pstore/0.2/pstore.rbs create mode 100644 gems/pstore/_reviewers.yaml diff --git a/gems/benchmark/.rubocop.yml b/gems/benchmark/.rubocop.yml new file mode 100644 index 000000000..7d0be8ff2 --- /dev/null +++ b/gems/benchmark/.rubocop.yml @@ -0,0 +1,19 @@ +# This configuration inherits from /.rubocop.yml. +# You can configure RBS style of this gem. +# This file is used on CI. It is configured to automatically +# make rubocop suggestions on pull requests for this gem. +# If you do not like the style enforcement, you should remove this file. +inherit_from: ../../.rubocop.yml + +## +# If you want to customize the style, please consult with the gem reviewers. +# You can see the list of cops at https://github.com/ksss/rubocop-on-rbs/blob/main/docs/modules/ROOT/pages/cops.adoc + +RBS/Layout: + Enabled: true + +RBS/Lint: + Enabled: true + +RBS/Style: + Enabled: true diff --git a/gems/benchmark/0.5/benchmark.rbs b/gems/benchmark/0.5/benchmark.rbs new file mode 100644 index 000000000..fdb43931b --- /dev/null +++ b/gems/benchmark/0.5/benchmark.rbs @@ -0,0 +1,452 @@ +# +# The Benchmark module provides methods to measure and report the time used to +# execute Ruby code. +# +# * Measure the time to construct the string given by the expression +# `"a"*1_000_000_000`: +# +# require 'benchmark' +# +# puts Benchmark.measure { "a"*1_000_000_000 } +# +# On my machine (OSX 10.8.3 on i5 1.7 GHz) this generates: +# +# 0.350000 0.400000 0.750000 ( 0.835234) +# +# This report shows the user CPU time, system CPU time, the sum of the user +# and system CPU times, and the elapsed real time. The unit of time is +# seconds. +# +# * Do some experiments sequentially using the #bm method: +# +# require 'benchmark' +# +# n = 5000000 +# Benchmark.bm do |x| +# x.report { for i in 1..n; a = "1"; end } +# x.report { n.times do ; a = "1"; end } +# x.report { 1.upto(n) do ; a = "1"; end } +# end +# +# The result: +# +# user system total real +# 1.010000 0.000000 1.010000 ( 1.014479) +# 1.000000 0.000000 1.000000 ( 0.998261) +# 0.980000 0.000000 0.980000 ( 0.981335) +# +# * Continuing the previous example, put a label in each report: +# +# require 'benchmark' +# +# n = 5000000 +# Benchmark.bm(7) do |x| +# x.report("for:") { for i in 1..n; a = "1"; end } +# x.report("times:") { n.times do ; a = "1"; end } +# x.report("upto:") { 1.upto(n) do ; a = "1"; end } +# end +# +# The result: +# +# user system total real +# for: 1.010000 0.000000 1.010000 ( 1.015688) +# times: 1.000000 0.000000 1.000000 ( 1.003611) +# upto: 1.030000 0.000000 1.030000 ( 1.028098) +# +# * The times for some benchmarks depend on the order in which items are run. +# These differences are due to the cost of memory allocation and garbage +# collection. To avoid these discrepancies, the #bmbm method is provided. +# For example, to compare ways to sort an array of floats: +# +# require 'benchmark' +# +# array = (1..1000000).map { rand } +# +# Benchmark.bmbm do |x| +# x.report("sort!") { array.dup.sort! } +# x.report("sort") { array.dup.sort } +# end +# +# The result: +# +# Rehearsal ----------------------------------------- +# sort! 1.490000 0.010000 1.500000 ( 1.490520) +# sort 1.460000 0.000000 1.460000 ( 1.463025) +# -------------------------------- total: 2.960000sec +# +# user system total real +# sort! 1.460000 0.000000 1.460000 ( 1.460465) +# sort 1.450000 0.010000 1.460000 ( 1.448327) +# +# * Report statistics of sequential experiments with unique labels, using the +# #benchmark method: +# +# require 'benchmark' +# include Benchmark # we need the CAPTION and FORMAT constants +# +# n = 5000000 +# Benchmark.benchmark(CAPTION, 7, FORMAT, ">total:", ">avg:") do |x| +# tf = x.report("for:") { for i in 1..n; a = "1"; end } +# tt = x.report("times:") { n.times do ; a = "1"; end } +# tu = x.report("upto:") { 1.upto(n) do ; a = "1"; end } +# [tf+tt+tu, (tf+tt+tu)/3] +# end +# +# The result: +# +# user system total real +# for: 0.950000 0.000000 0.950000 ( 0.952039) +# times: 0.980000 0.000000 0.980000 ( 0.984938) +# upto: 0.950000 0.000000 0.950000 ( 0.946787) +# >total: 2.880000 0.000000 2.880000 ( 2.883764) +# >avg: 0.960000 0.000000 0.960000 ( 0.961255) +# +module Benchmark + # + # Invokes the block with a Benchmark::Report object, which may be used to + # collect and report on the results of individual benchmark tests. Reserves + # `label_width` leading spaces for labels on each line. Prints `caption` at the + # top of the report, and uses `format` to format each line. (Note: `caption` + # must contain a terminating newline character, see the default + # Benchmark::Tms::CAPTION for an example.) + # + # Returns an array of Benchmark::Tms objects. + # + # If the block returns an array of Benchmark::Tms objects, these will be used to + # format additional lines of output. If `labels` parameter are given, these are + # used to label these extra lines. + # + # *Note*: Other methods provide a simpler interface to this one, and are + # suitable for nearly all benchmarking requirements. See the examples in + # Benchmark, and the #bm and #bmbm methods. + # + # Example: + # + # require 'benchmark' + # include Benchmark # we need the CAPTION and FORMAT constants + # + # n = 5000000 + # Benchmark.benchmark(CAPTION, 7, FORMAT, ">total:", ">avg:") do |x| + # tf = x.report("for:") { for i in 1..n; a = "1"; end } + # tt = x.report("times:") { n.times do ; a = "1"; end } + # tu = x.report("upto:") { 1.upto(n) do ; a = "1"; end } + # [tf+tt+tu, (tf+tt+tu)/3] + # end + # + # Generates: + # + # user system total real + # for: 0.970000 0.000000 0.970000 ( 0.970493) + # times: 0.990000 0.000000 0.990000 ( 0.989542) + # upto: 0.970000 0.000000 0.970000 ( 0.972854) + # >total: 2.930000 0.000000 2.930000 ( 2.932889) + # >avg: 0.976667 0.000000 0.976667 ( 0.977630) + # + def self?.benchmark: (String caption, ?Integer? label_width, ?String? format, *String labels) { (Report report) -> (Array[Tms] | untyped) } -> Array[Tms] + + # + # A simple interface to the #benchmark method, #bm generates sequential reports + # with labels. `label_width` and `labels` parameters have the same meaning as + # for #benchmark. + # + # require 'benchmark' + # + # n = 5000000 + # Benchmark.bm(7) do |x| + # x.report("for:") { for i in 1..n; a = "1"; end } + # x.report("times:") { n.times do ; a = "1"; end } + # x.report("upto:") { 1.upto(n) do ; a = "1"; end } + # end + # + # Generates: + # + # user system total real + # for: 0.960000 0.000000 0.960000 ( 0.957966) + # times: 0.960000 0.000000 0.960000 ( 0.960423) + # upto: 0.950000 0.000000 0.950000 ( 0.954864) + # + def self?.bm: (?Integer label_width, *String labels) { (Report report) -> void } -> Array[Tms] + + # + # Sometimes benchmark results are skewed because code executed earlier + # encounters different garbage collection overheads than that run later. #bmbm + # attempts to minimize this effect by running the tests twice, the first time as + # a rehearsal in order to get the runtime environment stable, the second time + # for real. GC.start is executed before the start of each of the real timings; + # the cost of this is not included in the timings. In reality, though, there's + # only so much that #bmbm can do, and the results are not guaranteed to be + # isolated from garbage collection and other effects. + # + # Because #bmbm takes two passes through the tests, it can calculate the + # required label width. + # + # require 'benchmark' + # + # array = (1..1000000).map { rand } + # + # Benchmark.bmbm do |x| + # x.report("sort!") { array.dup.sort! } + # x.report("sort") { array.dup.sort } + # end + # + # Generates: + # + # Rehearsal ----------------------------------------- + # sort! 1.440000 0.010000 1.450000 ( 1.446833) + # sort 1.440000 0.000000 1.440000 ( 1.448257) + # -------------------------------- total: 2.890000sec + # + # user system total real + # sort! 1.460000 0.000000 1.460000 ( 1.458065) + # sort 1.450000 0.000000 1.450000 ( 1.455963) + # + # #bmbm yields a Benchmark::Job object and returns an array of Benchmark::Tms + # objects. + # + def self?.bmbm: (?Integer width) { (Job job) -> void } -> Array[Tms] + + # + # Returns the time used to execute the given block as a Benchmark::Tms object. + # Takes `label` option. + # + # require 'benchmark' + # + # n = 1000000 + # + # time = Benchmark.measure do + # n.times { a = "1" } + # end + # puts time + # + # Generates: + # + # 0.220000 0.000000 0.220000 ( 0.227313) + # + def self?.measure: (?String label) { () -> void } -> Tms + + # + # Returns the elapsed real time used to execute the given block. The unit of + # time is seconds. + # + # Benchmark.realtime { "a" * 1_000_000_000 } + # #=> 0.5098029999935534 + # + def self?.realtime: () { () -> void } -> Float + + BENCHMARK_VERSION: String + + # + # The default caption string (heading above the output times). + # + CAPTION: String + + # + # The default format string used to display times. See also + # Benchmark::Tms#format. + # + FORMAT: String + + class Job + # Prints the `label` and measured time for the block, + # formatted by `format`. See Tms#format for the + # formatting rules. + def item: (?String label) { () -> void } -> self + + # An array of 2-element arrays, consisting of label and block pairs. + def list: () -> Array[untyped] + + alias report item + + # Length of the widest label in the #list. + def width: () -> Integer + end + + class Report + # Prints the `label` and measured time for the block, + # formatted by `format`. See Tms#format for the + # formatting rules. + def item: (?String label, *untyped format) { () -> void } -> Tms + + # An array of Benchmark::Tms objects representing each item. + def list: () -> Array[Tms] + + alias report item + end + + # + # A data object, representing the times associated with a benchmark measurement. + # + class Tms + # + # Returns a new Tms object obtained by memberwise multiplication of the + # individual times for this Tms object by `x`. + # + def *: (untyped x) -> untyped + + # + # Returns a new Tms object obtained by memberwise summation of the individual + # times for this Tms object with those of the `other` Tms object. This method + # and #/() are useful for taking statistics. + # + def +: (untyped other) -> untyped + + # + # Returns a new Tms object obtained by memberwise subtraction of the individual + # times for the `other` Tms object from those of this Tms object. + # + def -: (untyped other) -> untyped + + # + # Returns a new Tms object obtained by memberwise division of the individual + # times for this Tms object by `x`. This method and #+() are useful for taking + # statistics. + # + def /: (untyped x) -> untyped + + # + # Returns a new Tms object whose times are the sum of the times for this Tms + # object, plus the time required to execute the code block (`blk`). + # + def add: () { (*untyped) -> untyped } -> untyped + + # + # An in-place version of #add. Changes the times of this Tms object by making it + # the sum of the times for this Tms object, plus the time required to execute + # the code block (`blk`). + # + def add!: () { (*untyped) -> untyped } -> untyped + + # + # System CPU time of children + # + def cstime: () -> Float + + # + # User CPU time of children + # + def cutime: () -> Float + + # + # Returns the contents of this Tms object as a formatted string, according to a + # `format` string like that passed to Kernel.format. In addition, #format + # accepts the following extensions: + # + # `%u` + # : Replaced by the user CPU time, as reported by Tms#utime. + # + # `%y` + # : Replaced by the system CPU time, as reported by #stime (Mnemonic: y of + # "s*y*stem") + # + # `%U` + # : Replaced by the children's user CPU time, as reported by Tms#cutime + # + # `%Y` + # : Replaced by the children's system CPU time, as reported by Tms#cstime + # + # `%t` + # : Replaced by the total CPU time, as reported by Tms#total + # + # `%r` + # : Replaced by the elapsed real time, as reported by Tms#real + # + # `%n` + # : Replaced by the label string, as reported by Tms#label (Mnemonic: n of + # "*n*ame") + # + # + # If `format` is not given, FORMAT is used as default value, detailing the user, + # system and real elapsed time. + # + def format: (?String format, *untyped args) -> String + + # + # Label + # + def label: () -> String + + # + # Elapsed real time + # + def real: () -> Float + + # + # System CPU time + # + def stime: () -> Float + + # + # Returns a new 6-element array, consisting of the label, user CPU time, system + # CPU time, children's user CPU time, children's system CPU time and elapsed + # real time. + # + def to_a: () -> untyped + + # + # Same as #format. + # + def to_s: () -> String + + # + # Total time, that is `utime` + `stime` + `cutime` + `cstime` + # + def total: () -> Float + + # + # User CPU time + # + def utime: () -> Float + + # + # Default caption, see also Benchmark::CAPTION + # + CAPTION: String + + # + # Default format string, see also Benchmark::FORMAT + # + FORMAT: String + end +end diff --git a/gems/benchmark/_reviewers.yaml b/gems/benchmark/_reviewers.yaml new file mode 100644 index 000000000..0b4d3b7e8 --- /dev/null +++ b/gems/benchmark/_reviewers.yaml @@ -0,0 +1,2 @@ +reviewers: + - ksss diff --git a/gems/logger/.rubocop.yml b/gems/logger/.rubocop.yml new file mode 100644 index 000000000..7d0be8ff2 --- /dev/null +++ b/gems/logger/.rubocop.yml @@ -0,0 +1,19 @@ +# This configuration inherits from /.rubocop.yml. +# You can configure RBS style of this gem. +# This file is used on CI. It is configured to automatically +# make rubocop suggestions on pull requests for this gem. +# If you do not like the style enforcement, you should remove this file. +inherit_from: ../../.rubocop.yml + +## +# If you want to customize the style, please consult with the gem reviewers. +# You can see the list of cops at https://github.com/ksss/rubocop-on-rbs/blob/main/docs/modules/ROOT/pages/cops.adoc + +RBS/Layout: + Enabled: true + +RBS/Lint: + Enabled: true + +RBS/Style: + Enabled: true diff --git a/gems/logger/1.7/formatter.rbs b/gems/logger/1.7/formatter.rbs new file mode 100644 index 000000000..294f4c40b --- /dev/null +++ b/gems/logger/1.7/formatter.rbs @@ -0,0 +1,45 @@ +%a{annotate:rdoc:skip} +class Logger + # + # Default formatter for log messages. + # + class Formatter + attr_accessor datetime_format: String? + + # + # + def call: (String severity, Time time, untyped progname, untyped msg) -> String + + private + + # + # + def format_datetime: (Time time) -> untyped + + # + # + def initialize: () -> void + + # + # + def msg2str: (String | Exception | untyped msg) -> String + end + + interface _Formatter + def call: (String severity, Time time, untyped progname, untyped msg) -> _ToS + end +end + +Logger::Formatter::Format: String diff --git a/gems/logger/1.7/log_device.rbs b/gems/logger/1.7/log_device.rbs new file mode 100644 index 000000000..20e33ca25 --- /dev/null +++ b/gems/logger/1.7/log_device.rbs @@ -0,0 +1,100 @@ +%a{annotate:rdoc:skip} +class Logger + # + # Device used for logging messages. + # + class LogDevice + include MonitorMixin + + include Period + + attr_reader dev: _WriteCloser + attr_reader filename: String? + + # + # + def close: () -> nil + + # + # + def reopen: (?logdev log) -> self + + # + # + def write: (untyped message) -> untyped + + private + + # + # + def add_log_header: (IO file) -> untyped + + # + # + def check_shift_log: () -> untyped + + # + # + def create_logfile: (String filename) -> File + + # + # + def initialize: (?untyped logdev, ?binmode: boolish, ?shift_period_suffix: String, ?shift_size: Integer, ?shift_age: Numeric | String) -> void + + # + # + def lock_shift_log: () { () -> untyped } -> untyped + + # + # + def open_logfile: (String filename) -> File + + # + # + def set_dev: (logdev log) -> untyped + + # + # + def shift_log_age: () -> true + + # + # + def shift_log_period: (Time period_end) -> true + end +end diff --git a/gems/logger/1.7/logger.rbs b/gems/logger/1.7/logger.rbs new file mode 100644 index 000000000..82228660b --- /dev/null +++ b/gems/logger/1.7/logger.rbs @@ -0,0 +1,796 @@ +# +# Class Logger provides a simple but sophisticated logging utility that you can +# use to create one or more [event +# logs](https://en.wikipedia.org/wiki/Logging_(software)#Event_logs) for your +# program. Each such log contains a chronological sequence of entries that +# provides a record of the program's activities. +# +# ## About the Examples +# +# All examples on this page assume that Logger has been required: +# +# require 'logger' +# +# ## Synopsis +# +# Create a log with Logger.new: +# +# # Single log file. +# logger = Logger.new('t.log') +# # Size-based rotated logging: 3 10-megabyte files. +# logger = Logger.new('t.log', 3, 10485760) +# # Period-based rotated logging: daily (also allowed: 'weekly', 'monthly'). +# logger = Logger.new('t.log', 'daily') +# # Log to an IO stream. +# logger = Logger.new($stdout) +# +# Add entries (level, message) with Logger#add: +# +# logger.add(Logger::DEBUG, 'Maximal debugging info') +# logger.add(Logger::INFO, 'Non-error information') +# logger.add(Logger::WARN, 'Non-error warning') +# logger.add(Logger::ERROR, 'Non-fatal error') +# logger.add(Logger::FATAL, 'Fatal error') +# logger.add(Logger::UNKNOWN, 'Most severe') +# +# Close the log with Logger#close: +# +# logger.close +# +# ## Entries +# +# You can add entries with method Logger#add: +# +# logger.add(Logger::DEBUG, 'Maximal debugging info') +# logger.add(Logger::INFO, 'Non-error information') +# logger.add(Logger::WARN, 'Non-error warning') +# logger.add(Logger::ERROR, 'Non-fatal error') +# logger.add(Logger::FATAL, 'Fatal error') +# logger.add(Logger::UNKNOWN, 'Most severe') +# +# These shorthand methods also add entries: +# +# logger.debug('Maximal debugging info') +# logger.info('Non-error information') +# logger.warn('Non-error warning') +# logger.error('Non-fatal error') +# logger.fatal('Fatal error') +# logger.unknown('Most severe') +# +# When you call any of these methods, the entry may or may not be written to the +# log, depending on the entry's severity and on the log level; see [Log +# Level](rdoc-ref:Logger@Log+Level) +# +# An entry always has: +# +# * A severity (the required argument to #add). +# * An automatically created timestamp. +# +# And may also have: +# +# * A message. +# * A program name. +# +# Example: +# +# logger = Logger.new($stdout) +# logger.add(Logger::INFO, 'My message.', 'mung') +# # => I, [2022-05-07T17:21:46.536234 #20536] INFO -- mung: My message. +# +# The default format for an entry is: +# +# "%s, [%s #%d] %5s -- %s: %s\n" +# +# where the values to be formatted are: +# +# * Severity (one letter). +# * Timestamp. +# * Process id. +# * Severity (word). +# * Program name. +# * Message. +# +# You can use a different entry format by: +# +# * Setting a custom format proc (affects following entries); see +# [formatter=](Logger.html#attribute-i-formatter). +# * Calling any of the methods above with a block (affects only the one +# entry). Doing so can have two benefits: +# +# * Context: the block can evaluate the entire program context and create +# a context-dependent message. +# * Performance: the block is not evaluated unless the log level permits +# the entry actually to be written: +# +# logger.error { my_slow_message_generator } +# +# Contrast this with the string form, where the string is always +# evaluated, regardless of the log level: +# +# logger.error("#{my_slow_message_generator}") +# +# ### Severity +# +# The severity of a log entry has two effects: +# +# * Determines whether the entry is selected for inclusion in the log; see +# [Log Level](rdoc-ref:Logger@Log+Level). +# * Indicates to any log reader (whether a person or a program) the relative +# importance of the entry. +# +# ### Timestamp +# +# The timestamp for a log entry is generated automatically when the entry is +# created. +# +# The logged timestamp is formatted by method +# [Time#strftime](rdoc-ref:Time#strftime) using this format string: +# +# '%Y-%m-%dT%H:%M:%S.%6N' +# +# Example: +# +# logger = Logger.new($stdout) +# logger.add(Logger::INFO) +# # => I, [2022-05-07T17:04:32.318331 #20536] INFO -- : nil +# +# You can set a different format using method #datetime_format=. +# +# ### Message +# +# The message is an optional argument to an entry method: +# +# logger = Logger.new($stdout) +# logger.add(Logger::INFO, 'My message') +# # => I, [2022-05-07T18:15:37.647581 #20536] INFO -- : My message +# +# For the default entry formatter, `Logger::Formatter`, the message object may +# be: +# +# * A string: used as-is. +# * An Exception: `message.message` is used. +# * Anything else: `message.inspect` is used. +# +# **Note**: Logger::Formatter does not escape or sanitize the message passed to +# it. Developers should be aware that malicious data (user input) may be in the +# message, and should explicitly escape untrusted data. +# +# You can use a custom formatter to escape message data; see the example at +# [formatter=](Logger.html#attribute-i-formatter). +# +# ### Program Name +# +# The program name is an optional argument to an entry method: +# +# logger = Logger.new($stdout) +# logger.add(Logger::INFO, 'My message', 'mung') +# # => I, [2022-05-07T18:17:38.084716 #20536] INFO -- mung: My message +# +# The default program name for a new logger may be set in the call to Logger.new +# via optional keyword argument `progname`: +# +# logger = Logger.new('t.log', progname: 'mung') +# +# The default program name for an existing logger may be set by a call to method +# #progname=: +# +# logger.progname = 'mung' +# +# The current program name may be retrieved with method +# [progname](Logger.html#attribute-i-progname): +# +# logger.progname # => "mung" +# +# ## Log Level +# +# The log level setting determines whether an entry is actually written to the +# log, based on the entry's severity. +# +# These are the defined severities (least severe to most severe): +# +# logger = Logger.new($stdout) +# logger.add(Logger::DEBUG, 'Maximal debugging info') +# # => D, [2022-05-07T17:57:41.776220 #20536] DEBUG -- : Maximal debugging info +# logger.add(Logger::INFO, 'Non-error information') +# # => I, [2022-05-07T17:59:14.349167 #20536] INFO -- : Non-error information +# logger.add(Logger::WARN, 'Non-error warning') +# # => W, [2022-05-07T18:00:45.337538 #20536] WARN -- : Non-error warning +# logger.add(Logger::ERROR, 'Non-fatal error') +# # => E, [2022-05-07T18:02:41.592912 #20536] ERROR -- : Non-fatal error +# logger.add(Logger::FATAL, 'Fatal error') +# # => F, [2022-05-07T18:05:24.703931 #20536] FATAL -- : Fatal error +# logger.add(Logger::UNKNOWN, 'Most severe') +# # => A, [2022-05-07T18:07:54.657491 #20536] ANY -- : Most severe +# +# The default initial level setting is Logger::DEBUG, the lowest level, which +# means that all entries are to be written, regardless of severity: +# +# logger = Logger.new($stdout) +# logger.level # => 0 +# logger.add(0, "My message") +# # => D, [2022-05-11T15:10:59.773668 #20536] DEBUG -- : My message +# +# You can specify a different setting in a new logger using keyword argument +# `level` with an appropriate value: +# +# logger = Logger.new($stdout, level: Logger::ERROR) +# logger = Logger.new($stdout, level: 'error') +# logger = Logger.new($stdout, level: :error) +# logger.level # => 3 +# +# With this level, entries with severity Logger::ERROR and higher are written, +# while those with lower severities are not written: +# +# logger = Logger.new($stdout, level: Logger::ERROR) +# logger.add(3) +# # => E, [2022-05-11T15:17:20.933362 #20536] ERROR -- : nil +# logger.add(2) # Silent. +# +# You can set the log level for an existing logger with method #level=: +# +# logger.level = Logger::ERROR +# +# These shorthand methods also set the level: +# +# logger.debug! # => 0 +# logger.info! # => 1 +# logger.warn! # => 2 +# logger.error! # => 3 +# logger.fatal! # => 4 +# +# You can retrieve the log level with method #level. +# +# logger.level = Logger::ERROR +# logger.level # => 3 +# +# These methods return whether a given level is to be written: +# +# logger.level = Logger::ERROR +# logger.debug? # => false +# logger.info? # => false +# logger.warn? # => false +# logger.error? # => true +# logger.fatal? # => true +# +# ## Log File Rotation +# +# By default, a log file is a single file that grows indefinitely (until +# explicitly closed); there is no file rotation. +# +# To keep log files to a manageable size, you can use *log* *file* *rotation*, +# which uses multiple log files: +# +# * Each log file has entries for a non-overlapping time interval. +# * Only the most recent log file is open and active; the others are closed +# and inactive. +# +# ### Size-Based Rotation +# +# For size-based log file rotation, call Logger.new with: +# +# * Argument `logdev` as a file path. +# * Argument `shift_age` with a positive integer: the number of log files to +# be in the rotation. +# * Argument `shift_size` as a positive integer: the maximum size (in bytes) +# of each log file; defaults to 1048576 (1 megabyte). +# +# Examples: +# +# logger = Logger.new('t.log', 3) # Three 1-megabyte files. +# logger = Logger.new('t.log', 5, 10485760) # Five 10-megabyte files. +# +# For these examples, suppose: +# +# logger = Logger.new('t.log', 3) +# +# Logging begins in the new log file, `t.log`; the log file is "full" and ready +# for rotation when a new entry would cause its size to exceed `shift_size`. +# +# The first time `t.log` is full: +# +# * `t.log` is closed and renamed to `t.log.0`. +# * A new file `t.log` is opened. +# +# The second time `t.log` is full: +# +# * +t.log.0 is renamed as `t.log.1`. +# * `t.log` is closed and renamed to `t.log.0`. +# * A new file `t.log` is opened. +# +# Each subsequent time that `t.log` is full, the log files are rotated: +# +# * `t.log.1` is removed. +# * +t.log.0 is renamed as `t.log.1`. +# * `t.log` is closed and renamed to `t.log.0`. +# * A new file `t.log` is opened. +# +# ### Periodic Rotation +# +# For periodic rotation, call Logger.new with: +# +# * Argument `logdev` as a file path. +# * Argument `shift_age` as a string period indicator. +# +# Examples: +# +# logger = Logger.new('t.log', 'daily') # Rotate log files daily. +# logger = Logger.new('t.log', 'weekly') # Rotate log files weekly. +# logger = Logger.new('t.log', 'monthly') # Rotate log files monthly. +# +# Example: +# +# logger = Logger.new('t.log', 'daily') +# +# When the given period expires: +# +# * The base log file, `t.log` is closed and renamed with a date-based suffix +# such as `t.log.20220509`. +# * A new log file `t.log` is opened. +# * Nothing is removed. +# +# The default format for the suffix is `'%Y%m%d'`, which produces a suffix +# similar to the one above. You can set a different format using create-time +# option `shift_period_suffix`; see details and suggestions at +# [Time#strftime](rdoc-ref:Time#strftime). +# +class Logger + interface _WriteCloser + def write: (_ToS) -> untyped + + def close: () -> untyped + end + type logdev = _WriteCloser | String + + include Logger::Severity + + # + # Writes the given `msg` to the log with no formatting; returns the number of + # characters written, or `nil` if no log device exists: + # + # logger = Logger.new($stdout) + # logger << 'My message.' # => 10 + # + # Output: + # + # My message. + # + def <<: (untyped msg) -> (untyped | nil) + + # + # Creates a log entry, which may or may not be written to the log, depending on + # the entry's severity and on the log level. See [Log + # Level](rdoc-ref:Logger@Log+Level) and [Entries](rdoc-ref:Logger@Entries) for + # details. + # + # Examples: + # + # logger = Logger.new($stdout, progname: 'mung') + # logger.add(Logger::INFO) + # logger.add(Logger::ERROR, 'No good') + # logger.add(Logger::ERROR, 'No good', 'gnum') + # + # Output: + # + # I, [2022-05-12T16:25:31.469726 #36328] INFO -- mung: mung + # E, [2022-05-12T16:25:55.349414 #36328] ERROR -- mung: No good + # E, [2022-05-12T16:26:35.841134 #36328] ERROR -- gnum: No good + # + # These convenience methods have implicit severity: + # + # * #debug. + # * #info. + # * #warn. + # * #error. + # * #fatal. + # * #unknown. + # + def add: (Integer severity, ?untyped message, ?untyped progname) ?{ () -> untyped } -> true + + # + # Closes the logger; returns `nil`: + # + # logger = Logger.new('t.log') + # logger.close # => nil + # logger.info('foo') # Prints "log writing failed. closed stream" + # + # Related: Logger#reopen. + # + def close: () -> untyped + + # + # Returns the date-time format; see #datetime_format=. + # + def datetime_format: () -> String? + + # + # Sets the date-time format. + # + # Argument `datetime_format` should be either of these: + # + # * A string suitable for use as a format for method + # [Time#strftime](rdoc-ref:Time#strftime). + # * `nil`: the logger uses `'%Y-%m-%dT%H:%M:%S.%6N'`. + # + def datetime_format=: (String datetime_format) -> String + | (nil datetime_format) -> nil + + # + # Equivalent to calling #add with severity `Logger::DEBUG`. + # + def debug: (?untyped progname) ?{ () -> untyped } -> true + + # + # Sets the log level to Logger::DEBUG. See [Log + # Level](rdoc-ref:Logger@Log+Level). + # + def debug!: () -> Integer + + # + # Returns `true` if the log level allows entries with severity Logger::DEBUG to + # be written, `false` otherwise. See [Log Level](rdoc-ref:Logger@Log+Level). + # + def debug?: () -> bool + + # + # Equivalent to calling #add with severity `Logger::ERROR`. + # + def error: (?untyped progname) ?{ () -> untyped } -> true + + # + # Sets the log level to Logger::ERROR. See [Log + # Level](rdoc-ref:Logger@Log+Level). + # + def error!: () -> Integer + + # + # Returns `true` if the log level allows entries with severity Logger::ERROR to + # be written, `false` otherwise. See [Log Level](rdoc-ref:Logger@Log+Level). + # + def error?: () -> bool + + # + # Equivalent to calling #add with severity `Logger::FATAL`. + # + def fatal: (?untyped progname) ?{ () -> untyped } -> true + + # + # Sets the log level to Logger::FATAL. See [Log + # Level](rdoc-ref:Logger@Log+Level). + # + def fatal!: () -> Integer + + # + # Returns `true` if the log level allows entries with severity Logger::FATAL to + # be written, `false` otherwise. See [Log Level](rdoc-ref:Logger@Log+Level). + # + def fatal?: () -> bool + + # + # Sets or retrieves the logger entry formatter proc. + # + # When `formatter` is `nil`, the logger uses Logger::Formatter. + # + # When `formatter` is a proc, a new entry is formatted by the proc, which is + # called with four arguments: + # + # * `severity`: The severity of the entry. + # * `time`: A Time object representing the entry's timestamp. + # * `progname`: The program name for the entry. + # * `msg`: The message for the entry (string or string-convertible object). + # + # The proc should return a string containing the formatted entry. + # + # This custom formatter uses [String#dump](rdoc-ref:String#dump) to escape the + # message string: + # + # logger = Logger.new($stdout, progname: 'mung') + # original_formatter = logger.formatter || Logger::Formatter.new + # logger.formatter = proc { |severity, time, progname, msg| + # original_formatter.call(severity, time, progname, msg.dump) + # } + # logger.add(Logger::INFO, "hello \n ''") + # logger.add(Logger::INFO, "\f\x00\xff\\\"") + # + # Output: + # + # I, [2022-05-13T13:16:29.637488 #8492] INFO -- mung: "hello \n ''" + # I, [2022-05-13T13:16:29.637610 #8492] INFO -- mung: "\f\x00\xFF\\\"" + # + def formatter: () -> (_Formatter | nil) + + # + # Sets or retrieves the logger entry formatter proc. + # + # When `formatter` is `nil`, the logger uses Logger::Formatter. + # + # When `formatter` is a proc, a new entry is formatted by the proc, which is + # called with four arguments: + # + # * `severity`: The severity of the entry. + # * `time`: A Time object representing the entry's timestamp. + # * `progname`: The program name for the entry. + # * `msg`: The message for the entry (string or string-convertible object). + # + # The proc should return a string containing the formatted entry. + # + # This custom formatter uses [String#dump](rdoc-ref:String#dump) to escape the + # message string: + # + # logger = Logger.new($stdout, progname: 'mung') + # original_formatter = logger.formatter || Logger::Formatter.new + # logger.formatter = proc { |severity, time, progname, msg| + # original_formatter.call(severity, time, progname, msg.dump) + # } + # logger.add(Logger::INFO, "hello \n ''") + # logger.add(Logger::INFO, "\f\x00\xff\\\"") + # + # Output: + # + # I, [2022-05-13T13:16:29.637488 #8492] INFO -- mung: "hello \n ''" + # I, [2022-05-13T13:16:29.637610 #8492] INFO -- mung: "\f\x00\xFF\\\"" + # + def formatter=: (_Formatter) -> _Formatter + | (nil) -> nil + + # + # Equivalent to calling #add with severity `Logger::INFO`. + # + def info: (?untyped progname) ?{ () -> untyped } -> true + + # + # Sets the log level to Logger::INFO. See [Log + # Level](rdoc-ref:Logger@Log+Level). + # + def info!: () -> Integer + + # + # Returns `true` if the log level allows entries with severity Logger::INFO to + # be written, `false` otherwise. See [Log Level](rdoc-ref:Logger@Log+Level). + # + def info?: () -> bool + + # + # Logging severity threshold (e.g. `Logger::INFO`). + # + def level: () -> Integer + + # + # Sets the log level; returns `severity`. See [Log + # Level](rdoc-ref:Logger@Log+Level). + # + # Argument `severity` may be an integer, a string, or a symbol: + # + # logger.level = Logger::ERROR # => 3 + # logger.level = 3 # => 3 + # logger.level = 'error' # => "error" + # logger.level = :error # => :error + # + # Logger#sev_threshold= is an alias for Logger#level=. + # + def level=: (Integer | interned severity) -> Integer + + # + # + alias log add + + # + # Program name to include in log messages. + # + def progname: () -> untyped + + # + # Program name to include in log messages. + # + def progname=: (untyped) -> untyped + + # + # Sets the logger's output stream: + # + # * If `logdev` is `nil`, reopens the current output stream. + # * If `logdev` is a filepath, opens the indicated file for append. + # * If `logdev` is an IO stream (usually `$stdout`, `$stderr`, or an open File + # object), opens the stream for append. + # + # Example: + # + # logger = Logger.new('t.log') + # logger.add(Logger::ERROR, 'one') + # logger.close + # logger.add(Logger::ERROR, 'two') # Prints 'log writing failed. closed stream' + # logger.reopen + # logger.add(Logger::ERROR, 'three') + # logger.close + # File.readlines('t.log') + # # => + # # ["# Logfile created on 2022-05-12 14:21:19 -0500 by logger.rb/v1.5.0\n", + # # "E, [2022-05-12T14:21:27.596726 #22428] ERROR -- : one\n", + # # "E, [2022-05-12T14:23:05.847241 #22428] ERROR -- : three\n"] + # + def reopen: () -> self + | (logdev?) -> self + + # + # + alias sev_threshold level + + # + # + alias sev_threshold= level= + + # + # Equivalent to calling #add with severity `Logger::UNKNOWN`. + # + def unknown: (?untyped progname) ?{ () -> untyped } -> true + + # + # Equivalent to calling #add with severity `Logger::WARN`. + # + def warn: (?untyped progname) ?{ () -> untyped } -> true + + # + # Sets the log level to Logger::WARN. See [Log + # Level](rdoc-ref:Logger@Log+Level). + # + def warn!: () -> Integer + + # + # Returns `true` if the log level allows entries with severity Logger::WARN to + # be written, `false` otherwise. See [Log Level](rdoc-ref:Logger@Log+Level). + # + def warn?: () -> bool + + private + + # + # + def format_message: (String severity, Time datetime, untyped progname, untyped msg) -> _ToS + + # + # + def format_severity: (Integer severity) -> String + + # + # With the single argument `logdev`, returns a new logger with all default + # options: + # + # Logger.new('t.log') # => # + # + # Argument `logdev` must be one of: + # + # * A string filepath: entries are to be written to the file at that path; if + # the file at that path exists, new entries are appended. + # * An IO stream (typically +$stdout+, +$stderr+. or an open file): entries + # are to be written to the given stream. + # * `nil` or `File::NULL`: no entries are to be written. + # + # Examples: + # + # Logger.new('t.log') + # Logger.new($stdout) + # + # The keyword options are: + # + # * `level`: sets the log level; default value is Logger::DEBUG. See [Log + # Level](rdoc-ref:Logger@Log+Level): + # + # Logger.new('t.log', level: Logger::ERROR) + # + # * `progname`: sets the default program name; default is `nil`. See [Program + # Name](rdoc-ref:Logger@Program+Name): + # + # Logger.new('t.log', progname: 'mung') + # + # * `formatter`: sets the entry formatter; default is `nil`. See + # [formatter=](Logger.html#attribute-i-formatter). + # * `datetime_format`: sets the format for entry timestamp; default is `nil`. + # See #datetime_format=. + # * `binmode`: sets whether the logger writes in binary mode; default is + # `false`. + # * `shift_period_suffix`: sets the format for the filename suffix for + # periodic log file rotation; default is `'%Y%m%d'`. See [Periodic + # Rotation](rdoc-ref:Logger@Periodic+Rotation). + # * `reraise_write_errors`: An array of exception classes, which will be + # reraised if there is an error when writing to the log device. The default + # is to swallow all exceptions raised. + # + def initialize: (logdev? logdev, ?Numeric | String shift_age, ?Integer shift_size, ?shift_period_suffix: String, ?binmode: boolish, ?datetime_format: String, ?formatter: _Formatter, ?progname: String, ?level: Integer | interned) -> void +end + +Logger::ProgName: String + +# +# Severity label for logging (max 5 chars). +# +Logger::SEV_LABEL: Array[String] + +Logger::VERSION: String diff --git a/gems/logger/1.7/manifest.yaml b/gems/logger/1.7/manifest.yaml new file mode 100644 index 000000000..9a570c44f --- /dev/null +++ b/gems/logger/1.7/manifest.yaml @@ -0,0 +1,2 @@ +dependencies: + - name: monitor diff --git a/gems/logger/1.7/period.rbs b/gems/logger/1.7/period.rbs new file mode 100644 index 000000000..bb06eed60 --- /dev/null +++ b/gems/logger/1.7/period.rbs @@ -0,0 +1,17 @@ +module Logger::Period + # + # + def self?.next_rotate_time: (Time now, String shift_age) -> untyped + + # + # + def self?.previous_period_end: (Time now, String shift_age) -> untyped + + SiD: Integer +end diff --git a/gems/logger/1.7/severity.rbs b/gems/logger/1.7/severity.rbs new file mode 100644 index 000000000..200a4768c --- /dev/null +++ b/gems/logger/1.7/severity.rbs @@ -0,0 +1,34 @@ +# +# Logging severity. +# +module Logger::Severity + # + # Low-level information, mostly for developers. + # + DEBUG: 0 + + # + # Generic (useful) information about system operation. + # + INFO: 1 + + # + # A warning. + # + WARN: 2 + + # + # A handleable error condition. + # + ERROR: 3 + + # + # An unhandleable error that results in a program crash. + # + FATAL: 4 + + # + # An unknown message that should always be logged. + # + UNKNOWN: 5 +end diff --git a/gems/logger/_reviewers.yaml b/gems/logger/_reviewers.yaml new file mode 100644 index 000000000..0b4d3b7e8 --- /dev/null +++ b/gems/logger/_reviewers.yaml @@ -0,0 +1,2 @@ +reviewers: + - ksss diff --git a/gems/pstore/.rubocop.yml b/gems/pstore/.rubocop.yml new file mode 100644 index 000000000..7d0be8ff2 --- /dev/null +++ b/gems/pstore/.rubocop.yml @@ -0,0 +1,19 @@ +# This configuration inherits from /.rubocop.yml. +# You can configure RBS style of this gem. +# This file is used on CI. It is configured to automatically +# make rubocop suggestions on pull requests for this gem. +# If you do not like the style enforcement, you should remove this file. +inherit_from: ../../.rubocop.yml + +## +# If you want to customize the style, please consult with the gem reviewers. +# You can see the list of cops at https://github.com/ksss/rubocop-on-rbs/blob/main/docs/modules/ROOT/pages/cops.adoc + +RBS/Layout: + Enabled: true + +RBS/Lint: + Enabled: true + +RBS/Style: + Enabled: true diff --git a/gems/pstore/0.2/pstore.rbs b/gems/pstore/0.2/pstore.rbs new file mode 100644 index 000000000..f5c9d1195 --- /dev/null +++ b/gems/pstore/0.2/pstore.rbs @@ -0,0 +1,608 @@ +# +# PStore implements a file based persistence mechanism based on a Hash. User +# code can store hierarchies of Ruby objects (values) into the data store by +# name (keys). An object hierarchy may be just a single object. User code may +# later read values back from the data store or even update data, as needed. +# +# The transactional behavior ensures that any changes succeed or fail together. +# This can be used to ensure that the data store is not left in a transitory +# state, where some values were updated but others were not. +# +# Behind the scenes, Ruby objects are stored to the data store file with +# Marshal. That carries the usual limitations. Proc objects cannot be +# marshalled, for example. +# +# There are three important concepts here (details at the links): +# +# * [Store](rdoc-ref:PStore@The+Store): a store is an instance of PStore. +# * [Entries](rdoc-ref:PStore@Entries): the store is hash-like; each entry is +# the key for a stored object. +# * [Transactions](rdoc-ref:PStore@Transactions): each transaction is a +# collection of prospective changes to the store; a transaction is defined +# in the block given with a call to PStore#transaction. +# +# ## About the Examples +# +# Examples on this page need a store that has known properties. They can get a +# new (and populated) store by calling thus: +# +# example_store do |store| +# # Example code using store goes here. +# end +# +# All we really need to know about `example_store` is that it yields a fresh +# store with a known population of entries; its implementation: +# +# require 'pstore' +# require 'tempfile' +# # Yield a pristine store for use in examples. +# def example_store +# # Create the store in a temporary file. +# Tempfile.create do |file| +# store = PStore.new(file) +# # Populate the store. +# store.transaction do +# store[:foo] = 0 +# store[:bar] = 1 +# store[:baz] = 2 +# end +# yield store +# end +# end +# +# ## The Store +# +# The contents of the store are maintained in a file whose path is specified +# when the store is created (see PStore.new). The objects are stored and +# retrieved using module Marshal, which means that certain objects cannot be +# added to the store; see [Marshal::dump](rdoc-ref:Marshal.dump). +# +# ## Entries +# +# A store may have any number of entries. Each entry has a key and a value, just +# as in a hash: +# +# * Key: as in a hash, the key can be (almost) any object; see [Hash +# Keys](rdoc-ref:Hash@Hash+Keys). You may find it convenient to keep it +# simple by using only symbols or strings as keys. +# * Value: the value may be any object that can be marshalled by Marshal (see +# [Marshal::dump](rdoc-ref:Marshal.dump)) and in fact may be a collection +# (e.g., an array, a hash, a set, a range, etc). That collection may in turn +# contain nested objects, including collections, to any depth; those objects +# must also be Marshal-able. See [Hierarchical +# Values](rdoc-ref:PStore@Hierarchical+Values). +# +# ## Transactions +# +# ### The Transaction Block +# +# The block given with a call to method #transaction# contains a *transaction*, +# which consists of calls to PStore methods that read from or write to the store +# (that is, all PStore methods except #transaction itself, #path, and +# Pstore.new): +# +# example_store do |store| +# store.transaction do +# store.keys # => [:foo, :bar, :baz] +# store[:bat] = 3 +# store.keys # => [:foo, :bar, :baz, :bat] +# end +# end +# +# Execution of the transaction is deferred until the block exits, and is +# executed *atomically* (all-or-nothing): either all transaction calls are +# executed, or none are. This maintains the integrity of the store. +# +# Other code in the block (including even calls to #path and PStore.new) is +# executed immediately, not deferred. +# +# The transaction block: +# +# * May not contain a nested call to #transaction. +# * Is the only context where methods that read from or write to the store are +# allowed. +# +# As seen above, changes in a transaction are made automatically when the block +# exits. The block may be exited early by calling method #commit or #abort. +# +# * Method #commit triggers the update to the store and exits the block: +# +# example_store do |store| +# store.transaction do +# store.keys # => [:foo, :bar, :baz] +# store[:bat] = 3 +# store.commit +# fail 'Cannot get here' +# end +# store.transaction do +# # Update was completed. +# store.keys # => [:foo, :bar, :baz, :bat] +# end +# end +# +# * Method #abort discards the update to the store and exits the block: +# +# example_store do |store| +# store.transaction do +# store.keys # => [:foo, :bar, :baz] +# store[:bat] = 3 +# store.abort +# fail 'Cannot get here' +# end +# store.transaction do +# # Update was not completed. +# store.keys # => [:foo, :bar, :baz] +# end +# end +# +# ### Read-Only Transactions +# +# By default, a transaction allows both reading from and writing to the store: +# +# store.transaction do +# # Read-write transaction. +# # Any code except a call to #transaction is allowed here. +# end +# +# If argument `read_only` is passed as `true`, only reading is allowed: +# +# store.transaction(true) do +# # Read-only transaction: +# # Calls to #transaction, #[]=, and #delete are not allowed here. +# end +# +# ## Hierarchical Values +# +# The value for an entry may be a simple object (as seen above). It may also be +# a hierarchy of objects nested to any depth: +# +# deep_store = PStore.new('deep.store') +# deep_store.transaction do +# array_of_hashes = [{}, {}, {}] +# deep_store[:array_of_hashes] = array_of_hashes +# deep_store[:array_of_hashes] # => [{}, {}, {}] +# hash_of_arrays = {foo: [], bar: [], baz: []} +# deep_store[:hash_of_arrays] = hash_of_arrays +# deep_store[:hash_of_arrays] # => {:foo=>[], :bar=>[], :baz=>[]} +# deep_store[:hash_of_arrays][:foo].push(:bat) +# deep_store[:hash_of_arrays] # => {:foo=>[:bat], :bar=>[], :baz=>[]} +# end +# +# And recall that you can use [dig methods](rdoc-ref:dig_methods.rdoc) in a +# returned hierarchy of objects. +# +# ## Working with the Store +# +# ### Creating a Store +# +# Use method PStore.new to create a store. The new store creates or opens its +# containing file: +# +# store = PStore.new('t.store') +# +# ### Modifying the Store +# +# Use method #[]= to update or create an entry: +# +# example_store do |store| +# store.transaction do +# store[:foo] = 1 # Update. +# store[:bam] = 1 # Create. +# end +# end +# +# Use method #delete to remove an entry: +# +# example_store do |store| +# store.transaction do +# store.delete(:foo) +# store[:foo] # => nil +# end +# end +# +# ### Retrieving Values +# +# Use method #fetch (allows default) or #[] (defaults to `nil`) to retrieve an +# entry: +# +# example_store do |store| +# store.transaction do +# store[:foo] # => 0 +# store[:nope] # => nil +# store.fetch(:baz) # => 2 +# store.fetch(:nope, nil) # => nil +# store.fetch(:nope) # Raises exception. +# end +# end +# +# ### Querying the Store +# +# Use method #key? to determine whether a given key exists: +# +# example_store do |store| +# store.transaction do +# store.key?(:foo) # => true +# end +# end +# +# Use method #keys to retrieve keys: +# +# example_store do |store| +# store.transaction do +# store.keys # => [:foo, :bar, :baz] +# end +# end +# +# Use method #path to retrieve the path to the store's underlying file; this +# method may be called from outside a transaction block: +# +# store = PStore.new('t.store') +# store.path # => "t.store" +# +# ## Transaction Safety +# +# For transaction safety, see: +# +# * Optional argument `thread_safe` at method PStore.new. +# * Attribute #ultra_safe. +# +# Needless to say, if you're storing valuable data with PStore, then you should +# backup the PStore file from time to time. +# +# ## An Example Store +# +# require "pstore" +# +# # A mock wiki object. +# class WikiPage +# +# attr_reader :page_name +# +# def initialize(page_name, author, contents) +# @page_name = page_name +# @revisions = Array.new +# add_revision(author, contents) +# end +# +# def add_revision(author, contents) +# @revisions << {created: Time.now, +# author: author, +# contents: contents} +# end +# +# def wiki_page_references +# [@page_name] + @revisions.last[:contents].scan(/\b(?:[A-Z]+[a-z]+){2,}/) +# end +# +# end +# +# # Create a new wiki page. +# home_page = WikiPage.new("HomePage", "James Edward Gray II", +# "A page about the JoysOfDocumentation..." ) +# +# wiki = PStore.new("wiki_pages.pstore") +# # Update page data and the index together, or not at all. +# wiki.transaction do +# # Store page. +# wiki[home_page.page_name] = home_page +# # Create page index. +# wiki[:wiki_index] ||= Array.new +# # Update wiki index. +# wiki[:wiki_index].push(*home_page.wiki_page_references) +# end +# +# # Read wiki data, setting argument read_only to true. +# wiki.transaction(true) do +# wiki.keys.each do |key| +# puts key +# puts wiki[key] +# end +# end +# +class PStore[unchecked out K = untyped, unchecked out V = untyped] + # + # Returns the value for the given `key` if the key exists. `nil` otherwise; if + # not `nil`, the returned value is an object or a hierarchy of objects: + # + # example_store do |store| + # store.transaction do + # store[:foo] # => 0 + # store[:nope] # => nil + # end + # end + # + # Returns `nil` if there is no such key. + # + # See also [Hierarchical Values](rdoc-ref:PStore@Hierarchical+Values). + # + # Raises an exception if called outside a transaction block. + # + def []: (K name) -> V? + + # + # Creates or replaces the value for the given `key`: + # + # example_store do |store| + # temp.transaction do + # temp[:bat] = 3 + # end + # end + # + # See also [Hierarchical Values](rdoc-ref:PStore@Hierarchical+Values). + # + # Raises an exception if called outside a transaction block. + # + def []=: (K name, V value) -> V + + # + # Exits the current transaction block, discarding any changes specified in the + # [transaction block](rdoc-ref:PStore@The+Transaction+Block). + # + # Raises an exception if called outside a transaction block. + # + def abort: () -> void + + # + # Exits the current transaction block, committing any changes specified in the + # [transaction block](rdoc-ref:PStore@The+Transaction+Block). + # + # Raises an exception if called outside a transaction block. + # + def commit: () -> void + + # + # Removes and returns the value at `key` if it exists: + # + # example_store do |store| + # store.transaction do + # store[:bat] = 3 + # store.delete(:bat) + # end + # end + # + # Returns `nil` if there is no such key. + # + # Raises an exception if called outside a transaction block. + # + def delete: (K name) -> V? + + # + # Like #[], except that it accepts a default value for the store. If the `key` + # does not exist: + # + # * Raises an exception if `default` is `PStore::Error`. + # * Returns the value of `default` otherwise: + # + # example_store do |store| + # store.transaction do + # store.fetch(:nope, nil) # => nil + # store.fetch(:nope) # Raises an exception. + # end + # end + # + # Raises an exception if called outside a transaction block. + # + def fetch: (K name, ?V default) -> V + + # + # Returns the string file path used to create the store: + # + # store.path # => "flat.store" + # + def path: () -> String + + # + # + def key?: (K name) -> bool + + alias root? key? + + # + # + def keys: () -> Array[K] + + alias roots keys + + # + # Opens a transaction block for the store. See + # [Transactions](rdoc-ref:PStore@Transactions). + # + # With argument `read_only` as `false`, the block may both read from and write + # to the store. + # + # With argument `read_only` as `true`, the block may not include calls to + # #transaction, #[]=, or #delete. + # + # Raises an exception if called within a transaction block. + # + def transaction: [U] (?bool read_only) { (self) -> U } -> U + + # + # Whether PStore should do its best to prevent file corruptions, even when an + # unlikely error (such as memory-error or filesystem error) occurs: + # + # * `true`: changes are posted by creating a temporary file, writing the + # updated data to it, then renaming the file to the given #path. File + # integrity is maintained. Note: has effect only if the filesystem has + # atomic file rename (as do POSIX platforms Linux, MacOS, FreeBSD and + # others). + # + # * `false` (the default): changes are posted by rewinding the open file and + # writing the updated data. File integrity is maintained if the filesystem + # raises no unexpected I/O error; if such an error occurs during a write to + # the store, the file may become corrupted. + # + def ultra_safe: () -> bool + + # + # Whether PStore should do its best to prevent file corruptions, even when an + # unlikely error (such as memory-error or filesystem error) occurs: + # + # * `true`: changes are posted by creating a temporary file, writing the + # updated data to it, then renaming the file to the given #path. File + # integrity is maintained. Note: has effect only if the filesystem has + # atomic file rename (as do POSIX platforms Linux, MacOS, FreeBSD and + # others). + # + # * `false` (the default): changes are posted by rewinding the open file and + # writing the updated data. File integrity is maintained if the filesystem + # raises no unexpected I/O error; if such an error occurs during a write to + # the store, the file may become corrupted. + # + def ultra_safe=: (bool) -> bool + + private + + def dump: (untyped table) -> untyped + + # + # + def empty_marshal_checksum: () -> String + + # + # + def empty_marshal_data: () -> String + + # + # Raises PStore::Error if the calling code is not in a PStore#transaction. + # + def in_transaction: () -> void + + # + # Raises PStore::Error if the calling code is not in a PStore#transaction or if + # the code is in a read-only PStore#transaction. + # + def in_transaction_wr: () -> void + + # + # Returns a new PStore object. + # + # Argument `file` is the path to the file in which objects are to be stored; if + # the file exists, it should be one that was written by PStore. + # + # path = 't.store' + # store = PStore.new(path) + # + # A PStore object is + # [reentrant](https://en.wikipedia.org/wiki/Reentrancy_(computing)). If argument + # `thread_safe` is given as `true`, the object is also thread-safe (at the cost + # of a small performance penalty): + # + # store = PStore.new(path, true) + # + def initialize: (path file, ?boolish thread_safe) -> void + + def load: (untyped content) -> untyped + + # + # Load the given PStore file. If `read_only` is true, the unmarshalled Hash will + # be returned. If `read_only` is false, a 3-tuple will be returned: the + # unmarshalled Hash, a checksum of the data, and the size of the data. + # + def load_data: (path file, true read_only) -> Hash[untyped, untyped] + | (path file, false read_only) -> [Hash[untyped, untyped], String, Integer] + + # + # + def on_windows?: () -> bool + + # + # Open the specified filename (either in read-only mode or in read-write mode) + # and lock it for reading or writing. + # + # The opened File object will be returned. If *read_only* is true, and the file + # does not exist, then nil will be returned. + # + # All exceptions are propagated. + # + def open_and_lock_file: (string filename, bool read_only) -> File? + + # + # + def save_data: (untyped original_checksum, untyped original_file_size, untyped file) -> untyped + + # + # + def save_data_with_atomic_file_rename_strategy: (string data, File file) -> void + + # + # + def save_data_with_fast_strategy: (string data, File file) -> void + + EMPTY_MARSHAL_CHECKSUM: String + + EMPTY_MARSHAL_DATA: String + + EMPTY_STRING: String + + RDWR_ACCESS: { mode: Integer, encoding: Encoding } + + RD_ACCESS: { mode: Integer, encoding: Encoding } + + VERSION: String + + WR_ACCESS: { mode: Integer, encoding: Encoding } +end diff --git a/gems/pstore/_reviewers.yaml b/gems/pstore/_reviewers.yaml new file mode 100644 index 000000000..0b4d3b7e8 --- /dev/null +++ b/gems/pstore/_reviewers.yaml @@ -0,0 +1,2 @@ +reviewers: + - ksss