fmt 0.1.1 → 0.1.3

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: 55fc16022e0fcb4b8815d6e7242106018e5786a9d610f38f8040cbb31cadb1a0
4
- data.tar.gz: 185f056e8450fb1facfe15c7c637e9b49d2a19a2270689b8e12953c664393e10
3
+ metadata.gz: 7aded878eeff57ab27f37b6e76bc34281d94000c95c5d0ae77dde2bee5ab5989
4
+ data.tar.gz: 0d6390579afbfa629f0e53a127c25b60c04548b831e8ec13b9ebd8ed0880a238
5
5
  SHA512:
6
- metadata.gz: 707ed8686b167ecba644d7a2358cf179670656cd9a7eb77f6d9408b7b8d5f07a9c204ca94fcd928e8630ca5633ce716c0fb600c4bca8d8e6992a1f682fcd76dc
7
- data.tar.gz: 4b350d2ce4dd9ceeca3b7640d392b806c9698607bae33db99f18a39248898dbeef10100040f2d78dfc66f95d78839a982cf9a1cb3391129c70347f68f043bf4d
6
+ metadata.gz: 1235f7508b608760ce2f984cdcb321c01ac2a1cd3d7d5b9d1b586b45614e3b79c38bf900264b569fa469c17e34d2b6e1999984715a9b51c8caa792ee7d080aa9
7
+ data.tar.gz: '012095a10cbeac34f3457bf18ebe9135513a774984139cd1c7c300cd00d774590eff1826f7af137adbb32acd77f2eb8bffa1d52b48c579422fe43a2e7c3769cf'
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-347-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,7 +38,7 @@ I'm currenly using this to help build beautiful CLI applications with Ruby. Plus
9
38
  ## Setup
10
39
 
11
40
  ```
12
- bundle add rainbow # optional
41
+ bundle add rainbow # <- optional
13
42
  bundle add fmt
14
43
  ```
15
44
 
@@ -35,7 +64,7 @@ Also, you can use Rainbow filters like `bold`, `cyan`, `underline`, et al. if yo
35
64
 
36
65
  **You can even [register your own filters](#filters).**
37
66
 
38
- ### Rendering
67
+ ### Formatting
39
68
 
40
69
  Basic example:
41
70
 
@@ -43,8 +72,11 @@ Basic example:
43
72
  require "rainbow"
44
73
  require "fmt"
45
74
 
75
+ Fmt.add_rainbow_filters
76
+
46
77
  template = "Hello %{name}cyan|bold"
47
- result = Fmt(template, name: "World")
78
+ Fmt template, name: "World"
79
+
48
80
  #=> "Hello \e[36m\e[1mWorld\e[0m"
49
81
  ```
50
82
 
@@ -56,8 +88,11 @@ Mix and match native formatting with Rainbow formatting:
56
88
  require "rainbow"
57
89
  require "fmt"
58
90
 
91
+ Fmt.add_rainbow_filters
92
+
59
93
  template = "Date: %{date}.10s|magenta"
60
- result = Fmt(template, date: Time.now)
94
+ Fmt template, date: Time.now
95
+
61
96
  #=> "Date: \e[35m2024-07-26\e[0m"
62
97
  ```
63
98
 
@@ -69,6 +104,8 @@ Multiline example:
69
104
  require "rainbow"
70
105
  require "fmt"
71
106
 
107
+ Fmt.add_rainbow_filters
108
+
72
109
  template = <<~T
73
110
  Date: %{date}.10s|underline
74
111
 
@@ -77,7 +114,8 @@ template = <<~T
77
114
  %{message}strip|green
78
115
  T
79
116
 
80
- result = Fmt(template, date: Time.now, name: "Hopsoft", message: "This is neat!")
117
+ Fmt template, date: Time.now, name: "Hopsoft", message: "This is neat!"
118
+
81
119
  #=> "Date: \e[4m2024-07-26\e[0m\n\nGreetings, \e[1mHOPSOFT\e[0m\n\n\e[32mThis is neat!\e[0m\n"
82
120
  ```
83
121
 
@@ -92,16 +130,63 @@ The block accepts a string and should return a replacement string.
92
130
  require "rainbow"
93
131
  require "fmt"
94
132
 
95
- Fmt.add_filter(:repeat20) { |str| str * 20 }
133
+ Fmt.add_rainbow_filters
134
+ Fmt.add_filter(:ljust) { |val| "".ljust 14, val.to_s }
96
135
 
97
136
  template = <<~T
98
- %{head}repeat20|faint
137
+ %{head}ljust|faint
99
138
  %{message}bold
100
- %{tail}repeat20|faint
139
+ %{tail}ljust|faint
101
140
  T
102
141
 
103
- result = Fmt(template, head: "#", message: "Give it a try!", tail: "#")
104
- #=> "\e[2m####################\e[0m\n\e[1mGive it a try!\e[0m\n\e[2m####################\e[0m\n"
142
+ Fmt template, head: "#", message: "Give it a try!", tail: "#"
143
+
144
+ #=> "\e[2m##############\e[0m\n\e[1mGive it a try!\e[0m\n\e[2m##############\e[0m\n"
105
145
  ```
106
146
 
107
147
  ![CleanShot 2024-07-26 at 01 46 26@2x](https://github.com/user-attachments/assets/bd1d67c6-1182-428b-be05-756f3d330f67)
148
+
149
+ ### Embeds
150
+
151
+ Templates can be embedded or nested within other templates... as deep as needed!
152
+ Just wrap the embedded template in double curly braces: `{{EMBEDDED TEMPLATE HERE}}`
153
+
154
+ ```ruby
155
+ require "rainbow"
156
+ require "fmt"
157
+
158
+ Fmt.add_rainbow_filters
159
+
160
+ template = "%{value}lime {{%{embed_value}red|bold|underline}}"
161
+ Fmt template, value: "Outer", embed_value: "Inner"
162
+
163
+ #=> "\e[38;5;46mOuter\e[0m \e[31m\e[1m\e[4mInner\e[0m"
164
+ ```
165
+
166
+ ![CleanShot 2024-07-29 at 02 42 19@2x](https://github.com/user-attachments/assets/f67dd215-b848-4a23-bd73-72822cb7d970)
167
+
168
+ ```ruby
169
+ template = <<~T
170
+ |--%{value}yellow|bold|underline
171
+ | |--{{%{inner_value}green|bold|underline
172
+ | | |--{{%{deep_value}blue|bold|underline
173
+ | | | |-- We're in deep!}}}}
174
+ T
175
+
176
+ Fmt template, value: "Outer", inner_value: "Inner", deep_value: "Deep"
177
+
178
+ #=> "|--\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"
179
+ ```
180
+
181
+ ![CleanShot 2024-07-29 at 02 45 27@2x](https://github.com/user-attachments/assets/1b933bf4-a62d-4913-b817-d6c69b0e7028)
182
+
183
+ ## Sponsors
184
+
185
+ <p align="center">
186
+ <em>Proudly sponsored by</em>
187
+ </p>
188
+ <p align="center">
189
+ <a href="https://www.clickfunnels.com?utm_source=hopsoft&utm_medium=open-source&utm_campaign=fmt">
190
+ <img src="https://images.clickfunnel.com/uploads/digital_asset/file/176632/clickfunnels-dark-logo.svg" width="575" />
191
+ </a>
192
+ </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
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "monitor"
4
+ require_relative "../filter"
5
+
6
+ module Fmt
7
+ class FilterGroup
8
+ include Enumerable
9
+ include MonitorMixin
10
+
11
+ def initialize
12
+ super
13
+ @data = {}
14
+ end
15
+
16
+ def [](name)
17
+ synchronize { data[name.to_sym] }
18
+ end
19
+
20
+ def add(name, filter_proc = nil, &block)
21
+ raise ArgumentError, "filter_proc and block are mutually exclusive" if filter_proc && block
22
+ raise ArgumentError, "filter_proc must be a Proc" unless block || filter_proc.is_a?(Proc)
23
+ synchronize do
24
+ data[name.to_sym] = Fmt::Filter.new(name.to_sym, filter_proc || block)
25
+ end
26
+ end
27
+
28
+ def each(&block)
29
+ synchronize { data.each(&block) }
30
+ end
31
+
32
+ def fetch(name, default = nil)
33
+ synchronize { data.fetch name.to_sym, default }
34
+ end
35
+
36
+ def key?(name)
37
+ synchronize { data.key? name.to_sym }
38
+ end
39
+
40
+ alias_method :added?, :key?
41
+ alias_method :include?, :key?
42
+
43
+ def merge!(other)
44
+ synchronize { data.merge! other.to_h }
45
+ self
46
+ end
47
+
48
+ def to_h
49
+ data.dup
50
+ end
51
+
52
+ protected
53
+
54
+ attr_reader :data
55
+ end
56
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "filter_group"
4
+
5
+ module Fmt
6
+ class RainbowFilterGroup < FilterGroup
7
+ def initialize
8
+ super
9
+
10
+ if defined? Rainbow
11
+ methods = Rainbow::Presenter.public_instance_methods(false).select do |method|
12
+ Rainbow::Presenter.public_instance_method(method).arity == 0
13
+ end
14
+
15
+ method_names = methods
16
+ .map { |m| m.name.to_sym }
17
+ .concat(Rainbow::X11ColorNames::NAMES.keys)
18
+
19
+ method_names.each do |name|
20
+ add(name) { |string| Rainbow(string).public_send name }
21
+ end
22
+ end
23
+ rescue => error
24
+ puts "Error adding Rainbow filters! #{error.inspect}"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "filter_group"
4
+
5
+ module Fmt
6
+ class StringFilterGroup < FilterGroup
7
+ def initialize
8
+ super
9
+
10
+ # rubocop:disable Layout/ExtraSpacing AllowForAlignment
11
+ add :capitalize, ->(s) { s.capitalize }
12
+ add :chomp, ->(s) { s.chomp }
13
+ add :chop, ->(s) { s.chop }
14
+ add :downcase, ->(s) { s.downcase }
15
+ add :lstrip, ->(s) { s.lstrip }
16
+ add :reverse, ->(s) { s.reverse }
17
+ add :rstrip, ->(s) { s.rstrip }
18
+ add :shellescape, ->(s) { s.shellescape }
19
+ add :strip, ->(s) { s.strip }
20
+ add :succ, ->(s) { s.succ }
21
+ add :swapcase, ->(s) { s.swapcase }
22
+ add :undump, ->(s) { s.undump }
23
+ add :unicode_normalize, ->(s) { s.unicode_normalize }
24
+ add :upcase, ->(s) { s.upcase }
25
+ # rubocop:enable Layout/ExtraSpacing AllowForAlignment
26
+ end
27
+ end
28
+ end
data/lib/fmt/formatter.rb CHANGED
@@ -1,20 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "singleton"
4
- require_relative "filters"
4
+ require_relative "filter_groups/filter_group"
5
+ require_relative "filter_groups/rainbow_filter_group"
6
+ require_relative "filter_groups/string_filter_group"
7
+ require_relative "scanners"
5
8
  require_relative "transformer"
6
9
 
7
10
  module Fmt
8
11
  class Formatter
9
12
  include Singleton
10
13
 
11
- OPEN = /%\{/
12
- CLOSE = /\}/
13
- KEY = /\w+(?=\})/
14
- FILTERS = /[^\s]+(?=\s|$)/
15
-
16
14
  attr_reader :filters
17
15
 
16
+ def add_rainbow_filters
17
+ filters.merge! Fmt::RainbowFilterGroup.new.to_h
18
+ end
19
+
20
+ def add_filter(...)
21
+ filters.add(...)
22
+ end
23
+
18
24
  def format(string, **locals)
19
25
  result = string.to_s
20
26
  transformer = next_transformer(result)
@@ -31,31 +37,24 @@ module Fmt
31
37
 
32
38
  def initialize
33
39
  super
34
- @filters = Fmt::Filters.new
40
+ @filters = Fmt::FilterGroup.new.merge!(Fmt::StringFilterGroup.new)
35
41
  end
36
42
 
37
43
  def next_transformer(string)
38
- scanner = StringScanner.new(string)
44
+ embed_scanner = Fmt::EmbedScanner.new(string)
45
+ embed_scanner.scan
39
46
 
40
- # 1. advance to the opening delimiter
41
- scanner.skip_until(OPEN)
47
+ key_scanner = Fmt::KeyScanner.new(string)
48
+ key = key_scanner.scan
49
+ return nil unless key
42
50
 
43
- # 2. extract the key to be transformed
44
- key = scanner.scan(KEY)
45
-
46
- # 3. advance to the closing delimiter
47
- scanner.skip_until(CLOSE) if key
48
-
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
51
+ filter_scanner = Fmt::FilterScanner.new(key_scanner.rest, registered_filters: filters)
52
+ filter_string = filter_scanner.scan
57
53
 
58
- Fmt::Transformer.new(key.to_sym, *mapped_filters, placeholder: "%{#{key}}#{filter_string}".strip)
54
+ Fmt::Transformer.new key.to_sym,
55
+ embeds: embed_scanner.embeds,
56
+ filters: filter_scanner.filters,
57
+ placeholder: "%{#{key}}#{filter_string}".strip
59
58
  end
60
59
  end
61
60
  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,56 @@
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)
23
- 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}"
24
+ replacement = sprintf("%#{filter.value}", replacement)
25
+ rescue
26
+ raise Fmt::Error, <<~MSG
27
+ Invalid filter! #{filter.inspect}
28
+ Verify it has been properly registered. SEE: Fmt.add_filter(:#{filter.name}, &block)
29
+ MSG
25
30
  end
26
31
  elsif filter.proc?
27
32
  begin
28
33
  replacement = filter.value.call(replacement)
29
34
  rescue => error
30
- raise Fmt::Error, "Error in filter! #{filter.inspect} #{error.message}"
35
+ raise Fmt::Error, <<~MSG
36
+ Error in filter! #{filter.inspect}
37
+ #{error.message}
38
+ MSG
31
39
  end
32
40
  end
33
41
  end
34
42
 
35
- string.sub placeholder, replacement
43
+ result = string.sub(placeholder, replacement)
44
+ defined?(Rainbow) ? Rainbow(result) : result
45
+ end
46
+
47
+ private
48
+
49
+ def transform_embeds(string, **locals)
50
+ while embeds.any?
51
+ embed = embeds.shift
52
+ string = string.sub(embed.placeholder, embed.format(**locals))
53
+ end
54
+ string
36
55
  end
37
56
  end
38
57
  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.1"
4
+ VERSION = "0.1.3"
5
5
  end
data/lib/fmt.rb CHANGED
@@ -11,8 +11,16 @@ module Fmt
11
11
  Formatter.instance
12
12
  end
13
13
 
14
+ def filters
15
+ formatter.filters
16
+ end
17
+
18
+ def add_rainbow_filters
19
+ formatter.add_rainbow_filters
20
+ end
21
+
14
22
  def add_filter(...)
15
- formatter.filters.add(...)
23
+ formatter.add_filter(...)
16
24
  end
17
25
  end
18
26
  end
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.1
4
+ version: 0.1.3
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,17 @@ 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
- - lib/fmt/filters.rb
164
+ - lib/fmt/filter_groups/filter_group.rb
165
+ - lib/fmt/filter_groups/rainbow_filter_group.rb
166
+ - lib/fmt/filter_groups/string_filter_group.rb
150
167
  - lib/fmt/formatter.rb
168
+ - lib/fmt/scanners.rb
169
+ - lib/fmt/scanners/base_scanner.rb
170
+ - lib/fmt/scanners/embed_scanner.rb
171
+ - lib/fmt/scanners/filter_scanner.rb
172
+ - lib/fmt/scanners/key_scanner.rb
151
173
  - lib/fmt/transformer.rb
152
174
  - lib/fmt/version.rb
153
175
  homepage: https://github.com/hopsoft/fmt
@@ -171,7 +193,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
171
193
  - !ruby/object:Gem::Version
172
194
  version: '0'
173
195
  requirements: []
174
- rubygems_version: 3.5.5
196
+ rubygems_version: 3.5.11
175
197
  signing_key:
176
198
  specification_version: 4
177
199
  summary: A simple template engine based on native Ruby String formatting mechanics
data/lib/fmt/filters.rb DELETED
@@ -1,76 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "monitor"
4
- require_relative "filter"
5
-
6
- module Fmt
7
- class Filters
8
- include Enumerable
9
- include MonitorMixin
10
-
11
- DELIMITER = "|"
12
-
13
- NATIVE_FILTERS = %i[
14
- capitalize
15
- chomp
16
- chop
17
- downcase
18
- lstrip
19
- reverse
20
- rstrip
21
- shellescape
22
- strip
23
- succ
24
- swapcase
25
- undump
26
- unicode_normalize
27
- upcase
28
- ]
29
-
30
- def initialize
31
- super
32
- @entries = {}
33
-
34
- NATIVE_FILTERS.each do |name|
35
- add(name) { |str| str.then(&:"#{name}") }
36
- end
37
-
38
- if defined? Rainbow
39
- Rainbow::Presenter.public_instance_methods(false).each do |name|
40
- next unless Rainbow::Presenter.public_instance_method(name).arity == 0
41
- add(name) { |str| Rainbow(str).public_send(name) }
42
- end
43
-
44
- Rainbow::X11ColorNames::NAMES.keys.each do |name|
45
- add(name) { |str| Rainbow(str).public_send(name) }
46
- end
47
- end
48
- rescue
49
- # noop
50
- end
51
-
52
- def each(&block)
53
- entries.each(&block)
54
- end
55
-
56
- def add(name, filter = nil, &block)
57
- raise ArgumentError, "filter and block are mutually exclusive" if filter && block
58
- raise ArgumentError, "filter must be a Proc" unless block || filter.is_a?(Proc)
59
- entries[name.to_sym] = Filter.new(name, filter || block)
60
- end
61
-
62
- alias_method :<<, :add
63
-
64
- def [](name)
65
- synchronize { entries[name.to_sym] }
66
- end
67
-
68
- def fetch(name, default = nil)
69
- synchronize { entries.fetch name.to_sym, default }
70
- end
71
-
72
- private
73
-
74
- attr_reader :entries
75
- end
76
- end