fmt 0.1.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +167 -93
- 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 +50 -12
- 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 -18
- data/lib/fmt/embed.rb +0 -19
- data/lib/fmt/filter.rb +0 -32
- data/lib/fmt/filter_groups/filter_group.rb +0 -56
- data/lib/fmt/filter_groups/rainbow_filter_group.rb +0 -27
- data/lib/fmt/filter_groups/string_filter_group.rb +0 -28
- data/lib/fmt/formatter.rb +0 -60
- 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 -57
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,30 +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
|
-
|
23
|
-
|
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(...)
|
24
54
|
end
|
25
55
|
end
|
26
56
|
end
|
27
57
|
|
28
|
-
|
29
|
-
|
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)
|
30
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
|