fmt 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 546ccf984763e8c2f3590f1fead68a1a7c9f0a874fefd241df21b2cb54550aac
4
- data.tar.gz: 81cd66780873959882857eeffe05abea6bcf447e4de64946daf089c1efe05ab1
3
+ metadata.gz: a4bafcbfda352b24f4203057c8ae8c8df4ddc109727fd610250d54c1db02ea2a
4
+ data.tar.gz: 1e672363e323f01757e1590441d8f836bba9b03936b4551189cd040a942936f2
5
5
  SHA512:
6
- metadata.gz: 8c72adc7487d3352361e5ad8f56fda9c623351112dd377542830023e165c98cd6abfac7526b632f4f2a689dddd0bba1c34f901bfdee6ee72d067271e5cb17458
7
- data.tar.gz: 6c625ceeabf0aec90bda2945247eb648e5bb05ca41cf6f7b7a139c0366d8d8794f239fcb7a3e0e81b24017494e98a4bc826e55423764206986481c85e94e5186
6
+ metadata.gz: 39a4f811f2a9ee86e82fd0e7ce557aa6f22971cd31bde8c5a717b47f74b35119c8d7616580dac5de18851329edd304a72549ecd8239c2104caea3f41d4a3da7e
7
+ data.tar.gz: 75fe78f4908b6b94eb7fe37ba0d76819562d7005405d72d3c14ccb93b55d465da95e890becd9794b53eb7c3d44970a6f14ba3d62d45c1eb78e1cf31810e4885f
data/README.md CHANGED
@@ -1,6 +1,35 @@
1
+ <p align="center">
2
+ <a href="http://blog.codinghorror.com/the-best-code-is-no-code-at-all/">
3
+ <img alt="Lines of Code" src="https://img.shields.io/badge/loc-316-47d299.svg" />
4
+ </a>
5
+ <a href="https://github.com/testdouble/standard">
6
+ <img alt="Ruby Style" src="https://img.shields.io/badge/style-standard-168AFE?logo=ruby&logoColor=FE1616" />
7
+ </a>
8
+ <a href="https://github.com/sponsors/hopsoft">
9
+ <img alt="Sponsors" src="https://img.shields.io/github/sponsors/hopsoft?color=eb4aaa&logo=GitHub%20Sponsors" />
10
+ </a>
11
+ <a href="https://twitter.com/hopsoft">
12
+ <img alt="Twitter Follow" src="https://img.shields.io/twitter/url?label=%40hopsoft&style=social&url=https%3A%2F%2Ftwitter.com%2Fhopsoft">
13
+ </a>
14
+ </p>
15
+
1
16
  # Fmt
2
17
 
3
- Fmt is a simple template engine based on native Ruby String formatting mechanics.
18
+ #### A simple template engine based on native Ruby String formatting mechanics
19
+
20
+ <!-- Tocer[start]: Auto-generated, don't remove. -->
21
+
22
+ ## Table of Contents
23
+
24
+ - [Why?](#why)
25
+ - [Setup](#setup)
26
+ - [Usage](#usage)
27
+ - [Formatting](#formatting)
28
+ - [Filters](#filters)
29
+ - [Embeds](#embeds)
30
+ - [Sponsors](#sponsors)
31
+
32
+ <!-- Tocer[finish]: Auto-generated, don't remove. -->
4
33
 
5
34
  ## Why?
6
35
 
@@ -9,46 +38,68 @@ I'm currenly using this to help build beautiful CLI applications with Ruby. Plus
9
38
  ## Setup
10
39
 
11
40
  ```
12
- bundle add fmt
13
41
  bundle add rainbow # optional
42
+ bundle add fmt
14
43
  ```
15
44
 
16
45
  ## Usage
17
46
 
18
47
  Simply create a string with embedded formatting syntax as you'd normally do with `sprintf` or `format`.
19
- i.e. `"%{...}"`
20
48
 
21
- Filters can be chained after the placeholder like so `"%{...}FILTER|FILTER|FILTER"`
22
- Filters are processed in the order they are specified.
49
+ ```ruby
50
+ "%{...}"
51
+ ```
52
+
53
+ Filters can be chained after the placeholder like so.
23
54
 
24
- You can use native Ruby formatting as well as String methods like `upcase`, `reverse`, `strip`, etc.
25
- If you have the Rainbow GEM installed, you can also use Rainbow formatting like `red`, `bold`, etc.
55
+ ```ruby
56
+ "%{...}FILTER|FILTER|FILTER"
57
+ ```
26
58
 
27
- ### Rendering
59
+ > [!NOTE]
60
+ > Filters are processed in the order they are specified.
61
+
62
+ Filters can be [native Ruby formatting](https://docs.ruby-lang.org/en/master/format_specifications_rdoc.html) as well as String methods like `capitalize`, `downcase`, `strip`, etc.
63
+ Also, you can use Rainbow filters like `bold`, `cyan`, `underline`, et al. if you have the [Rainbow GEM](https://github.com/ku1ik/rainbow) installed.
64
+
65
+ **You can even [register your own filters](#filters).**
66
+
67
+ ### Formatting
28
68
 
29
69
  Basic example:
30
70
 
31
71
  ```ruby
72
+ require "rainbow"
32
73
  require "fmt"
33
74
 
34
75
  template = "Hello %{name}cyan|bold"
35
- result = Fmt(template, name: "World")
76
+ Fmt template, name: "World"
77
+
36
78
  #=> "Hello \e[36m\e[1mWorld\e[0m"
37
79
  ```
38
80
 
81
+ ![CleanShot 2024-07-26 at 01 40 33@2x](https://github.com/user-attachments/assets/04ff90e6-254a-42d4-9169-586ac24b82f0)
82
+
39
83
  Mix and match native formatting with Rainbow formatting:
40
84
 
41
85
  ```ruby
86
+ require "rainbow"
42
87
  require "fmt"
43
88
 
44
89
  template = "Date: %{date}.10s|magenta"
45
- result = Fmt(template, date: Time.now)
90
+ Fmt template, date: Time.now
91
+
46
92
  #=> "Date: \e[35m2024-07-26\e[0m"
47
93
  ```
48
94
 
95
+ ![CleanShot 2024-07-26 at 01 42 53@2x](https://github.com/user-attachments/assets/507913b0-826b-4526-9c79-27f766c904b3)
96
+
49
97
  Multiline example:
50
98
 
51
99
  ```ruby
100
+ require "rainbow"
101
+ require "fmt"
102
+
52
103
  template = <<~T
53
104
  Date: %{date}.10s|underline
54
105
 
@@ -57,24 +108,76 @@ template = <<~T
57
108
  %{message}strip|green
58
109
  T
59
110
 
60
- result = Fmt(template, date: Time.now, name: "Hopsoft", message: "This is neat!")
111
+ Fmt template, date: Time.now, name: "Hopsoft", message: "This is neat!"
112
+
61
113
  #=> "Date: \e[4m2024-07-26\e[0m\n\nGreetings, \e[1mHOPSOFT\e[0m\n\n\e[32mThis is neat!\e[0m\n"
62
114
  ```
63
115
 
116
+ ![CleanShot 2024-07-26 at 01 44 30@2x](https://github.com/user-attachments/assets/8926009c-7cf1-4140-9a2a-6ed718d50926)
117
+
64
118
  ### Filters
65
119
 
66
120
  You can also add your own filters to Fmt by calling `Fmt.add_filter(:name, &block)`.
67
121
  The block accepts a string and should return a replacement string.
68
122
 
69
123
  ```ruby
70
- Fmt.add_filter(:repeat20) { |str| str * 20 }
124
+ require "rainbow"
125
+ require "fmt"
126
+
127
+ Fmt.add_filter(:ljust) { |val| "".ljust 14, val.to_s }
71
128
 
72
129
  template = <<~T
73
- %{head}repeat20|faint
130
+ %{head}ljust|faint
74
131
  %{message}bold
75
- %{tail}repeat20|faint
132
+ %{tail}ljust|faint
76
133
  T
77
134
 
78
- result = Fmt(template, head: "#", message: "Give it a try!", tail: "#")
79
- #=> "\e[2m####################\e[0m\n\e[1mGive it a try!\e[0m\n\e[2m####################\e[0m\n"
135
+ Fmt template, head: "#", message: "Give it a try!", tail: "#"
136
+
137
+ #=> "\e[2m##############\e[0m\n\e[1mGive it a try!\e[0m\n\e[2m##############\e[0m\n"
138
+ ```
139
+
140
+ ![CleanShot 2024-07-26 at 01 46 26@2x](https://github.com/user-attachments/assets/bd1d67c6-1182-428b-be05-756f3d330f67)
141
+
142
+ ### Embeds
143
+
144
+ Templates can be embedded or nested within other templates... as deep as needed!
145
+ Just wrap the embedded template in double curly braces: `{{EMBEDDED TEMPLATE HERE}}`
146
+
147
+ ```ruby
148
+ require "rainbow"
149
+ require "fmt"
150
+
151
+ template = "%{value}lime {{%{embed_value}red|bold|underline}}"
152
+ Fmt template, value: "Outer", embed_value: "Inner"
153
+
154
+ #=> "\e[38;5;46mOuter\e[0m \e[31m\e[1m\e[4mInner\e[0m"
80
155
  ```
156
+
157
+ ![CleanShot 2024-07-29 at 02 42 19@2x](https://github.com/user-attachments/assets/f67dd215-b848-4a23-bd73-72822cb7d970)
158
+
159
+ ```ruby
160
+ template = <<~T
161
+ |--%{value}yellow|bold|underline
162
+ | |--{{%{inner_value}green|bold|underline
163
+ | | |--{{%{deep_value}blue|bold|underline
164
+ | | | |-- We're in deep!}}}}
165
+ T
166
+
167
+ Fmt template, value: "Outer", inner_value: "Inner", deep_value: "Deep"
168
+
169
+ #=> "|--\e[33m\e[1m\e[4mOuter\e[0m\n| |--\e[32m\e[1m\e[4mInner\e[0m\n| | |--\e[34m\e[1m\e[4mDeep\e[0m\n| | | |-- We're in deep!\n"
170
+ ```
171
+
172
+ ![CleanShot 2024-07-29 at 02 45 27@2x](https://github.com/user-attachments/assets/1b933bf4-a62d-4913-b817-d6c69b0e7028)
173
+
174
+ ## Sponsors
175
+
176
+ <p align="center">
177
+ <em>Proudly sponsored by</em>
178
+ </p>
179
+ <p align="center">
180
+ <a href="https://www.clickfunnels.com?utm_source=hopsoft&utm_medium=open-source&utm_campaign=fmt">
181
+ <img src="https://images.clickfunnel.com/uploads/digital_asset/file/176632/clickfunnels-dark-logo.svg" width="575" />
182
+ </a>
183
+ </p>
data/lib/fmt/embed.rb ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fmt
4
+ class Embed
5
+ def initialize(string)
6
+ @string = string
7
+ end
8
+
9
+ attr_reader :string
10
+
11
+ def placeholder
12
+ "{{#{string}}}"
13
+ end
14
+
15
+ def format(**locals)
16
+ Fmt(string, **locals)
17
+ end
18
+ end
19
+ end
data/lib/fmt/filters.rb CHANGED
@@ -8,8 +8,6 @@ module Fmt
8
8
  include Enumerable
9
9
  include MonitorMixin
10
10
 
11
- DELIMITER = "|"
12
-
13
11
  NATIVE_FILTERS = %i[
14
12
  capitalize
15
13
  chomp
@@ -17,7 +15,6 @@ module Fmt
17
15
  downcase
18
16
  lstrip
19
17
  reverse
20
- rjust
21
18
  rstrip
22
19
  shellescape
23
20
  strip
@@ -37,17 +34,19 @@ module Fmt
37
34
  end
38
35
 
39
36
  if defined? Rainbow
40
- Rainbow::Presenter.public_instance_methods(false).each do |name|
41
- next unless Rainbow::Presenter.instance_method(name).arity == 0
42
- add(name) { |str| Rainbow(str).public_send(name) }
43
- end
37
+ begin
38
+ Rainbow::Presenter.public_instance_methods(false).each do |name|
39
+ next unless Rainbow::Presenter.public_instance_method(name).arity == 0
40
+ add(name) { |str| Rainbow(str).public_send(name) }
41
+ end
44
42
 
45
- Rainbow::X11ColorNames::NAMES.keys.each do |name|
46
- add(name) { |str| Rainbow(str).public_send(name) }
43
+ Rainbow::X11ColorNames::NAMES.keys.each do |name|
44
+ add(name) { |str| Rainbow(str).public_send(name) }
45
+ end
46
+ rescue => error
47
+ puts "Error adding Rainbow filters! #{error.inspect}"
47
48
  end
48
49
  end
49
- rescue
50
- # noop
51
50
  end
52
51
 
53
52
  def each(&block)
data/lib/fmt/formatter.rb CHANGED
@@ -2,17 +2,13 @@
2
2
 
3
3
  require "singleton"
4
4
  require_relative "filters"
5
+ require_relative "scanners"
5
6
  require_relative "transformer"
6
7
 
7
8
  module Fmt
8
9
  class Formatter
9
10
  include Singleton
10
11
 
11
- OPEN = /%\{/
12
- CLOSE = /\}/
13
- KEY = /\w+(?=\})/
14
- FILTERS = /[^\s]+(?=\s|$)/
15
-
16
12
  attr_reader :filters
17
13
 
18
14
  def format(string, **locals)
@@ -35,27 +31,20 @@ module Fmt
35
31
  end
36
32
 
37
33
  def next_transformer(string)
38
- scanner = StringScanner.new(string)
39
-
40
- # 1. advance to the opening delimiter
41
- scanner.skip_until(OPEN)
42
-
43
- # 2. extract the key to be transformed
44
- key = scanner.scan(KEY)
34
+ embed_scanner = Fmt::EmbedScanner.new(string)
35
+ embed_scanner.scan
45
36
 
46
- # 3. advance to the closing delimiter
47
- scanner.skip_until(CLOSE) if key
37
+ key_scanner = Fmt::KeyScanner.new(string)
38
+ key = key_scanner.scan
39
+ return nil unless key
48
40
 
49
- # 4. scan for the filters
50
- filter_string = scanner.scan(FILTERS) if key
51
-
52
- return nil if key.nil? || filter_string.nil?
53
-
54
- mapped_filters = filter_string.split(Fmt::Filters::DELIMITER).map do |name|
55
- filters.fetch name.to_sym, Fmt::Filter.new(name, name)
56
- end
41
+ filter_scanner = Fmt::FilterScanner.new(key_scanner.rest, registered_filters: filters)
42
+ filter_string = filter_scanner.scan
57
43
 
58
- Fmt::Transformer.new(key.to_sym, *mapped_filters, placeholder: "%{#{key}}#{filter_string}".strip)
44
+ Fmt::Transformer.new key.to_sym,
45
+ embeds: embed_scanner.embeds,
46
+ filters: filter_scanner.filters,
47
+ placeholder: "%{#{key}}#{filter_string}".strip
59
48
  end
60
49
  end
61
50
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+ require "strscan"
5
+
6
+ module Fmt
7
+ class BaseScanner
8
+ extend Forwardable
9
+
10
+ def initialize(string)
11
+ @string_scanner = StringScanner.new(string)
12
+ end
13
+
14
+ def_delegators :string_scanner, :string, :rest
15
+ attr_reader :value
16
+
17
+ def performed?
18
+ !!@performed
19
+ end
20
+
21
+ def reset
22
+ @performed = false
23
+ string_scanner.reset
24
+ end
25
+
26
+ def scan
27
+ return if performed?
28
+ @performed = true
29
+ perform
30
+ value
31
+ end
32
+
33
+ protected
34
+
35
+ attr_reader :string_scanner
36
+
37
+ def perform
38
+ raise NotImplementedError
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_scanner"
4
+ require_relative "../embed"
5
+
6
+ module Fmt
7
+ class EmbedScanner < BaseScanner
8
+ def initialize(string, root: nil)
9
+ super(string)
10
+ @root ||= root || self
11
+ @embeds = []
12
+ end
13
+
14
+ def embeds
15
+ root? ? @embeds : root.embeds
16
+ end
17
+
18
+ attr_reader :root
19
+
20
+ def root?
21
+ root == self
22
+ end
23
+
24
+ protected
25
+
26
+ def includes_embed?(string)
27
+ string.match?(/[{]{2}/)
28
+ end
29
+
30
+ def next_scanner(string)
31
+ Fmt::EmbedScanner.new string, root: root
32
+ end
33
+
34
+ def scan_embeds(string)
35
+ return unless includes_embed?(string)
36
+
37
+ string = string[string.index(/[{]{2}/) + 2..]
38
+ scanner = next_scanner(string)
39
+ while (embed = scanner.scan)
40
+ embeds << Fmt::Embed.new(embed)
41
+ scanner = next_scanner(scanner.rest)
42
+ end
43
+ end
44
+
45
+ def perform?
46
+ !string.start_with?("}")
47
+ end
48
+
49
+ def perform
50
+ return unless perform?
51
+ scan_embeds string # <------------------------------------ extract embeds
52
+ string_scanner.scan_until(/[{]{2}/) # <------------------- advance to start
53
+ @value = string_scanner.scan_until(/[^}]*(?=[}]{2})/) # <- extract value
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_scanner"
4
+
5
+ module Fmt
6
+ class FilterScanner < BaseScanner
7
+ DELIMITER = "|"
8
+
9
+ def initialize(string, registered_filters:)
10
+ @registered_filters = registered_filters
11
+ @filters = []
12
+ super(string)
13
+ end
14
+
15
+ alias_method :filter_string, :value
16
+ attr_reader :filters
17
+
18
+ protected
19
+
20
+ attr_reader :registered_filters
21
+
22
+ def perform
23
+ @value = string_scanner.scan(/[^\s%]+/) # <- extract value
24
+ return unless string_scanner.matched?
25
+
26
+ @filters = value.split(DELIMITER)&.map do |name|
27
+ registered_filters.fetch name.to_sym, Fmt::Filter.new(name, name)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_scanner"
4
+
5
+ module Fmt
6
+ class KeyScanner < BaseScanner
7
+ protected
8
+
9
+ def perform
10
+ string_scanner.skip_until(/%[{]/) # <------------------------------ advance to start
11
+ @value = string_scanner.scan(/\w+/) if string_scanner.matched? # <- extract value
12
+ string_scanner.scan(/[}]/) if string_scanner.matched? # <---------- advance to end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ Dir[File.join(__dir__, "scanners", "*.rb")].each { |file| require file }
@@ -2,37 +2,62 @@
2
2
 
3
3
  module Fmt
4
4
  class Transformer
5
- def initialize(key, *filters, placeholder:)
5
+ def initialize(key, embeds:, filters:, placeholder:)
6
6
  @key = key
7
+ @embeds = embeds
7
8
  @filters = filters
8
9
  @placeholder = placeholder
9
10
  end
10
11
 
11
- attr_reader :key, :filters, :placeholder, :proc_filters, :string_filters
12
+ attr_reader :key, :embeds, :filters, :placeholder, :proc_filters, :string_filters
12
13
 
13
14
  def transform(string, **locals)
14
- return string if filters.none?
15
+ string = transform_embeds(string, **locals)
16
+
17
+ raise Fmt::Error, "Missing key! :#{key} <string=#{string.inspect} locals=#{locals.inspect}>" unless locals.key?(key)
15
18
 
16
- raise Fmt::Error, "Missing key :#{key} in #{locals.inspect}" unless locals.key?(key)
17
19
  replacement = locals[key]
18
20
 
19
21
  filters.each do |filter|
20
22
  if filter.string?
21
23
  begin
22
- replacement = format("%#{filter.value}", replacement)
24
+ replacement = sprintf("%#{filter.value}", replacement)
23
25
  rescue => error
24
- raise Fmt::Error, "Invalid filter! #{filter.inspect} Check the spelling and verify that it's registered `Fmt.add_filter(:#{filter.name}, &block)`; #{error.message}"
26
+ message = <<~MSG
27
+ Invalid filter!
28
+ #{filter.inspect}
29
+ Verify it's either a valid native filter or is registered with Fmt.
30
+ Example: Fmt.add_filter(:#{filter.name}, &block)
31
+ #{error.message}
32
+ MSG
33
+ raise Fmt::Error, message
25
34
  end
26
35
  elsif filter.proc?
27
36
  begin
28
37
  replacement = filter.value.call(replacement)
29
38
  rescue => error
30
- raise Fmt::Error, "Error in filter! #{filter.inspect} #{error.message}"
39
+ message = <<~MSG
40
+ Error in filter!
41
+ #{filter.inspect}
42
+ #{error.message}
43
+ MSG
44
+ raise Fmt::Error, message
31
45
  end
32
46
  end
33
47
  end
34
48
 
35
- string.sub placeholder, replacement
49
+ result = string.sub(placeholder, replacement)
50
+ defined?(Rainbow) ? Rainbow(result) : result
51
+ end
52
+
53
+ private
54
+
55
+ def transform_embeds(string, **locals)
56
+ while embeds.any?
57
+ embed = embeds.shift
58
+ string = string.sub(embed.placeholder, embed.format(**locals))
59
+ end
60
+ string
36
61
  end
37
62
  end
38
63
  end
data/lib/fmt/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fmt
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.2"
5
5
  end
data/lib/fmt.rb CHANGED
@@ -11,9 +11,15 @@ module Fmt
11
11
  Formatter.instance
12
12
  end
13
13
 
14
+ def filters
15
+ formatter.filters
16
+ end
17
+
14
18
  def add_filter(...)
15
19
  formatter.filters.add(...)
16
20
  end
21
+
22
+ alias_method :add, :add_filter
17
23
  end
18
24
  end
19
25
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fmt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nate Hopkins (hopsoft)
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-07-26 00:00:00.000000000 Z
11
+ date: 2024-07-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: amazing_print
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: tocer
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: yard
127
141
  requirement: !ruby/object:Gem::Requirement
@@ -145,9 +159,15 @@ extra_rdoc_files: []
145
159
  files:
146
160
  - README.md
147
161
  - lib/fmt.rb
162
+ - lib/fmt/embed.rb
148
163
  - lib/fmt/filter.rb
149
164
  - lib/fmt/filters.rb
150
165
  - lib/fmt/formatter.rb
166
+ - lib/fmt/scanners.rb
167
+ - lib/fmt/scanners/base_scanner.rb
168
+ - lib/fmt/scanners/embed_scanner.rb
169
+ - lib/fmt/scanners/filter_scanner.rb
170
+ - lib/fmt/scanners/key_scanner.rb
151
171
  - lib/fmt/transformer.rb
152
172
  - lib/fmt/version.rb
153
173
  homepage: https://github.com/hopsoft/fmt
@@ -171,7 +191,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
171
191
  - !ruby/object:Gem::Version
172
192
  version: '0'
173
193
  requirements: []
174
- rubygems_version: 3.5.5
194
+ rubygems_version: 3.5.11
175
195
  signing_key:
176
196
  specification_version: 4
177
197
  summary: A simple template engine based on native Ruby String formatting mechanics