fmt 0.1.2 → 0.3.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 +4 -4
- data/README.md +170 -87
- data/lib/fmt/boot.rb +50 -0
- data/lib/fmt/lru_cache.rb +181 -0
- data/lib/fmt/mixins/matchable.rb +26 -0
- data/lib/fmt/models/arguments.rb +194 -0
- data/lib/fmt/models/embed.rb +48 -0
- data/lib/fmt/models/macro.rb +58 -0
- data/lib/fmt/models/model.rb +66 -0
- data/lib/fmt/models/pipeline.rb +47 -0
- data/lib/fmt/models/template.rb +55 -0
- data/lib/fmt/node.rb +128 -0
- data/lib/fmt/parsers/arguments_parser.rb +43 -0
- data/lib/fmt/parsers/embed_parser.rb +54 -0
- data/lib/fmt/parsers/macro_parser.rb +113 -0
- data/lib/fmt/parsers/parser.rb +56 -0
- data/lib/fmt/parsers/pipeline_parser.rb +41 -0
- data/lib/fmt/parsers/template_parser.rb +125 -0
- data/lib/fmt/refinements/kernel_refinement.rb +38 -0
- data/lib/fmt/registries/native_registry.rb +66 -0
- data/lib/fmt/registries/rainbow_registry.rb +36 -0
- data/lib/fmt/registries/registry.rb +127 -0
- data/lib/fmt/renderer.rb +132 -0
- data/lib/fmt/sigils.rb +23 -0
- data/lib/fmt/token.rb +126 -0
- data/lib/fmt/tokenizer.rb +96 -0
- data/lib/fmt/version.rb +3 -1
- data/lib/fmt.rb +51 -11
- data/sig/generated/fmt/boot.rbs +2 -0
- data/sig/generated/fmt/lru_cache.rbs +122 -0
- data/sig/generated/fmt/mixins/matchable.rbs +18 -0
- data/sig/generated/fmt/models/arguments.rbs +115 -0
- data/sig/generated/fmt/models/embed.rbs +34 -0
- data/sig/generated/fmt/models/macro.rbs +37 -0
- data/sig/generated/fmt/models/model.rbs +45 -0
- data/sig/generated/fmt/models/pipeline.rbs +31 -0
- data/sig/generated/fmt/models/template.rbs +33 -0
- data/sig/generated/fmt/node.rbs +64 -0
- data/sig/generated/fmt/parsers/arguments_parser.rbs +25 -0
- data/sig/generated/fmt/parsers/embed_parser.rbs +36 -0
- data/sig/generated/fmt/parsers/macro_parser.rbs +60 -0
- data/sig/generated/fmt/parsers/parser.rbs +44 -0
- data/sig/generated/fmt/parsers/pipeline_parser.rbs +25 -0
- data/sig/generated/fmt/parsers/template_parser.rbs +50 -0
- data/sig/generated/fmt/refinements/kernel_refinement.rbs +23 -0
- data/sig/generated/fmt/registries/native_registry.rbs +19 -0
- data/sig/generated/fmt/registries/rainbow_registry.rbs +11 -0
- data/sig/generated/fmt/registries/registry.rbs +69 -0
- data/sig/generated/fmt/renderer.rbs +70 -0
- data/sig/generated/fmt/sigils.rbs +30 -0
- data/sig/generated/fmt/token.rbs +77 -0
- data/sig/generated/fmt/tokenizer.rbs +51 -0
- data/sig/generated/fmt/version.rbs +5 -0
- data/sig/generated/fmt.rbs +41 -0
- metadata +126 -16
- data/lib/fmt/embed.rb +0 -19
- data/lib/fmt/filter.rb +0 -32
- data/lib/fmt/filters.rb +0 -76
- data/lib/fmt/formatter.rb +0 -50
- data/lib/fmt/scanners/base_scanner.rb +0 -41
- data/lib/fmt/scanners/embed_scanner.rb +0 -56
- data/lib/fmt/scanners/filter_scanner.rb +0 -31
- data/lib/fmt/scanners/key_scanner.rb +0 -15
- data/lib/fmt/scanners.rb +0 -3
- data/lib/fmt/transformer.rb +0 -63
data/lib/fmt/renderer.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rbs_inline: enabled
|
4
|
+
|
5
|
+
module Fmt
|
6
|
+
# Renders templates to a formatted string
|
7
|
+
class Renderer
|
8
|
+
PIPELINE_START = Regexp.new("(?=%s)" % [Sigils::FORMAT_PREFIX]).freeze # : Regexp -- detects start of first pipeline
|
9
|
+
|
10
|
+
# Constructor
|
11
|
+
# @rbs template: Template
|
12
|
+
def initialize(template)
|
13
|
+
@template = template
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :template # : Template
|
17
|
+
|
18
|
+
# Renders the template to a string
|
19
|
+
# @note Positional and Keyword arguments are mutually exclusive
|
20
|
+
# @rbs args: Array[Object] -- positional arguments (user provided)
|
21
|
+
# @rbs kwargs: Hash[Symbol, Object] -- keyword arguments (user provided)
|
22
|
+
# @rbs return: String -- rendered template
|
23
|
+
def render(*args, **kwargs)
|
24
|
+
raise Error, "positional and keyword arguments are mutually exclusive" if args.any? && kwargs.any?
|
25
|
+
|
26
|
+
context = template.source
|
27
|
+
|
28
|
+
render_embeds(context, *args, **kwargs) do |embed, result|
|
29
|
+
kwargs[embed.key] = result
|
30
|
+
end
|
31
|
+
|
32
|
+
render_pipelines(context, *args, **kwargs)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Escapes a string for use in a regular expression
|
38
|
+
# @rbs value: String -- string to escape
|
39
|
+
# @rbs return: String -- escaped string
|
40
|
+
def esc(value) = Regexp.escape(value.to_s)
|
41
|
+
|
42
|
+
# Renders all template embeds
|
43
|
+
# @rbs context: String -- starting context
|
44
|
+
# @rbs args: Array[Object] -- positional arguments (user provided)
|
45
|
+
# @rbs kwargs: Hash[Symbol, Object] -- keyword arguments (user provided)
|
46
|
+
# @rbs &block: Proc -- block to execute after rendering embeds (signature: Proc(String, *args, **kwargs))
|
47
|
+
def render_embeds(context, *args, **kwargs)
|
48
|
+
template.embeds.each do |embed|
|
49
|
+
yield embed, Renderer.new(embed.template).render(*args, **kwargs)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Renders all template pipelines
|
54
|
+
# @rbs context: String -- starting context
|
55
|
+
# @rbs args: Array[Object] -- positional arguments (user provided)
|
56
|
+
# @rbs kwargs: Hash[Symbol, Object] -- keyword arguments (user provided)
|
57
|
+
# @rbs return: String
|
58
|
+
def render_pipelines(context, *args, **kwargs)
|
59
|
+
template.pipelines.each_with_index do |pipeline, index|
|
60
|
+
result = render_pipeline(pipeline, *args[index..], **kwargs)
|
61
|
+
context = context.sub(pipeline.urtext, result)
|
62
|
+
end
|
63
|
+
|
64
|
+
context
|
65
|
+
end
|
66
|
+
|
67
|
+
# Renders a single pipeline
|
68
|
+
# @rbs pipeline: Pipeline -- pipeline to render
|
69
|
+
# @rbs args: Array[Object] -- positional arguments (user provided)
|
70
|
+
# @rbs kwargs: Hash[Symbol, Object] -- keyword arguments (user provided)
|
71
|
+
# @rbs return: String
|
72
|
+
def render_pipeline(pipeline, *args, **kwargs)
|
73
|
+
result = ""
|
74
|
+
|
75
|
+
pipeline.macros.each do |macro|
|
76
|
+
result = case macro
|
77
|
+
in name: Sigils::FORMAT_METHOD
|
78
|
+
case [args, kwargs]
|
79
|
+
in [], {} then invoke_formatter(macro)
|
80
|
+
in [], {**} => kwargs then invoke_formatter(macro, **kwargs)
|
81
|
+
in [*], {} then invoke_formatter(macro, *args)
|
82
|
+
in [*], {**} => kwargs then invoke_formatter(macro, *args, **kwargs)
|
83
|
+
end
|
84
|
+
else invoke_macro(result, macro)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
result
|
89
|
+
end
|
90
|
+
|
91
|
+
# Invokes native Ruby string formatting
|
92
|
+
# @rbs macro: Macro -- macro to use (source, arguments, etc.)
|
93
|
+
# @rbs args: Array[Object] -- positional arguments (user provided)
|
94
|
+
# @rbs kwargs: Hash[Symbol, Object] -- keyword arguments (user provided)
|
95
|
+
# @rbs return: String
|
96
|
+
def invoke_formatter(macro, *args, **kwargs)
|
97
|
+
callable = Fmt.registry[[Kernel, macro.name]]
|
98
|
+
context = macro.arguments.args[0]
|
99
|
+
context.instance_exec(*args, **kwargs, &callable)
|
100
|
+
rescue => error
|
101
|
+
raise_format_error(macro, *args, cause: error, **kwargs)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Invokes a macro
|
105
|
+
# @rbs context: Object -- self in callable (Proc)
|
106
|
+
# @rbs macro: Macro -- macro to use (source, arguments, etc.)
|
107
|
+
# @rbs return: Object -- result
|
108
|
+
def invoke_macro(context, macro)
|
109
|
+
callable = Fmt.registry[[context.class, macro.name]] || Fmt.registry[[Object, macro.name]]
|
110
|
+
raise Error, "[#{context.class.name} | Object, #{macro.name}] is not a registered formatter!" unless callable
|
111
|
+
|
112
|
+
args = macro.arguments.args
|
113
|
+
kwargs = macro.arguments.kwargs
|
114
|
+
|
115
|
+
context.instance_exec(*args, **kwargs, &callable)
|
116
|
+
rescue => error
|
117
|
+
args ||= []
|
118
|
+
kwargs ||= {}
|
119
|
+
raise_format_error(macro, *args, cause: error, **kwargs)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Raises an invocation error if/when Proc invocations fail
|
123
|
+
# @rbs macro: Macro -- macro that failed
|
124
|
+
# @rbs args: Array[Object] -- positional arguments (user provided)
|
125
|
+
# @rbs cause: Exception -- exception that caused the error
|
126
|
+
# @rbs kwargs: Hash[Symbol, Object] -- keyword arguments (user provided)
|
127
|
+
# @rbs return: void
|
128
|
+
def raise_format_error(macro, *args, cause:, **kwargs)
|
129
|
+
raise FormatError, "Error in macro! `#{macro.urtext}` args=#{args.inspect} kwargs=#{kwargs.inspect} cause=#{cause.inspect}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/lib/fmt/sigils.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rbs_inline: enabled
|
4
|
+
|
5
|
+
module Fmt
|
6
|
+
# Common Fmt sigils (used in String templates)
|
7
|
+
class Sigils
|
8
|
+
# Native Ruby format specifiers
|
9
|
+
# @see https://docs.ruby-lang.org/en/master/format_specifications_rdoc.html
|
10
|
+
FORMAT_PREFIX = "%" # : String -- start of a format string (i.e. a template)
|
11
|
+
FORMAT_SPECIFIERS = %w[A E G X a b c d e f g i o p s u x].freeze # : Array[String] -- format specifiers
|
12
|
+
FORMAT_FLAGS = [" ", "#", "+", "-", "0", ":", "::", "^", "_"].freeze # : Array[String] -- format flags
|
13
|
+
FORMAT_METHOD = :sprintf # : Symbol -- format method name
|
14
|
+
|
15
|
+
KEY_PREFIXES = ["<", "{"].freeze # : Array[String] -- keyed template prefix
|
16
|
+
KEY_SUFFIXES = [">", "}"].freeze # : Array[String] -- keyed template suffix
|
17
|
+
ARGS_PREFIX = "(" # : String -- macro arguments prefix
|
18
|
+
ARGS_SUFFIX = ")" # : String -- macro arguments suffix
|
19
|
+
PIPE_OPERATOR = "|>" # : String -- macro delimiter
|
20
|
+
EMBED_PREFIX = "{{" # : String -- embed prefix
|
21
|
+
EMBED_SUFFIX = "}}" # : String -- embed prefix
|
22
|
+
end
|
23
|
+
end
|
data/lib/fmt/token.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rbs_inline: enabled
|
4
|
+
|
5
|
+
module Fmt
|
6
|
+
# Convenience wrapper for Ripper tokens
|
7
|
+
#
|
8
|
+
# @see https://rubyapi.org/3.4/o/ripper
|
9
|
+
# @see doc/RIPPER.md (cheetsheet)
|
10
|
+
#
|
11
|
+
# @example Ripper Token
|
12
|
+
# [[lineno, column], type, token, state]
|
13
|
+
# [[Integer, Integer], Symbol, String, Object]
|
14
|
+
#
|
15
|
+
class Token
|
16
|
+
include Matchable
|
17
|
+
|
18
|
+
# Constructor
|
19
|
+
# @rbs ripper_token: Array[[Integer, Integer], Symbol, String, Object] -- Ripper token
|
20
|
+
def initialize(ripper_token)
|
21
|
+
(lineno, column), type, token, state = ripper_token
|
22
|
+
@ripper_token = ripper_token
|
23
|
+
@lineno = lineno
|
24
|
+
@column = column
|
25
|
+
@type = type.to_s.delete_prefix("on_").to_sym # strip Ripper's "on_" prefix for parser semantics
|
26
|
+
@token = token
|
27
|
+
@state = state
|
28
|
+
freeze
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_reader :ripper_token # : Array[[Integer, Integer], Symbol, String, Object]
|
32
|
+
attr_reader :lineno # : Integer
|
33
|
+
attr_reader :column # : Integer
|
34
|
+
attr_reader :type # : Symbol
|
35
|
+
attr_reader :token # : String
|
36
|
+
attr_reader :state # : Object
|
37
|
+
|
38
|
+
# @note The entire data structure is considered a "token"
|
39
|
+
# Alias the embedded "token" as "value" to reduce confusion
|
40
|
+
alias_method :value, :token
|
41
|
+
|
42
|
+
# Returns a Hash representation of the token
|
43
|
+
# @rbs return: Hash[Symbol, Object]
|
44
|
+
def to_h
|
45
|
+
{
|
46
|
+
lineno: lineno,
|
47
|
+
column: column,
|
48
|
+
type: type,
|
49
|
+
token: token,
|
50
|
+
value: token,
|
51
|
+
state: state
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
# --------------------------------------------------------------------------
|
56
|
+
# @!group Pattern Matching Support
|
57
|
+
# --------------------------------------------------------------------------
|
58
|
+
alias_method :deconstruct, :ripper_token
|
59
|
+
|
60
|
+
# Returns a Hash representation of the token limited to the given keys
|
61
|
+
# @rbs keys: Array[Symbol] -- keys to include
|
62
|
+
# @rbs return: Hash[Symbol, Object]
|
63
|
+
def deconstruct_keys(keys = [])
|
64
|
+
to_h.select { _1 in ^keys }
|
65
|
+
end
|
66
|
+
|
67
|
+
# --------------------------------------------------------------------------
|
68
|
+
# @!group Helpers
|
69
|
+
# --------------------------------------------------------------------------
|
70
|
+
|
71
|
+
# Indicates if the token is a left paren (i.e. start of arguments)
|
72
|
+
# @rbs return: bool
|
73
|
+
def arguments_start?
|
74
|
+
type == :lparen
|
75
|
+
end
|
76
|
+
|
77
|
+
# Indicates if the token is a right paren (i.e. end of arguments)
|
78
|
+
# @rbs return: bool
|
79
|
+
def arguments_finish?
|
80
|
+
type == :rparen
|
81
|
+
end
|
82
|
+
|
83
|
+
# Indicates if the token starts a key (string formatting named parameter)
|
84
|
+
# @rbs return: bool
|
85
|
+
def key_start?
|
86
|
+
type == :lbrace || (type == :op && value == "<")
|
87
|
+
end
|
88
|
+
|
89
|
+
# Indicates if the token finishes a key (string formatting named parameter)
|
90
|
+
# @rbs return: bool
|
91
|
+
def key_finish?
|
92
|
+
type == :rbrace || (type == :op && value == ">")
|
93
|
+
end
|
94
|
+
|
95
|
+
# Indicates if the token is an identifier (e.g. method name, format specifier, variable name, etc.)
|
96
|
+
# @rbs return: bool
|
97
|
+
def identifier?
|
98
|
+
type == :ident
|
99
|
+
end
|
100
|
+
|
101
|
+
# Indicates if the token is a method name (i.e. method name or operator)
|
102
|
+
# @rbs return: bool
|
103
|
+
def method_name?
|
104
|
+
identifier? || operator?
|
105
|
+
end
|
106
|
+
|
107
|
+
# Indicates if the token is an operator
|
108
|
+
# @rbs return: bool
|
109
|
+
def operator?
|
110
|
+
type == :op
|
111
|
+
end
|
112
|
+
|
113
|
+
# Indicates if the token is a whitespace
|
114
|
+
# @rbs return: bool
|
115
|
+
def whitespace?
|
116
|
+
type == :on_sp
|
117
|
+
end
|
118
|
+
|
119
|
+
# Indicates if the token is a native String format specifier
|
120
|
+
# @see Sigils::FORMAT_SPECIFIERS
|
121
|
+
# @rbs return: bool
|
122
|
+
def specifier?
|
123
|
+
identifier? && value in Sigils::FORMAT_SPECIFIERS
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rbs_inline: enabled
|
4
|
+
|
5
|
+
module Fmt
|
6
|
+
# Ruby source code token extractor
|
7
|
+
#
|
8
|
+
# Uses Ripper from Ruby's standard library
|
9
|
+
# @see https://rubyapi.org/3.4/o/ripper
|
10
|
+
# @see doc/RIPPER.md (cheetsheet)
|
11
|
+
#
|
12
|
+
# @example Ripper token
|
13
|
+
# [[lineno, column], type, token, state]
|
14
|
+
# [[Integer, Integer], Symbol, String, Object]
|
15
|
+
#
|
16
|
+
class Tokenizer
|
17
|
+
# Constructor
|
18
|
+
# @rbs urtext: String -- original source code
|
19
|
+
def initialize(urtext)
|
20
|
+
@urtext = urtext.to_s
|
21
|
+
@tokens = []
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :urtext # : String -- original source code
|
25
|
+
attr_reader :tokens # : Array[Object] -- result of tokenization
|
26
|
+
|
27
|
+
# Tokenizes the urtext (original source code)
|
28
|
+
# @rbs return: Array[Token] -- wrapped ripper tokens
|
29
|
+
def tokenize
|
30
|
+
Ripper.lex(urtext).each do |token|
|
31
|
+
tokens << Token.new(token)
|
32
|
+
end
|
33
|
+
tokens
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns identifier tokens (typically method names)
|
37
|
+
# @rbs start: Integer -- start index
|
38
|
+
# @rbs return: Array[Token]
|
39
|
+
def identifier_tokens(start: 0)
|
40
|
+
tokens[start..].each_with_object([]) do |token, memo|
|
41
|
+
break memo if token.arguments_start?
|
42
|
+
memo << token if token.identifier?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns method tokens (identifiers and operators)
|
47
|
+
# @rbs start: Integer -- start index
|
48
|
+
# @rbs return: Array[Token]
|
49
|
+
def method_name_tokens(start: 0)
|
50
|
+
identifier_tokens(start: start) + operator_tokens(start: start)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns key (named parameter) tokens
|
54
|
+
# @rbs start: Integer -- start index
|
55
|
+
# @rbs return: Array[Token]?
|
56
|
+
def key_tokens(start: 0)
|
57
|
+
start = tokens[start..].find(&:key_start?)
|
58
|
+
identifier = tokens[tokens.index(start)..].find(&:identifier?) if start
|
59
|
+
finish = tokens[tokens.index(identifier)..].find(&:key_finish?) if identifier
|
60
|
+
list = [start, identifier, finish].compact
|
61
|
+
|
62
|
+
return [] unless list.size == 3
|
63
|
+
return [] unless urtext.include?(list.map(&:value).join)
|
64
|
+
|
65
|
+
list
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns operator tokens
|
69
|
+
# @rbs start: Integer -- start index
|
70
|
+
# @rbs return: Array[Token]
|
71
|
+
def operator_tokens(start: 0)
|
72
|
+
tokens[start..].each_with_object([]) do |token, memo|
|
73
|
+
break memo if token.arguments_start?
|
74
|
+
memo << token if token.operator?
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns the argument tokens
|
79
|
+
# @rbs start: Integer -- start index
|
80
|
+
# @rbs return: Array[Token]
|
81
|
+
def argument_tokens(start: 0)
|
82
|
+
starters = 0
|
83
|
+
finishers = 0
|
84
|
+
|
85
|
+
tokens[start..].each_with_object([]) do |token, memo|
|
86
|
+
break memo if starters.positive? && finishers == starters
|
87
|
+
|
88
|
+
starters += 1 if token.arguments_start?
|
89
|
+
next if starters.zero?
|
90
|
+
|
91
|
+
finishers += 1 if token.arguments_finish?
|
92
|
+
memo << token
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/fmt/version.rb
CHANGED
data/lib/fmt.rb
CHANGED
@@ -1,28 +1,68 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
# rbs_inline: enabled
|
4
|
+
#
|
5
|
+
require_relative "fmt/boot"
|
5
6
|
|
7
|
+
# Extends native Ruby String format specifications
|
8
|
+
# @see https://ruby-doc.org/3.3.4/format_specifications_rdoc.html
|
6
9
|
module Fmt
|
10
|
+
LOCK = Monitor.new # : Monitor
|
11
|
+
private_constant :LOCK
|
12
|
+
|
13
|
+
# Standard error class for Fmt
|
7
14
|
class Error < StandardError; end
|
8
15
|
|
16
|
+
# Error for formatting failures
|
17
|
+
class FormatError < Error; end
|
18
|
+
|
9
19
|
class << self
|
10
|
-
|
11
|
-
|
20
|
+
# Global registry for storing and retrieving String formatters i.e. Procs
|
21
|
+
def registry
|
22
|
+
@registry ||= LOCK.synchronize do
|
23
|
+
NativeRegistry.new.merge! RainbowRegistry.new
|
24
|
+
end
|
12
25
|
end
|
13
26
|
|
14
|
-
|
15
|
-
|
27
|
+
# Adds a keypair to the registry
|
28
|
+
# @rbs key: Array[Class | Module, Symbol] -- key to use
|
29
|
+
# @rbs overwrite: bool -- overwrite the existing keypair (default: false)
|
30
|
+
# @rbs block: Proc -- Proc to add (optional, if proc is provided)
|
31
|
+
# @rbs return: Proc
|
32
|
+
def register(...)
|
33
|
+
registry.add(...)
|
16
34
|
end
|
17
35
|
|
18
|
-
|
19
|
-
|
36
|
+
# Deletes a keypair from the registry
|
37
|
+
# @rbs key: Array[Class | Module, Symbol] -- key to delete
|
38
|
+
# @rbs return: Proc?
|
39
|
+
def unregister(...)
|
40
|
+
registry.delete(...)
|
20
41
|
end
|
21
42
|
|
22
|
-
|
43
|
+
# Executes a block with registry overrides
|
44
|
+
#
|
45
|
+
# @note Overrides will temporarily be added to the registry
|
46
|
+
# and will overwrite existing entries for the duration of the block
|
47
|
+
# Non overriden entries remain unchanged
|
48
|
+
#
|
49
|
+
# @rbs overrides: Hash[Array[Class | Module, Symbol], Proc] -- overrides to apply
|
50
|
+
# @rbs block: Proc -- block to execute with overrides
|
51
|
+
# @rbs return: void
|
52
|
+
def with_overrides(...)
|
53
|
+
registry.with_overrides(...)
|
54
|
+
end
|
23
55
|
end
|
24
56
|
end
|
25
57
|
|
26
|
-
|
27
|
-
|
58
|
+
# Top level helper for formatting and rendering a source string
|
59
|
+
# @rbs source: String -- string to format
|
60
|
+
# @rbs args: Array[Object] -- positional arguments (user provided)
|
61
|
+
# @rbs kwargs: Hash[Symbol, Object] -- keyword arguments (user provided)
|
62
|
+
# @rbs return: String -- rendered template
|
63
|
+
def Fmt(source, *args, **kwargs)
|
64
|
+
ast = Fmt::TemplateParser.new(source).parse
|
65
|
+
template = Fmt::Template.new(ast)
|
66
|
+
renderer = Fmt::Renderer.new(template)
|
67
|
+
renderer.render(*args, **kwargs)
|
28
68
|
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# Generated from lib/fmt/lru_cache.rb with RBS::Inline
|
2
|
+
|
3
|
+
module Fmt
|
4
|
+
# A threadsafe fixed-size LRU in-memory cache
|
5
|
+
# Grows to capacity then evicts the least used entries
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# cache = Fmt::Cache.new
|
9
|
+
#
|
10
|
+
# cache.put :key, "value"
|
11
|
+
# cache.get :key
|
12
|
+
# cache.delete :key
|
13
|
+
# cache.fetch :key, "default"
|
14
|
+
# cache.fetch(:key) { "default" }
|
15
|
+
#
|
16
|
+
# @example Capacity
|
17
|
+
# Fmt::Cache.capacity = 10_000
|
18
|
+
class LRUCache
|
19
|
+
include MonitorMixin
|
20
|
+
|
21
|
+
DEFAULT_CAPACITY: ::Integer
|
22
|
+
|
23
|
+
# Constructor
|
24
|
+
# @rbs capacity: Integer -- max capacity (negative values are uncapped, default: 5_000)
|
25
|
+
# @rbs return: Fmt::Cache
|
26
|
+
def initialize: (?capacity: Integer) -> Fmt::Cache
|
27
|
+
|
28
|
+
# The cache max capacity (number of entries)
|
29
|
+
# @rbs return: Integer
|
30
|
+
def capacity: () -> Integer
|
31
|
+
|
32
|
+
# Set the max capacity (number of entries)
|
33
|
+
# @rbs capacity: Integer -- new max capacity
|
34
|
+
# @rbs return: Integer -- new max capacity
|
35
|
+
def capacity=: (Integer capacity) -> Integer
|
36
|
+
|
37
|
+
# Indicates if the cache is capped
|
38
|
+
# @rbs return: bool
|
39
|
+
def capped?: () -> bool
|
40
|
+
|
41
|
+
# Clears the cache
|
42
|
+
# @rbs return: void
|
43
|
+
def clear: () -> void
|
44
|
+
|
45
|
+
# Deletes the entry for the specified key
|
46
|
+
# @rbs key: Object -- key to delete
|
47
|
+
# @rbs return: Object? -- the deleted value
|
48
|
+
def delete: (Object key) -> Object?
|
49
|
+
|
50
|
+
# Fetches the value for the specified key
|
51
|
+
# Writes the default value if the key is not found
|
52
|
+
# @rbs key: Object -- key to fetch
|
53
|
+
# @rbs default: Object -- default value to write
|
54
|
+
# @rbs block: Proc -- block to call to get the default value
|
55
|
+
# @rbs return: Object -- value
|
56
|
+
def fetch: (Object key, ?Object default) ?{ (?) -> untyped } -> Object
|
57
|
+
|
58
|
+
# Fetches a value from the cache without synchronization (not thread safe)
|
59
|
+
# @rbs key: Object -- key to fetch
|
60
|
+
# @rbs default: Object -- default value to write
|
61
|
+
# @rbs block: Proc -- block to call to get the default value
|
62
|
+
# @rbs return: Object -- value
|
63
|
+
def fetch_unsafe: (Object key, ?Object default) ?{ (?) -> untyped } -> Object
|
64
|
+
|
65
|
+
# Indicates if the cache is full
|
66
|
+
# @rbs return: bool
|
67
|
+
def full?: () -> bool
|
68
|
+
|
69
|
+
# Retrieves the value for the specified key
|
70
|
+
# @rbs key: Object -- key to retrieve
|
71
|
+
def get: (Object key) -> untyped
|
72
|
+
|
73
|
+
# Cache keys
|
74
|
+
# @rbs return: Array[Object]
|
75
|
+
def keys: () -> Array[Object]
|
76
|
+
|
77
|
+
# Indicates if the cache contains the specified key
|
78
|
+
# @rbs key: Object -- key to check
|
79
|
+
# @rbs return: bool
|
80
|
+
def key?: (Object key) -> bool
|
81
|
+
|
82
|
+
# Stores the value for the specified key
|
83
|
+
# @rbs key: Object -- key to store
|
84
|
+
# @rbs value: Object -- value to store
|
85
|
+
# @rbs return: Object -- value
|
86
|
+
def put: (Object key, Object value) -> Object
|
87
|
+
|
88
|
+
# Resets the cache capacity to the default
|
89
|
+
# @rbs return: Integer -- capacity
|
90
|
+
def reset_capacity: () -> Integer
|
91
|
+
|
92
|
+
# The current size of the cache (number of entries)
|
93
|
+
# @rbs return: Integer
|
94
|
+
def size: () -> Integer
|
95
|
+
|
96
|
+
# Returns a Hash with only the given keys
|
97
|
+
# @rbs keys: Array[Object] -- keys to include
|
98
|
+
# @rbs return: Hash[Object, Object]
|
99
|
+
def slice: (*untyped keys) -> Hash[Object, Object]
|
100
|
+
|
101
|
+
# Hash representation of the cache
|
102
|
+
# @rbs return: Hash[Object, Proc]
|
103
|
+
def to_h: () -> Hash[Object, Proc]
|
104
|
+
|
105
|
+
# Cache values
|
106
|
+
# @rbs return: Array[Object]
|
107
|
+
def values: () -> Array[Object]
|
108
|
+
|
109
|
+
# Executes a block with a synchronized mutex
|
110
|
+
# @rbs block: Proc -- block to execute
|
111
|
+
def lock: () ?{ (?) -> untyped } -> untyped
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
attr_reader store: untyped
|
116
|
+
|
117
|
+
# Moves the key to the end keeping it fresh
|
118
|
+
# @rbs key: Object -- key to reposition
|
119
|
+
# @rbs return: Object -- value
|
120
|
+
def reposition: (Object key) -> Object
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Generated from lib/fmt/mixins/matchable.rb with RBS::Inline
|
2
|
+
|
3
|
+
module Fmt
|
4
|
+
module Matchable
|
5
|
+
# Hash representation of the Object (required for pattern matching)
|
6
|
+
# @rbs return: Hash[Symbol, Object]
|
7
|
+
def to_h: () -> Hash[Symbol, Object]
|
8
|
+
|
9
|
+
# Returns a Hash representation of the object limited to the given keys
|
10
|
+
# @rbs keys: Array[Symbol] -- keys to include
|
11
|
+
# @rbs return: Hash[Symbol, Object]
|
12
|
+
def deconstruct_keys: (?Array[Symbol] keys) -> Hash[Symbol, Object]
|
13
|
+
|
14
|
+
# Returns an Array representation of the object
|
15
|
+
# @rbs return: Array[Object]
|
16
|
+
def deconstruct: () -> Array[Object]
|
17
|
+
end
|
18
|
+
end
|